参考:https://developer.aliyun.com/article/882393
1、java.util.TimerTask:
所有的TimerTask是在同一个线程中串行执行,相互影响。也就是说,对于同一个Timer里的多个TimerTask任务,如果一个TimerTask任务在执行中,其它TimerTask即使到达执行的时间,也只能排队等待。如果有异常产生,线程将退出,整个定时任务就失败
2、ScheduledExecutorService
基于线程池设计的定时任务解决方案,每个调度任务都会分配到线程池中的一个线程去执行,解决Timer定时器无法并发执行的问题
3、Springboot中@EnableScheduling + @Scheduled
并行任务业务幂等解决方案:
1、分布式锁:
1)基于DB、zookeeper: 有可能导致任务重复执行的。比如任务执行的非常快,A这台机器抢到锁,执行完任务后很快就释放锁了。B这台机器后抢锁,还是会抢到锁,再执行一遍任务。
2)redis:抢锁支持过期时间,不用主动去释放锁,并且可以充分利用这个过期时间,解决任务执行过快释放锁导致任务重复执行的问题
2、Quartz:只需要定义了 Job(任务),Trigger(触发器)和Scheduler(调度器),即可实现一个定时调度能力。支持基于数据库的集群模式,可以做到任务幂等执行
上面方案,在架构上问题是每次调度都需要抢锁,特别是使用DB和Zookeeper抢锁,性能会比较差,一旦任务量增加到一定的量,就会有比较明显的调度延时。
还有一个痛点,就是业务想要修改调度配置,或者增加一个任务,得修改代码重新发布应用。
开源任务调度中间件
任务调度中间件,通过任务调度系统进行定,任务的创建、修改和调度,这其中国内最火的就是XXL-JOB和ElasticJob。
1、ElasticJob
一款基于Quartz开发,依赖Zookeeper作为注册中心、轻量级、无中心化的分布式任务调度框架,目前已经通过Apache开源。
关于分片,失效转移:https://shardingsphere.apache.org/elasticjob/current/cn/features/elastic/
ElasticJob相对于Quartz来说,从功能上最大的区别就是支持分片,可以将一个任务分片参数分发给不同的机器执行。架构上最大的区别就是使用Zookeeper作为注册中心,不同的任务分配给不同的节点调度,不需要抢锁触发,性能上比Quartz上强大很多,架构如下:
springboot配置文件
elasticjob: regCenter: serverLists: localhost:2181 namespace: elasticjob-lite-springboot jobs: simpleJob: elasticJobClass: org.apache.shardingsphere.elasticjob.lite.example.job.SpringBootSimpleJob cron: 0/5 * * * * ? timeZone: GMT+08:00 shardingTotalCount: 3 shardingItemParameters: 0=Beijing,1=Shanghai,2=Guangzhou scriptJob: elasticJobType: SCRIPT cron: 0/10 * * * * ? shardingTotalCount: 3 props: script.command.line: "echo SCRIPT Job: " manualScriptJob: elasticJobType: SCRIPT jobBootstrapBeanName: manualScriptJobBean shardingTotalCount: 9 props: script.command.line: "echo Manual SCRIPT Job: "
job实现:
@Component public class SpringBootShardingJob implements SimpleJob { @Override public void execute(ShardingContext context) { System.out.println("分片总数="+context.getShardingTotalCount() + ", 分片号="+context.getShardingItem() + ", 分片参数="+context.getShardingParameter()); } }
日志打印:
分片总数=3, 分片号=0, 分片参数=Beijing 分片总数=3, 分片号=1, 分片参数=Shanghai 分片总数=3, 分片号=2, 分片参数=Guangzhou
ElasticJob还提供了一个简单的UI,可以查看任务的列表,同时支持修改、触发、停止、生效、失效操作
2、XXL-JOB
是一个开箱即用的轻量级分布式任务调度系统,其核心设计目标是开发迅速、学习简单、轻量级、易扩展,在开源社区广泛流行。
XXL-JOB是Master-Slave架构,Master负责任务的调度,Slave负责任务的执行,架构图如下
XXL-JOB接入也很方便,不同于ElasticJob定义任务实现类,是通过@XxlJob 注解定义JobHandler
@Component public class SampleXxlJob { private static Logger logger = LoggerFactory.getLogger(SampleXxlJob.class); /** * 1、简单任务示例(Bean模式) */ @XxlJob("demoJobHandler") public ReturnT<String> demoJobHandler(String param) throws Exception { XxlJobLogger.log("XXL-JOB, Hello World."); for (int i = 0; i < 5; i++) { XxlJobLogger.log("beat at:" + i); TimeUnit.SECONDS.sleep(2); } return ReturnT.SUCCESS; } /** * 2、分片广播任务 */ @XxlJob("shardingJobHandler") public ReturnT<String> shardingJobHandler(String param) throws Exception { // 分片参数 ShardingUtil.ShardingVO shardingVO = ShardingUtil.getShardingVo(); XxlJobLogger.log("分片参数:当前分片序号 = {}, 总分片数 = {}", shardingVO.getIndex(), shardingVO.getTotal()); // 业务逻辑 for (int i = 0; i < shardingVO.getTotal(); i++) { if (i == shardingVO.getIndex()) { XxlJobLogger.log("第 {} 片, 命中分片开始处理", i); } else { XxlJobLogger.log("第 {} 片, 忽略", i); } } return ReturnT.SUCCESS; } }
XXL-JOB相较于ElasticJob,最大的特点就是功能比较丰富,可运维能力比较强,不但支持控制台动态创建任务,还有调度日志、运行报表等功能。
XXL-JOB所有功能都依赖数据库,且调度中心不支持分布式架构,在任务量和调度量比较大的情况下,会有性能瓶颈。不过如果对任务量级、高可用、监控报警、可视化等没有过高要求的话,XXL-JOB基本可以满足定时任务的需求。