Quartz部分定时任务不执行问题分析过程及修复方案
背景:
2021年1月7号上线迁移需求之后,出现最为明显的 众帮文件上传 其他部分定时任务也不执行的情况 执行时间并没有按照约定时间去执行
分析1 怀疑是代码问题?
对众帮文件上传定时任务代码进行排查 并无发现编码问题,并且对1月7号上的版本 和上一个版本进行代码比对 并未发现对众邦的代码有所改动的地方
分析2 项目定时任务框架 spring quartz 内部任务存储 调度问题?
Quartz 是基于RAMJobStore调度中心去调度的,里面有Job(任务),Trigger(触发器)。
通过对项目定时任务代码的了解 及源码的分析 得知:
项目启动时 会把applicationContext-quartz.xml配置文件中的所有定时任务加载到jobDefinitions 这个队列中,遍历jobDefinitions 把每个定时任务的 job 和 Trigger获取到,通过Quartz API 把定时任务添加到RAMJobStore, 经过对jobDetail的校验 ,这里主要是校验name,group,jobClass 是否为空,然后jobDetail的下次触发时间是否合法 然后添加到RAMJobStore(JobWrapper, TriggerWrapper)中。
秉着这一逻辑思想
1月14号上线 项目重启后 拉取项目 堆栈包heap1.hprof
使用jprofiler分析工具 打开heap1.hprof
如图 找到RAMJobStore中的两个存储对象 发现job 和 trigger中存储的数量是一致的,于是我打算点对点的排查 由于生产最为明显的是众帮文件上传定时任务不执行 所以我从这76任务中,找到众帮文件上传的wrapper 看看是不是有什么问题。
如图可以看出 众帮文件上传的定时任务 从开始执行时间,下次触发时间,是否有配套的监听器 都是正常的 ,可就是不执行,于是我开始quartz调度的过程,如何调度的执行的。
几经翻查 发现quertz 定时任务在调度时,都需要初始化一个线程池SimpleThreadPool 而这个线程池大小默认是10,而quertz任务触发调度的原则是:
1、线程池资源获取等待定时任务过期作废机制。
2、Quartz框架的定时任务执行是绝对时间触发的,所以存在“过期不候”的现象。
所以开始怀疑是项目定时任务过多87个 并且大部分定时任务都是 10秒 ,20秒,30秒 1分钟执行一次 过于密集
分析3 quartz 调度初始化没有问题 定时任务多,且时间密集导致?
为了复现和生产一样的情况 众帮及其他部分定时任务不执行的情况,由于测试环境基本没有数据,所以每个定时任务执行的非常快,所以不存在生产的情况,所以我把代码中每个定时任务 让他们执行时,都线程等待60秒,并且做了一个定时任务计数器。
“fpcCallBack”:205,
“alipayPromoteDataAndRule”:1657,
“bdTransactionCallBack”:680,
“networkVerifyDataAndRule”:5,
“processDataAndRule”:976,
“paphLoanDataAndRule”:203,
“fplCallBack”:124,
“jdDataAndRule”:828,
“paphDataAndRule”:312,
“callBack”:511,
“fpShareLoanPeople”:1260,
“fpcDataAndRule”:1282,
“wsdMigrateCreditAckDate”:9,
“zhongbangFileUp2View”:“1”,
“sendTxnJob”:“1”,
“AliPayBigTextAnalysis”:393,
“pplDataAndRule”:33,
“sendTxnBdCreditMigrate”:18,
“blackList”:46,
“sendTxnJDLoan”:15,
“creditCallBack”:1312,
“fileSyncDataAndRule”:1529,
“ppcDataAndRule”:127,
“jLFSCallBack”:62,
“qlLoanDataAndRule”:2,
“OpenAccountNetCheck”:54,
“zbCallBack”:3,
“yjSendTxnLoan”:5,
“gjjCallBack”:101,
“ppdDataAndRule”:82,
“alipayDataAndRule”:1687,
“jfDataAndRule”:506,
“jfCallBack”:17,
“qlLoanCallBack”:2,
“bdCreditDataAndRule”:4966,
“alipayHBDataAndRule”:1673,
“fileSyncCallBack”:3123,
“youXLoanDataAndRule”:6,
“pplCallBack”:4,
“bdCreditCallBack”:794,
“wsdConfirmDataAndRule”:12,
“bdAdjustLimitMigrate”:3045,
“sendSoapJob”:1102,
“OdMonitor”:1321,
“estDateSourceMonitor”:25,
“qianLLCallBack”:4,
“yxCallBack”:“1”,
“jLFSDataAndRule”:605,
“zbDataAndRule”:2,
“cxcDataAndRule”:535,
“bdTransactionDataAndRule”:3224,
“bdAdjustmentCallBack”:1020,
“fileUpload”:678,
“bdCreditMigrate”:2043,
“yxLoanDataAndRule”:6,
“sendTxnLoan”:11,
“ppcCallBack”:9,
“wsdDataAndRule”:10,
“dfCallBack”:477,
“cxlDataAndRule”:475,
“gjjDataAndRule”:725,
“qianLLoanDataAndRule”:519,
“qlCallBack”:“1”,
“fileDownloadAndUpBD”:386,
“tszCallBack”:“1”,
“tszDataAndRule”:“1”,
“yxDataAndRule”:6,
“bdAdjustmentDataAndRule”:6760,
“yjDataAndRule”:8,
“jdCallBack”:1153,
“bdTransactionMigrate”:1913,
“wsdMigrateCreditDate”:9,
“yjFileSyncDataAndRule”:8,
“fplDataAndRule”:1308
从计数器打印结果可以看出
在测试环境执行了15小时左右 果然出现了和生产一样的情况
一共87个定时任务 执行了73个有19个未执行 已执行的存在执行次数为个位数的情况
所以我认定quartz定时任务执行存在线程瓶颈 与此我发现为什么众帮的文件上传一次执行的机会都不能抢到吗?想必小伙伴们也会有这种猜疑 就算触发时间相同情况下 可触发的数量有限 难道众邦一次都抢不到执行线程吗?
于是 又一顿的百度 博客 终于找到了对应的解释
默认情况是触发时间先后顺序排列,触发时间比较前的先执行任务,但如果一个或多个任务同时在相同时间触发下,触发器设置优先级越高越先执行。如果优先级相同,则跟任务的存储方式有关,RAMJobStore时与TriggerKey排序有关,即按触发器名的字母序;如果是JdbcStore则跟数据库查询的默认排序有关了。Trigger优先级默认为5,数值越大优先级越高
这会大家应该明白了吧 众邦的定时任务名称叫做 zhongbangFileUp2ViewJob 所以 在相同触发条件下 并且执行数量有限的情况下,他被淘汰了
**
问题解决:
那么此时留下的问题就是 解决 quartz执行线程数量太少10个的问题
我们要做的就是 增加quartz 执行线程数量
由于本项目 quartz 的框架版本过低1.5.2 无法通过quartz.properties
进行声明线程数量 这里我就不想吐槽了 试了很多种办法不行 ,而且版本调整到2.0.0以上后 OPS 封装的quarzt代码全部报错 版本完全不兼容。
于是我们在applicationContext-quartz.xml中
在SchedulerFactoryBean中对quartz.properties进行复写 并且改变线程数量为100,以及Trigger检查器 默认每次只Acquire一个Trigger。
丢到测试环境测试 定时任务计数器打印结果如下:
“bdCreditDataAndRule”:128,
“fpShareLoanPeople”:14,
“gjjCallBack”:14,
“youXLoanDataAndRule”:16,
“jdCallBack”:15,
“jfCallBack”:31,
“bdAdjustLimitMigrate”:30,
“sendTxnJDLoan”:32,
“pplDataAndRule”:15,
“estDateSourceMonitor”:“1”,
“fplDataAndRule”:11,
“wsdMigrateCreditDate”:9,
“creditCallBack”:7,
“paphDataAndRule”:10,
“tszDataAndRule”:12,
“ppdDataAndRule”:51,
“wsdMigrateCreditAckDate”:19,
“dfCallBack”:10,
“yxLoanDataAndRule”:43,
“zhongbangFileUp2View”:2,
“qianLLoanDataAndRule”:11,
“fpcDataAndRule”:12,
“callBack”:9,
“paphLoanDataAndRule”:11,
“pplCallBack”:11,
“blackList”:2,
“yjSendTxnLoan”:39,
“ppcDataAndRule”:20,
“cxlDataAndRule”:10,
“yxLoanCallBack”:14,
“gjjDataAndRule”:23,
“processDataAndRule”:19,
“sendTxnLoan”:37,
“ppcCallBack”:15,
“alipayPromoteDataAndRule”:8,
“qlLoanCallBack”:8,
“qlCallBack”:6,
“bdTransactionDataAndRule”:129,
“qlDataAndRule”:8,
“fileSyncCallBack”:156,
“plagueMonitor”:2,
“yjFileSyncDataAndRule”:49,
“fplCallBack”:10,
“bdAdjustmentCallBack”:19,
“bdAdjustmentDataAndRule”:149,
“sendTxnBdCreditMigrate”:39,
“bdCreditCallBack”:14,“fpcCallBack”:9,
“bdTransactionMigrate”:35,
“yjDataAndRule”:39,
“cxcDataAndRule”:14,
“bdTransactionCallBack”:24,
“sendSoapJob”:22,
“fileDownloadAndUpBD”:24,
“alipayDataAndRule”:8,
“OpenAccountNetCheck”:3,
“fileUpload”:7,
“AliPayBigTextAnalysis”:11,
“qlLoanDataAndRule”:14,
“jfDataAndRule”:10,
“qianLLCallBack”:24,
“bdCreditMigrate”:35,
“alipayHBDataAndRule”:13,
“wsdDataAndRule”:38,
“jdDataAndRule”:15,
“yxDataAndRule”:8,
“sendTxnJob”:19,
“wsdConfirmDataAndRule”:37,
“yxCallBack”:10,
“OdMonitor”:14,
“fileSyncDataAndRule”:36,
“zbCallBack”:15,
“tszCallBack”:11,
jLFSDataAndRule":13,
“jLFSCallBack”:9,
“zbDataAndRule”:39
从结果中可以看出 每次定时任务都执行了,并且执行的次数都比较均匀,不像一开始 有的执行几千次,有的执行几十次,受线程执行数 已经定时任务首字木影响较大。 而且从结果中可以看出 众帮的数据处理执行了39次 众邦的文件上传 每10分钟执行一次 每次轮到执行的执行时,都执行了,不存在拿不到执行线程的情况
quartz定时任务使用结论
1、线程池资源获取等待定时任务过期作废机制。
2、Quartz框架的定时任务执行是绝对时间触发的,所以存在“过期不候”的现象。
3、在使用Quartzs框架时,一定要预先计算好triggers数量与线程池大小的匹配程度,资源一定要够,或者任务执行密度不能太大,否则等到线程任务释放完,trigger早已过期,就无法按预期时间触发了。
4、在进行业务代码开发过程中 尽量一个定时任务中处理多种业务代码,做异步线程处理,对定时任务进行归类 抽取
5.根据业务情况 不要一上来就增加定时任务,靠定时任务处理问题
来源:oschina
链接:https://my.oschina.net/u/4276902/blog/4917442