一次因sched_name重复引起的定时任务error
1、事件简述
一次因sched_name不唯一引起的部分定时任务error
2、时间过程
今天运营突然在跑过来告诉我今天的定时结算全部处于待结算状态,收到消息后第一反应是定时任务没有执行,查线上log果然没有执行,再次查了定时任务前后日志没有相关的定时任务报错信息,手动点击了定时任务链接触发结算操作,结算成功后慢慢定位信息,查询数据库定时任务QRTZ_TRIGGERS表,发现有很多定时任务的TRIGGER_STATE状态处于error,以致于定时任务没有执行成功,查询了持久化到定时任务数据库的几个服务发布情况,发现只有一个服务昨天发布过并且新增了定时任务持久化配置,立即查找定时任务QRTZ_SCHEDULER_STATE表,果然里面sched_name 有两个服务重复。
3、分析问题
成功定位到问题后进行错误分析,定时任务在执行时首先从QRTZ_TRIGGERS找到定时任务信息,然后找到对应的sched_name。通过sched_name关联到QRTZ_SCHEDULER_STATE表,找到对应的服务INSTANCE_NAME,然后进行执行。因此如果两个服务的sched_name重复则本应该去自己服务执行的定时任务跑到了sched_name相同的另外一个服务,而另外一个Server_INSTANCE并没有相对应的task。因此报错java.lang.ClassNotFoundException: com.wlqq.etc.agent.service.task.ActiveSaleDataTask。出现error。
4、解决步骤
4.1、考虑将最近上线的server进行重启以刷新定时任务状态,但是考虑到直接重启server会断流进行了蓝绿操作,但是TRIGGER_STATE状态仍然处于error。
4.2、考虑通过Scheduler 类的API删除出错的定时任务,但是风险太大跳过。
4.3、最终方案修改最近发布server的sched_name 然后删除老的sched_name 对应下的实例以让另外一个服务能正常找到自己的实例执行定时任务,
4.4、修改了shched-name 并添加scheduler的api接口以删除老的错误的sched_name。发版上线,新的sched_name和对应的定时任务成功持久化到数据库。
4.5、删除老的错误的sched_name对应的instance,这时因为sched-name已经是更新后的sched_name,仅仅通过jobName和jobGroup删除时只能删除刚刚持久化到数据库的新的定时任务,考虑通过sql暴力删除,因为quartz有FOREIGN KEY。删除时要先删除依赖,删除成功后重启TRIGGER_STATE状态处于error的server,问题得到解决。
5、总结
5.1、发生原因
使用quartz配置定时任务,多台服务器使用同一数据库进行开发持久化sched_name不唯一。
5.2、建议
使用quartz配置定时任务,多台服务器使用同一数据库进行开发时,保证数据库qrtz_scheduler_state(任务调度表)中只有一条数据,这样就能保证定时任务是自己的应用服务取走执行的。
5.3、TRIGGER_STATE的状态包括如下:
WAITING:等待
PAUSED:暂停
ACQUIRED:正常执行
BLOCKED:阻塞
ERROR:错误