This website requires JavaScript.

分布式定时任务的处理

1 2020-08-04 01:00:27 33

定时任务

在开发中相信大家或多或少都会用过 @Scheduled处理定时任务,在以前都是单体服务,没有多实例的概念,但是现在随着发展越来越多的项目应用上了微服务分布式系统,分布式引发的一系列问题也随之而来,像前面说的CAP理论、分布式事务,以及这里说的分布式定时任务等等问题。

定时任务如果不处理的话,N个实例会导致定时任务N次触发,后果不亚于银行转账不用强一致性导致的灾难。

解决方案

只要涉及到分布式,基本上就是使用分布式锁解决,定时任务也不列外,一般有以下几种:

一:幂等

定时任务相对简单,比如定时清理不活跃的用户,只要保证此任务是幂等,那么执行多少次都没关系。缺点是多实例都会执行,浪费资源,虽然简洁但不推荐。

二:开源分布式定时任务

ShedLock

ShedLock只做一件事。它确保您的计划任务最多同时执行一次。如果正在一个节点上执行任务,它将获取一个锁,以防止从另一个节点(或线程)执行相同的任务。请注意,如果一个任务已在一个节点上执行,则其他节点上的执行不会等待,只会跳过它。

Use: 添加依赖

        
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
<!-- 确保spring-boot版本大于2.1.0 --> <parent> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-parent</artifactId> <version>2.2.6.RELEASE</version> <relativePath/> <!-- lookup parent from repository --> </parent> <dependencies> <dependency> <groupId>redis.clients</groupId> <artifactId>jedis</artifactId> <version>3.2.0</version> <scope>compile</scope> </dependency> <dependency> <groupId>net.javacrumbs.shedlock</groupId> <artifactId>shedlock-provider-redis-jedis</artifactId> <version>4.13.0</version> </dependency> <dependency> <groupId>net.javacrumbs.shedlock</groupId> <artifactId>shedlock-spring</artifactId> <version>4.13.0</version> </dependency> </dependencies>
  1. 添加配置
        
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
@EnableSchedulerLock(defaultLockAtMostFor = "10m") @Configuration @EnableScheduling public class demoConfig { @Primary @Bean public JedisPool jedisPool() { JedisPoolConfig config = new JedisPoolConfig(); return new JedisPool(config, "IP", 6379,10000, "密码"); } @Bean public LockProvider lockProvider(JedisPool jedisPool) { return new JedisLockProvider(jedisPool); } }
  1. 测试多实例相同任务是否加锁
        
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
@Component public class ScheduledTest { @Scheduled(cron = "0/5 * * * * ?") @SchedulerLock(name = "scheduledTaskName", lockAtMostFor = "5s",lockAtLeastFor = "1s") public void getInfoa() { System.out.println(System.currentTimeMillis()); System.out.println(new Date()); System.out.println("Lock one"); } @Scheduled(cron = "0/5 * * * * ?") @SchedulerLock(name = "scheduledTaskName", lockAtMostFor = "5s",lockAtLeastFor = "1s") public void getInfoaa() { System.out.println(System.currentTimeMillis()); System.out.println(new Date()); System.out.println("Lock two"); } }

三:在代码中使用redis分布式锁

        
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
@Scheduled(cron = "0 0 8 * * ? ") public void sendMessage() { RLock rLock = null; try { rLock = this.redisson.getLock("gene_sms_state"); if (!rLock.tryLock(0L, 300, TimeUnit.SECONDS)) { return; } // do something } catch (Exception e) { this.log.warn("", e.getMessage()); } finally { if (rLock != null) { rLock.forceUnlock(); } } }

但是每个定时任务都需要写这一套,非常繁琐。或者即使想使用上面提到的ShedLock但是引入新依赖需要考虑团队因素,最低版本限制等等, 那么这里就可以使用切面注解进行优化,注解相对比较简单,这里就不阐述了。可以参考

优化

        
  • 1
@Scheduled(cron = "*/1 0 0 * * ?")

像这种每秒都执行的或者几分钟执行不建议使用以上的分布式定时任务,这种情况应该根据业务场景去写脚本走批量或者单独使用任务调度服务去解决。

免责声明

本站提供Hack区的一切软件、教程和仅限用于学习和研究目的;不得将其用于商业或者非法用途,否则,一切后果由用户自己承担 您必须在下载后的24个小时之内, 从您的电脑中彻底删除。如果条件支持,请支持正版,得到更好的服务。 另如有侵权请邮件与我 联系处理。敬请谅解!

本文于   2020/8/4 上午  发布 

永久地址: https://madaoo.com/article/1290331708947828736

版权声明: 自由转载-署名-非商业性使用   |   Creative Commons BY-NC 3.0 CN