文章目录
1. 定时任务概述
1.1 什么是定时任务
定时任务是一种按照预设的时间和频率执行的程序。
1.2 定时任务的常用场景
- 数据备份和归档:如定时在低峰时段(如凌晨)自动备份数据
- 系统维护:如定期删除过期的日志,缓存等数据
- 系统监控:定期检测系统资源的使用状态等
- 订单处理:超时未支付订单时取消订单
- 定时通知:一些周期性的事件,可以定时发邮件通知
- 同步状态
- 报表生成
1.3 定时任务的常用实现
- Timer和TimerTask
- ScheduledExecutorService
- Spring Framework的Task Scheduling
- Quartz
- xxl-job:一个分布式任务调度平台,支持动态配置、弹性扩容缩容、失败重试等功能,适合微服务架构下的定时任务调度。
2. Cron表达式
Cron表达式是一种用于配置定时任务的时间表达式,常来指定任务的执行时间、执行频率和执行间隔。Cron表达式由6或7个字段组成,分别表示秒、分、时、日期、月份、星期、年份(通常省略)。格式如下所示:
* * * * * * [year]
- - - - - -
| | | | | |
| | | | | +--- 星期域 (0 - 6) (周日=0)
| | | | +----- 月份 (1 - 12)
| | | +------- 日期 (1 - 31)
| | +--------- 小时 (0 - 23)
| +----------- 分钟 (0 - 59)
+------------- 秒 (0 - 59)
对于每个字段的值,可以使用如下的字符:
*
:代表任何可能的值,表示每个单位时间都要执行。
,
:指定枚举值时使用,如x,y表示在第x个单位时间和第y个单位时间执行,多个枚举值之间用都要隔开。
-
:定义一个时间范围,如x-y表示从第x个单位时间到第y个单位时间,每个单位时间执行一次。
/
:表示等步长序列,如x/y表示第x个单位时间执行一次,之后每隔y个单位时间执行一次。
?
:通常只用于日和周的字段,表示不确定的值。因为每月的日和每周的日会相互影响。如每月的第20日执行,日期填20,而星期域要填?
而不是*
。
L
:只在日期域和星期域中使用,表示最后月末和周末。
W
:只在日期域中使用,表示最接近指定天的工作日。
#
:只在星期域中使用,表示月份中第几个星期几,例如x#y表示第y个星期x
3. 定时任务的简单方案:Spring Framework的Task Scheduling
3.1 使用流程
- 在启动类上使用
@EnableScheduling
注解来开启定时任务 - 创建任务调度类,并加上
@Configuration
注解 - 在任务调度类中的方法上,使用
@Scheduled(cron = "* * * * * *")
注解来配置定时任务 - 完成定时任务的方法体
3.2 多线程异步任务
在任务调度类上加上@EnableAsync
注解,并在定时任务的方法上加上@Async
注解
4. 微服务中定时任务的优选方案:xxl-job
4.1 xxl-job与quartz对比
- quartz采用api的方式调用任务,不方便;xxl-job提供了管理界面。
- quartz比xxl-job代码侵入更强。
- quartz调度逻辑和QuartzJobBean耦合在一个项目中,当任务增多,逻辑复杂的时候,性能会受到影响。
- quartz底层以抢占式获取DB锁并且由抢占成功的节点运行,导致节点负载悬殊非常大;xxl-job通过执行器实现协同分配式运行任务,各个节点比较均衡。
4.2 xxl-job与elastic-job对比
- elastic-job是去中心化的,通过zookeeper的选举机制选出主服务器,如果主服务器挂了,重新选举出主服务器,因此elastic-job的扩展性和可用性较好,但是使用有一定的复杂度。适用于业务复杂,业务量大,服务器多的情形。
- xxl-job是中心式的调度平台调度执行器执行任务,使用的是DB锁来保证集群分布式调用的一致性,学习简单,操作容易,成本不高。
4.3 任务的开发与配置
- 创建任务
- 在 调度中心->执行器管理 新增执行器
- 在调度中心->任务管理 添加并配置任务
- 执行任务
4.4 xxl-job创建任务之BEAN模式
Bean模式:将JobHandler作为Spring Bean进行管理的一种方式,这种方式可以让JobHandler更加自然地融入到Spring应用的上下文中,便于利用Spring的依赖注入等功能。
BEAN模式有两种形式:
- 类形式:每个类对应一个任务,该类要继承自
com.xxl.job.core.handler.IJobHandler
并实现其中的任务方法,需要在类上使用@JobHandler
注解来实现自动注册(扫描到任务并注入到执行器容器)。 - 方法形式:每个方法对应一个任务,需要在方法上使用
@XxlJob
注解来实现自动注册。
4.5 新增执行器
通常每个项目在每一个环境下有一个执行器,需要配置的属性如下:
- AppName:是每个执行器集群的唯一标示AppName, 执行器会周期性以AppName为对象进行自动注册。可通过该配置自动发现注册成功的执行器, 供任务调度时使用。
- 名称:执行器的名称, 因为AppName限制字母数字等组成,可读性不强, 名称为了提高执行器的可读性。
- 排序:执行器的排序, 系统中需要执行器的地方,如任务新增, 将会按照该排序读取可用的执行器列表。
- 注册方式:调度中心获取执行器地址的方式。 自动注册,执行器自动进行执行器注册,调度中心通过底层注册表可以动态发现执行器机器地址。手动录入,人工手动录入执行器的地址信息,多地址逗号分隔,供调度中心使用。
- 机器地址:"注册方式"为"手动录入"时有效,支持人工维护执行器的地址信息。
4.7 添加并配置任务
在任务管理模块,先选择相应的执行器,然后点击新增任务。
然后在界面配置任务的信息,核心配置介绍如下:
- 路由策略:决定如何将任务分配给不同的执行器的策略,这里暂时选轮询。
- 运行模式:选BEAN模式就行。
- Cron:略。
- JobHandler:就是定义任务是任务的名字(注解中的那个参数)。
- 阻塞处理策略:前一个任务没执行完,后一个任务要开始了,此时的处理策略。可选单机串行、丢弃后续调度和覆盖之前调度。
- 任务参数:执行定时任务所需的参数,以JSON字符串格式给出。
4.8 操作任务
在任务管理模块,选择相应的和执行器后就会展示相应的任务列表,点击操作按钮就可以进行编辑、启动和删除等操作。