xxl-job:记录一次线上问题的分析和排查

567 阅读3分钟

一.问题描述

我们线上有两台xxljob的机器,ip分别是39和40,但是运行一段时间后,有人反馈有些任务会执行两次,导致重复执行

二.问题排查

2.1 排查日志记录

通过对触发日志的查询,确实同一时刻有两条日志都触发了

image.png

但是我记得xxl是支持集群部署的,多台机器同一时刻只有一台机器会执行,通过查看代码,得知

while (!scheduleThreadToStop) {  
  
// Scan Job  
long start = System.currentTimeMillis();  
  
Connection conn = null;  
Boolean connAutoCommit = null;  
PreparedStatement preparedStatement = null;  
  
boolean preReadSuc = true;  
try {  
  
conn = XxlJobAdminConfig.getAdminConfig().getDataSource().getConnection();  
connAutoCommit = conn.getAutoCommit();  
conn.setAutoCommit(false);

// todo 这里会获取xxl_job_lock表的锁,哪一台机器抢到锁,哪一台会执行
preparedStatement = conn.prepareStatement( "select * from xxl_job_lock where lock_name = 'schedule_lock' for update" );  
preparedStatement.execute();  

但是通过程序发现这个锁没有启动用,于是查询数据库得知 select * from xxl_job_lock where lock_name = 'schedule_lock' ; 表中为空......啊,原来是忘记插入这个表的数据了,于是两台机器都能执行,导致任务会重复触发

三.问题修复过程中的问题

问题很容易就排查出来了,那么就很简单了,原来是数据忘记插入了,那直接执行sql语句插入数据不就行了 INSERT INTO xxl_job.xxl_job_lock ( lock_name) VALUES ( 'schedule_lock');

但是紧接着问题就来了

3.1 问题

在插入过程中,会等待很久,插入数据失败,过了51秒后,会报超时

image.png

然后就查询mysql设置的过期时间

image.png 确实是50秒,没问题,能对的上

3.2 设置超时时间,再次执行sql

SET innodb_lock_wait_timeout = 150; 我把mysql的超时时间设置为150秒,再次执行插入语句,执行多次后依旧插入数据失败,于是我感觉问题不是那么简单

3.3 超时时间分析

为什么超时时间边长后,还是执行不了呢,原因是线上环境存在大量定时任务,通过查询有360多个任务,线上两台机器还在一些死循环获取这个锁,哪一台机器获取锁之后,就会执行这些任务,导致我本地很难抢到锁,咋办?难道要把程序stop一次,那就算是事故了.....

3.4 问题解决

既然被锁住了,肯定有一些进程在等待,思路就是找到这些线程,然后杀死 使用 show processlist 这个命令不能添加where过滤条件

image.png

于是使用这个语句 select * from information_schema.processlist where db='xxl_job' 把id查出来后,批量杀死进程

kill 3952509;
kill 3952510;
kill 3952511;
kill 3952522;
kill 3952513;
kill 3952520;
kill 3952514;
kill 3952515;
kill 3952507;
kill 3952525;
kill 3952521;
kill 3952523;
kill 3952506;
kill 3952474;
kill 3952517;
kill 3952505;
kill 3952518;
kill 3952519;
kill 3952508;
kill 3952516;
kill 3952524;

然后再次执行 INSERT INTO xxl_job.xxl_job_lock ( lock_name) VALUES ( 'schedule_lock');语句,成功插入,至此数据插入完成,程序也不在报错

四.验证

我把这个任务触发时间修改了下,等待任务触发

image.png 修改后,任务确实只触发一次,不会重复执行