此文已由作者杨凯明授权网易云社区发布。
欢迎访问网易云社区,了解更多网易技术产品运营经验。
1.背景
目前项目中使用的定时任务框架存在下面这些问题
没有统一的定时任务管理平台
目前项目中使用定时任务的方式比较混乱,有部分系统使用了cron插件,有部分系统直接使用的spring task注解配置,没有一个统一的管理平台。使用cron插件的定时任务的配置集成在ms后台,管理页面比较简陋,需要一个更加友好、功能更加完善的管理系统,专门负责定时任务的管理,包括定时任务的创建、修改、删除、触发、查看历史等
对每个定时任务没有完善的监控
目前项目中没有对定时任务的监控,无法知道定时任务的执行情况和执行时间;当定时任务没有触发时没有告警;没法查看定时任务的执行历史情况
单点问题
目前使用spring task或quartz来做定时任务的系统,都需要手动指定运行定时任务的机器,这样会导致某台机器负载很高,并且如果这台机器宕机了则定时任务都不会执行
重复执行问题
有些定时任务本身不是幂等的,如果重复执行的话会有很多问题,比如短信的定时发送等;目前cron出现过zk断开后,导致定时任务被重复执行的情况
针对这些问题,需要设计一种能解决上述问题的新框架
2.设计
设计目标:
定时任务统一配置、统一管理
支持动态修改任务状态,动态暂停/恢复任务,即时生效
减少使用方的限制依赖
定时任务不遗漏、不重复的被执行
支持任务分片并发执行
完善的监控、统计功能
整个框架分为四部分:
调度服务器管理平台:负责定时任务的配置和管理,并定时进行任务的分配;获取每次任务执行的结果进行统计
任务执行器:通过心跳更新服务器信息;获取配置的任务信息定时执行任务,并根据任务执行情况上报执行结果
zk集群:存储任务配置信息和服务器节点信息;提供分布式协调服务
数据库:记录任务的每次执行情况,用于监控和统计
架构部署图如下:
ZK节点图:
触发类型:
定时触发:通过定时任务框架与cron表达式定时触发
手动触发:通过kschedule平台触发(1.按分配信息触发;2.指定一台机器执行)
启动触发:应用启动时触发(如加载初始化本地缓存)
RPC触发:通过dubbo接口触发
任务类型:
都不执行(只需人为去触发,使用场景较少)
所有的机器上只有一台执行:该产品下所有注册上来的机器只会选择一台来执行
所有机器都执行:该产品下所有注册上来的机器都会执行该任务
在指定的机器上全部执行:被选择的机器都会执行该任务
指定的机器上只有一台执行:被选择的机器中只会有一台会执行该任务
任务分片执行(下面解释)
不重复执行策略及异常整理
任务状态变为DOING状态后,备用机器不再监听任务,认为任务已经有机器执行,如执行的机器在任务执行时挂掉,则造成任务遗漏执行。
如主机器500ms内没有将任务变为DOING状态,则备用机器恢复抢锁进行任务执行,没抢到锁的机器继续监听任务状态。
kschedule服务端挂了:不影响任务执行
执行前服务器网络断开:由备机执行
执行前服务器挂掉:由备机执行
执行中服务器网络断开:任务正常执行,可能会报警遗漏执行
执行中服务器挂掉:任务不能正常执行,会报警
zk挂掉:任务不能正常(zk进程监控)
执行分配的机器全挂掉:任务不能正常执行,会报警
不遗漏执行策略及异常整理
任务状态变为DOING状态后,备用机器继续监听任务,直到任务状态变更为DONE时,监听取消。
如主机器在DOING状态后挂掉或者失去与ZK的连接,则备用机器恢复抢锁进行任务执行,没抢到锁的机器继续监听任务状态,可能会造成任务重复执行。
kschedule服务端挂了:不影响任务执行
执行前服务器网络断开:由备机执行
执行前服务器挂掉:由备机执行
执行中服务器网络断开:由备机执行,可能重复执行
执行中服务器挂掉:由备机执行
执行中任务执行超时:由备机执行,可能重复执行
zk挂掉:任务不能正常( zk进程监控)
执行分配的机器全挂掉:任务不能正常执行,会报警
分片任务:
分片任务即将一个任务按一定规则拆成多个子任务在多台机器上的多个线程中并行执行。
需要实现IScheduleShardingTask接口,
例子1:服务器有5台
分片信息:0,1,2,3,4,5,6,7,8,9
分片最大线程数:10
分片获取数据的数量:100
则kschedule会将任务进行分片,每台机器分配到2个执行线程,每个线程执行1个分片,每个分片拉取100条数据。
每个线程回调到selectItems方法上的参数为selectItems(null,10,[0],100);
selectItems:应用系统在接口实现中根据分片传入的参数去DB拉取待处理的数据。
execute:应用系统在接口实现中根据selectItems方法拉取的数据进行数据处理。
例子2: 服务器有5台
分片信息:a,b,c,d,e,f,g,h,i,j,k,l,m,n,o,p,q,r,s,t,u,v,w,x,y,z
分片最大线程数:10
分片获取数据的数量:1000
则kschedule会将任务进行分片,每台机器分配到2个执行线程,每个线程执行3个分片(部分是2个分片),每个分片拉取1000条数据。
每个线程回调到selectItems方法上的参数为selectItems(null,26,[a,b,c],1000);
selectItems:应用系统在接口实现中根据分片传入的参数去DB拉取待处理的数据。
execute:应用系统在接口实现中根据selectItems方法拉取的数据进行数据处理。
/**
- 分片任务定义接口,业务方需实现该接口才能使用分片功能
- @author hzyangkaiming
*
/public interface IScheduleShardingTask { /*
* 拉取业务数据的方法
* @param parameter 自定义参数
* @param shardingCount 任务分片总数
* @param shardingIndexs 分配到的任务分片
* @param limit 每次获取数据的数量
* @return 待处理的数据
* @author yangkaiming
* @since 2016年5月16日
*/
public List selectItems(String parameter,int shardingCount,List shardingIndexs,int limit); /**
* 执行指定的任务
*
* @param items 处理的数据
* @return 成功/失败
* @author yangkaiming
* @since 2016年5月16日
*/
public boolean execute(T[] items);
执行日志:
执行日志会在框架调度业务方法时进行记录,写到ZK,kschedule平台会收集并清理zk。
用户可以在kschedule平台查询任务执行情况,包括执行时间情况,任务每次触发执行的服务器等信息
3.监控
1.根据cron表达式及任务执行日志,对任务漏执行、重复执行、超时执行等进行准实时报警。
2.任务执行失败(业务方法未捕获的Exeption)报警,并推送错误堆栈,帮助定位解决问题。
2.支持非常规节假日短信报警,平日上班时间popo、stone报警,下班时间popo、stone、短信报警。
4.实践
经过一年的推广及优化,考拉目前各系统定时任务已经基本完成切换,并稳定运行于kschedule平台,当然也有不少坑:
坑1:业务方法卡住
背景:定时任务运行时会占有该任务的分布式锁,如果任务挂住会造成后面每次触发任务都会忽略执行,防止重复执行。
问题: http,ftp等外部调用相关业务定时任务出现任务挂住的问题
实时处理:任务出现超时并长期运行中,则优先打印堆栈信息,保留现场,并根据业务方评估是否人工去zk释放锁,让任务先跑起来。
后期处理:业务放根据堆栈等信息定位卡主原因进行优化(主要以超时时间为主),平台增加实时zk锁查询,判断任务是否占用锁,并增加管理员删锁按钮,提升运维能力。
坑2:新上线分片任务重复执行并执行紊乱
背景:分片任务会根据分配信息异步起线程(有默认线程池)进行处理,并且单个分片没有加分片的分布式锁。业务放上线10个分片任务,每个分片任务10个线程,总共4台机器,触发时间都是每分钟的0秒,默认线程池5个初始线程,1000的队列,队列满扩到20个线程
问题:业务系统上线,业务系统ftp调用超时问题也比较多,造成分片任务执行完全紊乱,执行日志无参考性,业务方法出现重复执行
实时处理:经过初步判断,根据业务方分片任务数量及线程量,优先加大线程池提高处理能力,紧急上线,但不能完全解决问题。
后期处理:修改默认线程池策略,去掉缓冲队列,线程不够直接扩,最大200个,超过150个报警;因为业务方需要高频率调度这些分片任务,加上与ftp调用等执行时间不可控因素,因此增加单个分片的分布式锁,防止重复执行,并且极大降低线程占用量。
那么现在:
kschedule平台目前每日的任务执行次数平均在20+w次。
网易金融也已搭建自己的kschedule环境,并在线上使用中。
考拉订单流推送、申报单推送、物流信息等供应链相关业务已接入分片任务,极大提高了业务吞吐量、降低DB压力,提升了通关效率。
支撑双十一、黑五、双十二等大促,高峰期统一暂停非关键定时任务,让出系统资源,提高业务系统稳定性。
5.计划
近:任务执行时间TOP统计,方便业务系统优化定时任务性能等;
远:可以根据考拉的业务需要,增加非java方法调度功能等;
欢迎考拉的兄弟姐妹贡献需求及改进点
网易云免费体验馆,0成本体验20+款云产品!
更多网易技术、产品、运营经验分享请点击。
文章来源: 网易云社区
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。