澎湖县网站建设_网站建设公司_外包开发_seo优化
2025/12/20 0:19:54 网站建设 项目流程

一、问题描述:在xxl-job中,因为项目使用了mysql的悲观锁,导致加锁时间非常慢,因此采用了redis的分布式锁,发现5个进程同时运行时,有的任务同一秒钟会触发两次,导致撮合系统压力巨大。
二、问题定位
2.1、工作原理:
现货任务是通过定时任务执行的,执行周期可配置,然后定时任务会定时执行该任务。
通过xxl-job的执行日志的调度时间有时同一秒调用两次同一个任务,可分析处应该是调度系统出现问题,当任务执行周期设置为5秒时,应该是每5秒执行一次。
三、机理分析
正常情况下,xxljob获取数据的条数是根据(快的线程池中最大线程数+慢的线程池最大线程数)*20=本次最多可以执行多少个定时任务。每次执行任务是查询数据库的条件是本次最多可执行的任务数和当前时间+5秒的数据。有6个进程在同时执行这块的逻辑,只有抢到锁的进程才能真正的查询数据库做后面的操作。
而当前的处理的逻辑恰好是分布式的逻辑出现问题导致同一秒任务会重复执行。下面是发生问题的代码。hasKey和stringRedisTemplate.opsForValue().set方法并不是原子性操作,在多线程的情况下存在竞态场景,A线程发现key不存在,于是加锁,当A正在加锁但是没有加锁成功时,此时B也判断是否有key,于是B也去加锁,而stringRedisTemplate.opsForValue().set方法会直接覆盖之前的锁,这样就会导致多个线程会覆盖之前的锁。

public void lock() {if(!stringRedisTemplate.hasKey(lockKey)){Long redisLockTimeout = XxlJobAdminConfig.getAdminConfig().getRedisLockTimeout();stringRedisTemplate.opsForValue().set(lockKey,"1",redisLockTimeout, TimeUnit.SECONDS);isLock = true;}}

更重要的下面是这段代码,加了锁之后直接就开始查询数据库了,并没有判断锁是否加成功。这就导致了任务会重复执行的另一个原因,就等于没有加锁,任何进程都可以随时调用这块的逻辑。

 long addlockStartTime = System.currentTimeMillis();distributedLock.lock();long addlockEndTime = System.currentTimeMillis();logger.info(">>>>>>>>> addLock cost:{}ms",addlockEndTime-addlockStartTime);// 1、pre readlong nowTime = System.currentTimeMillis();List<XxlJobInfo> scheduleList = XxlJobAdminConfig.getAdminConfig().getXxlJobInfoDao().scheduleJobQuery(nowTime + PRE_READ_MS, preReadCount);

四、问题复现
该问题为固有问题,测试环境均可以复现。
五、措施及验证情况

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询