定时任务的框架有哪些
● Timer,JDK自带的,比较简单,使用的时候,定义一个TimerTask,实现run方法,然后定义一个Timer类,调用timer.schedule(timerTask,1000,3000);
○ 缺点:单线程、无异常处理,如果执行时间太长或者任务执行异常,会影响其他任务调度
● springboot 的 @Scheduled (Spring Task)
使用简单,在启动类上开启,EnableScheduling,然后在使用定时任务的方法上添加`@Scheduled(cron=“xxxxxxx”)
○ 优点:1.简单 2.比Timer准确一点 3.可以配置多线程
○ 缺点:1.无自带异常处理,2.设计上偏向单节点运行
● Quartz 框架,是一个完全由Java编写的开源作业调度框架,这是一个功能比较强大的的调度器,可以让你的程序在指定时间执行,也可以按照某一个频度执行,配置起来稍显复杂。
○ 优点:1.强大的调度功能 2.灵活的应用方式 3.支持持久化
○ 缺点:需要把任务信息持久化到业务数据库、和业务有耦合、并且表比较多
● xxl-job:的组成角色:整体分为两个部分:调度中心和执行器,调度中心:可以理解为服务端,需要单独部署,提供的UI界面来管理定时任务,负责发起调度,本身不承接业务逻辑。把调度和任务解耦,提高了系统可用性和稳定性,同时调度系统性能不再受限于任务模块。执行器:可以理解为客户端,需要跟我们实际开发的项目整合,负责接收调度请求并执行任务逻辑。任务模块专注于任务的执行等操作,开发和维护更加简单和高效;任务抽象成分散的JobHandler
,交由“执行器”统一管理,“执行器”负责接收调度请求并执行对应的JobHandler中业务逻辑。
下面着重介绍下Quartz以及xxl-job
Quartz
里面有几个核心的概念:Job(任务)表示我们定时要执行的业务逻辑,需要重写execute()方法,Trigger(触发器)表示什么时候执行,Scheduler(调度器)负责把Job和Trigger绑定在一起去执行,保证任务可以在正确的时间执行。
Job和Trigger关系:一个job可以有多个trigger;一个trigger只能有个一个job;
核心代码
private void openJob(Scheduler scheduler){try {//1.创建一个JobDetailJobDetail jobDetail = JobBuilder.newJob(MyJob.class).withIdentity("job1", "group1").build();//2.触发器表达式对象CronScheduleBuilder cronScheduleBuilder = CronScheduleBuilder.cronSchedule("0/4 * * * * ?");//CronScheduleBuilder cronScheduleBuilder = CronScheduleBuilder.cronSchedule("30 25 16 * * ?");//3.准备一个触发器对象CronTrigger cronTrigger = TriggerBuilder.newTrigger().withIdentity("trigger1", "triggergroup1").withSchedule(cronScheduleBuilder).build();//4.开始调度scheduler.scheduleJob(jobDetail,cronTrigger);} catch (SchedulerException e) {e.printStackTrace();} finally {}
}
通过代码可以看到几个重要的类:
● JobDetail: 真正的任务内容,任务本身是集成Job接口的,但是真正的任务是 JobBuilder通过反射的方式实例化的。
● Trigger: 触发器,定义任务应当开始的时间,主要分为两类 SimpleTrigger,CronTrigger,当前例子的就是简单触发器,CronTrigger主要用于 处理quartz表达式定义的任务,比如每个月20号,每个星期一之类的。
● Scheduler: 计划执行者,现在我们有了要做的内容(HelloJob),有了要做的时间 (下一分钟),接下来,就把这两个内容填充到计划任务Scheduler对象里面,到了时间它就可以自动运行了。
Quartz核心类与方法
暂停Job:------pauseJob(jobKey)
//通过JobName以及JobGroup获得JobKeyJobKey jobKey = JobKey.jobKey("123", JOB_GROUP_NAME);try {Scheduler scheduler = schedulerFactoryBean.getScheduler();scheduler .pauseJob(jobKey);} catch (SchedulerException e) {e.printStackTrace();}
恢复Job:-----resumeJob(jobKey)
//通过JobName以及JobGroup获得JobKey
JobKey jobKey = JobKey.jobKey("123", JOB_GROUP_NAME);
try {schedulerFactoryBean.getScheduler().resumeJob(jobKey);
} catch (SchedulerException e) {e.printStackTrace();
}
删除job------deleteJob(jobKey)/unscheduleJob(triggerkey)
方法一:
//通过JobName以及JobGroup获得JobKey
JobKey jobKey = JobKey.jobKey("123", JOB_GROUP_NAME);
try {schedulerFactoryBean.getScheduler().deleteJob(jobKey);
} catch (SchedulerException e) {e.printStackTrace();
}
方法二:
//通过triggerName获取TriggerKey
TriggerKey aaatrigger = TriggerKey.triggerKey("aaatrigger");
try {schedulerFactoryBean.getScheduler().unscheduleJob(aaatrigger);
} catch (SchedulerException e) {e.printStackTrace();
}
JobDetail
JobDetail为Job实例提供了许多设置属性,以及JobDetailMap成员变量属性,它用来存储特定Job实例的状态信息,调度器需要借助JobDetail对象来添加Job实例。
JobDetail属性
name:任务名称
group:任务所属组
jobClass:任务实现类
jobDataMap:传参的作用
JobExecutionContext
当Scheduler调用一个Job,就会将JobExecutionContext传递给Job的execute()方法;
Job能通过JobExecutionContext对象访问到Quartz运行时候的环境以及Job本身的明细数据。
JobDataMap
在进行任务调度时JobDataMap存储在JobExecutionContext中,非常方便获取
JobDataMap可以用来装载任务可序列化的数据对象,当job实例对象被执行时这些参数对象会传递给它
JobDataMap实现了JDK的Map接口,并且添加了一些非常方便的方法用来存取基本数据类型
说明:
1、实际上,Quartz在进行调度器初始化的时候,会加载quartz.properties文件进行一些属性的设置,比如Quartz后台线程池的属性(threadCount)、作业存储设置等。它会先从工程中找,如果找不到那么就是用quartz.jar中的默认的quartz.properties文件。
2、Quartz存在监听器的概念,比如任务执行前后、任务的添加等,可以方便实现任务的监控。
XXL-JOB
quartz现有定时任务的缺点:
● 1.各个模块都需要使用QuartzJobConfig配置,调度逻辑和执行任务耦合在一起
● 2.定时任务缓存会影响配置修改
● 3.开关不方便
● 4.没有管理页面
XXL-JOB的优点(特性)
使用简单,支持集群部署;提供运维界面维护成本小;自带错误预警;相对elastic-job来说不需要额外的组件(zookeeper);支持调度策略;支持分片;故障转移 ;更适合分布式。
● 简单:支持通过Web页面对任务进行CRUD操作,支持动态修改任务状态、启动/停止任务,操作简单
● 任务分布式执行,任务”执行器”支持集群部署,支持弹性扩容缩容;
● 丰富的路由策略:包括:第一个、最后一个、轮询、随机、一致性HASH、最不经常使用、最近最久未使用、故障转移、忙碌转移等
● 任务超时控制:支持自定义任务超时时间,任务运行超时将会主动中断任务
● 任务失败重试:支持自定义任务失败重试次数,当任务失败时将会按照预设的失败重试次数主动进行重试;其中分片任务支持分片粒度的失败重试
● 任务失败告警;默认提供邮件方式失败告警,同时预留扩展接口,可方便的扩展短信、钉钉等告警方式
● 分片广播任务:执行器集群部署时,任务路由策略选择"分片广播"情况下,一次任务调度将会广播触发集群中所有执行器执行一次任务,可根据分片参数开发分片任务
xxl-job的组成角色:
整体分为两个部分:调度中心 和 执行器
调度中心:可以理解为服务端,需要单独部署,提供的UI界面来管理定时任务,负责发起调度,本身不承接业务逻辑。把调度和任务解耦,提高了系统可用性和稳定性,同时调度系统性能不再受限于任务模块。
支持可视化、简单且动态的管理调度信息,包括任务新建,更新,删除,GLUE开发和任务报警等,所有上述操作都会实时生效,同时支持监控调度结果以及执行日志,支持执行器Failover。
执行器:可以理解为客户端,需要跟我们实际开发的项目整合,负责接收调度请求并执行任务逻辑。任务模块专注于任务的执行等操作,开发和维护更加简单和高效;接收“调度中心”的执行请求、终止请求和日志请求等。任务抽象成分散的JobHandler,交由“执行器”统一管理,“执行器”负责接收调度请求并执行对应的JobHandler中业务逻辑
总结:“调度”和“任务”两部分可以相互解耦,提高系统整体稳定性和扩展性。
三个文件
- xxl-job-admin 调度中心
- xxl-job-core 公共依赖
- xxl-job-executor-samples 实例–执行器
任务详解
基础配置:
- 执行器:任务的绑定的执行器,任务触发调度时将会自动发现注册成功的执行器, 实现任务自动发现功能; 另一方面也可以方便的进行任务分组。每个任务必须绑定一个执行器, 可在 “执行器管理” 进行设置;
- 任务描述:任务的描述信息,便于任务管理;
- 负责人:任务的负责人;
- 报警邮件:任务调度失败时邮件通知的邮箱地址,支持配置多邮箱地址,配置多个邮箱地址时用逗号分隔;
触发配置:
- 调度类型:
无:该类型不会主动触发调度;
CRON:该类型将会通过CRON,触发任务调度;
固定速度:该类型将会以固定速度,触发任务调度;按照固定的间隔时间,周期性触发;
固定延迟:该类型将会以固定延迟,触发任务调度;按照固定的延迟时间,从上次调度结束后开始计算延迟时间,到达延迟时间后触发下次调度;
- CRON:触发任务执行的Cron表达式;
- 固定速度:固件速度的时间间隔,单位为秒;
- 固定延迟:固件延迟的时间间隔,单位为秒;
任务配置:
- 运行模式:
BEAN模式:任务以JobHandler方式维护在执行器端;需要结合 “JobHandler” 属性匹配执行器中任务;
GLUE模式(Java):任务以源码方式维护在调度中心;该模式的任务实际上是一段继承自IJobHandler的Java类代码并 “groovy” 源码方式维护,它在执行器项目中运行,可使用@Resource/@Autowire注入执行器里中的其他服务;
GLUE模式(Shell):任务以源码方式维护在调度中心;该模式的任务实际上是一段 “shell” 脚本;
GLUE模式(Python):任务以源码方式维护在调度中心;该模式的任务实际上是一段 “python” 脚本;
GLUE模式(PHP):任务以源码方式维护在调度中心;该模式的任务实际上是一段 “php” 脚本;
GLUE模式(NodeJS):任务以源码方式维护在调度中心;该模式的任务实际上是一段 “nodejs” 脚本;
GLUE模式(PowerShell):任务以源码方式维护在调度中心;该模式的任务实际上是一段 “PowerShell” 脚本;
- JobHandler:运行模式为 “BEAN模式” 时生效,对应执行器中新开发的JobHandler类“@JobHandler”注解自定义的value值;
- 执行参数:任务执行所需的参数;
高级配置:
- 路由策略:当执行器集群部署时,提供丰富的路由策略,包括;
FIRST(第一个):固定选择第一个机器;
LAST(最后一个):固定选择最后一个机器;
ROUND(轮询):;
RANDOM(随机):随机选择在线的机器;
CONSISTENT_HASH(一致性HASH):每个任务按照Hash算法固定选择某一台机器,且所有任务均匀散列在不同机器上。
LEAST_FREQUENTLY_USED(最不经常使用):使用频率最低的机器优先被选举;
LEAST_RECENTLY_USED(最近最久未使用):最久未使用的机器优先被选举;
FAILOVER(故障转移):按照顺序依次进行心跳检测,第一个心跳检测成功的机器选定为目标执行器并发起调度;
BUSYOVER(忙碌转移):按照顺序依次进行空闲检测,第一个空闲检测成功的机器选定为目标执行器并发起调度;
SHARDING_BROADCAST(分片广播):广播触发对应集群中所有机器执行一次任务,同时系统自动传递分片参数;可根据分片参数开发分片任务;
- 子任务:每个任务都拥有一个唯一的任务ID(任务ID可以从任务列表获取),当本任务执行结束并且执行成功时,将会触发子任务ID所对应的任务的一次主动调度。
- 调度过期策略:
- 忽略:调度过期后,忽略过期的任务,从当前时间开始重新计算下次触发时间;
- 立即执行一次:调度过期后,立即执行一次,并从当前时间开始重新计算下次触发时间;
- 阻塞处理策略:调度过于密集执行器来不及处理时的处理策略;
单机串行(默认):调度请求进入单机执行器后,调度请求进入FIFO队列并以串行方式运行;
丢弃后续调度:调度请求进入单机执行器后,发现执行器存在运行的调度任务,本次请求将会被丢弃并标记为失败;
覆盖之前调度:调度请求进入单机执行器后,发现执行器存在运行的调度任务,将会终止运行中的调度任务并清空队列,然后运行本地调度任务;
- 任务超时时间:支持自定义任务超时时间,任务运行超时将会主动中断任务;
- 失败重试次数;支持自定义任务失败重试次数,当任务失败时将会按照预设的失败重试次数主动进行重试;
xxl-job框架的使用步骤:
1、从GitHub下载对应项目代码
2、将xxl-job-core编译成jar包,上传到远程仓库(执行器需要应用对应jar包)
3、调度中心部署:
(可以用Jenkins打包发布)
1)将xxl-job-master整体打成jar包(删除xxl-job-excutor-samples模块)
2)对应jar包生成容器镜像
3)在机器上面配置环境变量,然后直接启容器镜像
4、调度项目接入
1)pom文件添加依赖
<!-- xxl-job -->
<dependency><groupId>com.xuxueli</groupId><artifactId>xxl-job-core</artifactId><version>${xxl.job.core.version}</version>
</dependency>
2)添加java配置及yaml配置
# xxl-job
xxl:job:### xxl-job admin address list, such as "http://address" or "http://address01,http://address02"admin.addresses: http://127.0.0.1:8083/xxl-job-admin### xxl-job, access tokenaccessToken: jrU2XoTTq2VSuh5n0J83z40jbcZL1Yn1executor:### xxl-job executor appnameappname: xm-ds-executor### xxl-job executor registry-address: default use address to registry , otherwise use ip:port if address is nulladdress:### xxl-job executor server-infoip:port: 9999### xxl-job executor log-pathlogpath: /data0/logs/xxl-job### xxl-job executor log-retention-dayslogretentiondays: 30
XxlJobConfig.java
@Bean
public XxlJobSpringExecutor xxlJobExecutor() {logger.info(">>>>>>>>>>> xxl-job config init.");XxlJobSpringExecutor xxlJobSpringExecutor = new XxlJobSpringExecutor();xxlJobSpringExecutor.setAdminAddresses(adminAddresses);xxlJobSpringExecutor.setAppname(appname);xxlJobSpringExecutor.setIp(ip);xxlJobSpringExecutor.setPort(port);xxlJobSpringExecutor.setAccessToken(accessToken);xxlJobSpringExecutor.setLogPath(logPath);xxlJobSpringExecutor.setLogRetentionDays(logRetentionDays);return xxlJobSpringExecutor;
}
3)代码改造
在原来的quartz代码基础上,加上注解即可。注意注解后面的JobHandler名称需要跟控制台一致。
日志如果要记录到远程记录上面,需要使用对应的日志方法 XxlJobHelper.log(“XXL-JOB, Hello World.”);
@XxlJob("demoJobHandler")
public void demoJobHandler() throws Exception {XxlJobHelper.log("XXL-JOB, Hello World.");
}
5、调度中心配置任务触发详情
1)添加执行器(新应用接入,添加后需要过30s左右时间,机器会自动注册过来)
注:此处的AppName需要跟第四步的2)yaml文件里面的appname一致。名称是用于页面其他地方展示执行器的名称
2)创建任务
注:此处的执行器,选择上一步添加的执行器,然后填写任务描述、负责人、报警邮件(接收人),填写调度配置、任务配置(注解接入属于BEAN模式,JobHandler需要跟第四步的3)里面注解填写的JobHandler名称一致,填写高级配置(路由策略、调度过期策略、阻塞处理策略需谨慎选择),子任务ID一般为空,任务超时时间和失败重试次数根据业务需要填写
3)任务测试及启动
此处执行一次,相当于手动触发一次立即执行的任务。业务允许的情况下,最好执行一次测试下,看下是否有问题(可以看执行日志)
4)查看执行日志,看下执行是否正常
注:实际的日志是记录到对应的执行器服务器上面的文件的,所以如果执行器未注册到调度中心,日志是看不了的
5)运行报表,可以用于查看每天任务调度情况
参考:
https://www.xuxueli.com/index.html