背景:
任务调度是日常开发中非常常见的一个业务场景。目前系统采用的是Quartz框架进行任务调度,但与业务系统耦合性太高,极端情况下,耗时任务甚至会耗尽调度线程,导致大量任务堵塞与延迟。也可能拖垮业务系统。
另一方面Quartz没有后台管理界面。问题定位排查 手动触发任务,随时修改任务执行时间等较难。
综上所述,我们需要解决以下几个问题:
1:有良好的后台管理页面。
2:任务动态分片,数据庞大的大任务处理。
3:任务阻塞,路由及报警策略。
4:开发文档和社区完善。
调度框架对比选型
此次主要对xxl-job(大众),Elastic-job(当当),staturn(唯品会),lts,TBSchedule(阿里)五种调度框架进行综合对比。
对比总结:
e-Job和xxl-job都有广泛的用户基础和的技术文档,都能满足定时任务的基本功能需求。
e-Job已有2年左右没更新,社区也已经不维护,后续稳定性无法保证。
xxl-job 文档详细,且任务报警、阻塞及路由策略丰富,社区完善。
因此采用xxl-job调度框架。
XXL-JOB特性简介
-路由策略:- 阻塞处理策略 -子任务:-失败报警 -分片广播 & 动态分片 等
这里就不照搬官网参数,参考:https://github.com/xuxueli/xxl-job/
- 任务调度错过触发时间时的处理策略:
可能原因:服务重启;调度线程被阻塞,线程被耗尽;上次调度持续阻塞,下次调度被错过;
处理策略:
过期超5s:本次忽略,当前时间开始计算下次触发时间
过期5s内:立即触发一次,当前时间开始计算下次触发时间
目前压测存在的问题:
1、在上万任务同时并发时 。发现有部分任务丢失问题。
此问题可通过以下三种方案提升并发量:
a.通过加大调度线程池数量。
b.增加调度中心集群数量。
c.修改过期任务处理策略,默认为5秒。
官方数据:单机并发5千,目前本地实测:单机并发1千
2、因外部因素导致服务宕机时,正在执行的任务可能会丢失。例如:服务器断电。kill -9
此问题通过二次开发解决,方案如下:
a.心跳检测时,将下线的机器中正在执行的任务标记为失败,并重试。(默认30秒检测一次,3次失败则将此执行器下线,如果执行器在心跳检测间隔期重启完成,则正在执行中的任务会卡住无结果,解决方案参考b)
b.有新执行器注册时,查找此执行器是否有正在执行中的任务,有则标记为失败,任务会自动重试。
3、不支持重复性一次性任务
二次开发可以解决,即利用同一个任务,每次通过接口方式传入需要执行的参数,例如:订单号10086在15分钟后超时取消。 通过调用启动任务接口,动态传参即可
接入指南:
1、在admin后台添加对应的执行器。AppName为唯一标识
2、添加Maven依赖
<dependency>
<groupId>com.xuxueli</groupId>
<artifactId>xxl-job-core</artifactId>
<version>2.1.1-SNAPSHOT</version>
</dependency>
3、添加xxl-job.properties
### 调度中心部署跟地址:如调度中心集群部署存在多个地址则用逗号分隔。执行器将会使用该地址进行"执行器心跳注册"和"任务结果回调";为空则关闭自动注册;
xxl.job.admin.addresses=http://127.0.0.1:8080/xxl-job-admin
### 执行器AppName:(在admin后台配置的AppName) 执行器心跳注册分组依据;为空则关闭自动注册,同一个执行器集群内AppName需要保持一致;调度中心根据该配置动态发现不同集群的在线执行器列表。
xxl.job.executor.appname=top-service-job
### 执行器IP:默认为空表示自动获取IP,多网卡时可手动设置指定IP,该IP不会绑定Host仅作为通讯实用;地址信息用于 "执行器注册" 和 "调度中心请求并触发任务";
xxl.job.executor.ip=
### 执行器端口号 :小于等于0则自动获取;默认端口为9999,单机部署多个执行器时,注意要配置不同执行器端口;
xxl.job.executor.port=9999
### 执行器通讯TOKEN :非空时启用;
xxl.job.accessToken=
### 执行器运行日志文件存储磁盘路径 :需要对该路径拥有读写权限;为空则使用默认路径;
xxl.job.executor.logpath=/data/applogs/xxl-job/jobhandler
### 执行器日志保存天数 :值大于3时生效,启用执行器Log文件定期清理功能,否则不生效;
xxl.job.executor.logretentiondays=-1
至此xxl-job接入完成,即可开始开发任务。