在清理Github的时候,发现之前写的一个简单的分布式任务分发系统ClawHub/task-distribution,使用了zk的选主与队列,调度器使用spring的ThreadPoolTaskScheduler,任务支持cron表达式。html
这让我想起在这以前还封装过Quartz,那时候作的是单机版超大型应用,内部须要作调度系统。java
目前在公司使用过Elastic-Job,也使用过Spring自带调度与ZK的结合,还有公司Boss系统提供的配置版本任务调度。git
写这篇文章主要是简单的回忆一下Quartz与Spring的ThreadPoolTaskScheduler,还有Elastic-job。再写一下本身对分布式Job的思考。github
官网地址:quartz-scheduler.orgspring
maven的pom文件:架构
<dependency> <groupId>org.quartz-scheduler</groupId> <artifactId>quartz</artifactId> <version>2.3.2</version> </dependency>
具体任务:dom
import org.quartz.Job; import org.quartz.JobExecutionContext; import java.time.LocalDateTime; import java.util.Random; public class HelloJob implements Job { @Override public void execute(JobExecutionContext jobExecutionContext) { System.out.println(jobExecutionContext.getJobDetail().getJobDataMap().get("jobDetailJobData1")); System.out.println(jobExecutionContext.getTrigger().getJobDataMap().get("triggerJobData1")); System.out.println("HelloJob start at:" + LocalDateTime.now() + ", prints: Hello Job-" + new Random().nextInt(100)); } }
主调度器:maven
import org.quartz.*; import org.quartz.impl.StdSchedulerFactory; import java.util.concurrent.TimeUnit; public class MainScheduler { public static void main(String[] args) throws SchedulerException, InterruptedException { // 一、建立调度器Scheduler SchedulerFactory schedulerFactory = new StdSchedulerFactory(); Scheduler scheduler = schedulerFactory.getScheduler(); // 二、建立JobDetail实例,并与PrintWordsJob类绑定(Job执行内容) JobDetail jobDetail = JobBuilder.newJob(HelloJob.class).usingJobData("jobDetailJobData1", "测试JobDetail上下文") .withIdentity("job1", "group1").build(); // 三、构建Trigger实例,每隔1s执行一次 Trigger trigger = TriggerBuilder.newTrigger().usingJobData("triggerJobData1", "测试Trigger上下文") .withIdentity("trigger1", "triggerGroup1") .startNow()//当即生效 .withSchedule(SimpleScheduleBuilder.simpleSchedule() .withIntervalInSeconds(1)//每隔1s执行一次 .repeatForever()).build();//一直执行 //四、执行 scheduler.scheduleJob(jobDetail, trigger); System.out.println("--------MainScheduler start ! ------------"); scheduler.start(); //睡眠 TimeUnit.MINUTES.sleep(1); scheduler.shutdown(); System.out.println("--------MainScheduler shutdown ! ------------"); } }
通过上面三步,执行main方法,就能够简单的实现调度任务了,下面分别介绍出现的角色。分布式
Job是Quartz中的任务模板,JobDetail是Job的描述,当Scheduler执行任务的时候,会根据JobDetail建立新的Job,用过以后就释放掉。ide
触发器,描述了任务是何时触发执行,经常使用有SimpleTrigger、CronTrigger,cron表达式很是强大,基本上都是基于CronTrigger来作任务调度。
JobExecutionContext任务执行的上下文,JobDataMap保存上下文传输的数据。
上面这幅图能够简单的描述Quartz核心对象之间的关系。
底层依赖于JUC的java.util.concurrent.ScheduledExecutorService。
import org.springframework.scheduling.concurrent.ThreadPoolTaskScheduler; import org.springframework.scheduling.support.CronTrigger; import java.time.LocalDateTime; import java.util.Random; public class Main { public static void main(String[] args) { ThreadPoolTaskScheduler scheduler = new ThreadPoolTaskScheduler(); scheduler.setPoolSize(10); scheduler.initialize(); scheduler.schedule(() -> { System.out.println("HelloJob start at:" + LocalDateTime.now() + ", prints: Hello Job-" + new Random().nextInt(100)); }, new CronTrigger("0/1 * * * * ?")); } }
Elastic-Job是一个分布式调度解决方案,由两个相互独立的子项目Elastic-Job-Lite和Elastic-Job-Cloud组成。
官网:elasticjob.io。
咱们使用的是轻量级的使用方案:elasticjob/elastic-job-lite
官方架构图:
做业执行流程图:
我也只有使用经验,并无深刻的了解其原理,具体请参考官方文档。
如今的Quartz也有集群版本,我尚未接触过,如今碰到的基本上都是基于Quartz的包装,好比Elastic-Job、xxl job、azkaban。
由于如今的项目基本都为分布式系统,因此对于调度系统而言,单机已经不怎么适用了。
而对于分布式调度系统,必然也有不少要求,下面讲一下个人理解:
这个式调度系统的基本要求。
对于一些大量数据的定时任务,若是只在一台机器上执行是很吃力的,因此应该有任务分片执行的功能,将大任务拆分到多个系统上执行。
在增长或者减小节点时,任务可以自动均衡。
任务的信息可动态配置。
任务动态地启动、暂停、终止、删除等。
任务的执行与否由各个节点本身控制,可是要保证任务执行不可重复。
节点发生故障时,任务自动转移。
任务的执行信息须要有记录。
能想到这只有这么多,感受Elastic-Job就很好用。