多节点定时任务校准

                                  业务场景

        最近对公司项目做优化,之前的订阅项目是指定主副节点模式,目前为了迎合市场分布式微服务技术,需改为无主节点概念的模式。

        通过查询资料决定用redisson做分布式锁,多节点根据持锁标识决定是否分发任务;

      try {
        log.info("======抢《apiErr任务》锁======" + ServerUtils.getHostIpAndPort());
        boolean isGetLock = distributedLocker.tryLock(key, TimeUnit.SECONDS, 5L,  60 * 60L);
        if (isGetLock) {
          log.info("======获得《apiErr任务》锁======" + ServerUtils.getHostIpAndPort());
          subscribeApiErrorService.handleApiErr(null, SubscribeConstants.TRIGGER_TYPE_0);
        } else {
          log.info("======未获得《apiErr任务》锁======" + ServerUtils.getHostIpAndPort());
        }
      } catch (Exception e) {
        distributedLocker.unlock(key);
        e.printStackTrace();
        log.error("《apiErr任务》获取异常:" + e.getMessage(), e);
      }

       

        通过redisson是解决了多节分发问题,但是我们项目不是电商秒杀类的项目,是很定时任务分配,还需要解决多节点抢锁时间的校准。翻翻资料,参考XXL-JOB做了下符合我们业务场景的校准。

Quartz :

       定时任务执行时使用数据库的表锁,保证一个任务只能被一个节点触发,而表锁随着事务的提交或者回滚自动释放。

xxl-job  触发校准:

    xxl-job是基于 Quartz实现,拿到了距now 5秒内的任务列表数据:scheduleList,分三种情况处理:for循环遍历scheduleList集合

        (1)对到达now时间后的任务:(超出now 5秒外):直接跳过不执行; 重置trigger_next_time;

        (2)对到达now时间后的任务:(超出now 5秒内):线程执行触发逻辑; 若任务下一次触发时间是在5秒内, 则放到时间轮内(Map<Integer, List<Integer>> 秒数(1-60) => 任务id列表);

                 再 重置trigger_next_time;

        (3)对未到达now时间的任务:直接放到时间轮内;重置trigger_next_time 。

经过参考xxl-job 的任务触发机制,调整订阅的任务分配流程,如下:

    log.info("======《分配任务》开始======" + ServerUtils.getHostIpAndPort());
    String key = "redisson_task_key";
    try {
      log.info("======抢《分配任务》锁======" + ServerUtils.getHostIpAndPort());
      boolean isGetLock = distributedLocker
          .tryLock(key, TimeUnit.SECONDS, 3*60L, 3*60L-1);
      if (isGetLock) {
        long time = System.currentTimeMillis();
        log.info("======获得《分配任务》锁======" + ServerUtils.getHostIpAndPort());
        if(null != redisUtil.get("_task_time")){
          long taskTime = (Long)redisUtil.get("_task_time");
          if((time<(taskTime-5*1000)) && (time>=(taskTime-3*60*1000))){
            distributedLocker.unlock(key);
            return;
          }else if((time>(taskTime+5*1000)) &&(time<=(taskTime+3*60*1000)) ){
            distributedLocker.unlock(key);
            return;
          }
        }
        redisUtil.set("_task_time",time+3*60*1000);
        try {
          //业务......
        }catch (Exception e1) {
          log.error("任务执行失败:" + e1.getMessage(), e1);
        }
      } else {
        log.info("======未获得《分配任务》锁======" + ServerUtils.getHostIpAndPort());
      }
    } catch (Exception e) {
      distributedLocker.unlock(key);
      e.printStackTrace();
      log.error("《分配任务》锁获取异常 ip:" + ServerUtils.getHostIpAndPort() + ",错误信息:" + e.getMessage(), e);
    }
    log.info("======《分配任务》结束======" + ServerUtils.getHostIpAndPort());

   写的还有点简单,欢迎大佬指点!

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值