美文网首页
多节点下定时任务单个线程执行

多节点下定时任务单个线程执行

作者: 菜的无法无天 | 来源:发表于2020-11-05 22:56 被阅读0次

    微服务系统,系统按照业务被拆分成多个模块,每个模块被部署在不同节点服务器上一个或者多个。这样的话,对定时任务而言就产生一些不确定性。对于包含定时任务的模块,你不确定这个服务被部署了多少份,假如被部署了3份,每天8点执行一些定时任务,如果不加处理的话,定时任务会被执行3次,这样非常不好。

    目前有很多方法来解决这个问题

    • 如把定时任务统一写在一个模块内,让这个开发|测试|生产只跑一个服务,就可以避免这个问题,按照这个思路有现成的、更好的第三方框架 如 xxl-job
    • 第二个就是自己写业务逻辑来控制定时任务,保证在分布式情况下,只有一个定时任务会正常执行业务逻辑,其他的都不执行业务逻辑。

    这里介绍的就是第二种方法,直接上代码

    定时任务调度器

     @Scheduled(cron = "59 59 23 * * ?")
        public void sc12() {
            dataHupService.pushData2RushLibrary("pushData2RushLibrary");
        }
    

    定时任务逻辑部分

    /**
         * 推送大保活动数据到一网通办办证库
         *
         * @param keyPre redis key前缀
         */
        public void pushData2RushLibrary(String keyPre) {
            String runKey = DeveloperSetting.REDIS_KEY_PRO + keyPre;
            boolean run = true;
            try {
                log.info(" [办证库] 推送大保活动数据到一网通办办证库 ");
                Boolean hasRun = redisTemplate.opsForValue().setIfAbsent(runKey, true, 3, TimeUnit.MINUTES);
                //保证只有一个现成在运行
                if (hasRun != null && hasRun) {
                    do {
                        try {
                            //获取访问token
                            Success<String> success = checkModuleFeignService.giveMeToken(thirdService);
                            HttpHeaders requestHeaders = new HttpHeaders();
                            requestHeaders.add(INSIDE_TOKEN_HEADER, success.getObj());
                            requestHeaders.setContentType(MediaType.APPLICATION_JSON);
                            HttpEntity<Object> request = new HttpEntity<>(null, requestHeaders);
                            ResponseEntity<Success> response = restTemplate.exchange("http://dbsys-admin-check-place-module/activity/pushData2RushLibrary", HttpMethod.POST, request, Success.class);
                            if (Objects.requireNonNull(response.getBody()).getCode() == 0) {
                                run = false;
                                log.info(" [办证库] 推送大保活动数据到一网通办办证库 成功 ");
                            }
                        } catch (Exception e) {
                            redisTemplate.expire(runKey, 3, TimeUnit.MINUTES);
                            e.printStackTrace();
                            Thread.sleep(30000);
                        }
                    } while (run);
                } else {
                    log.info(" [办证库] 推送大保活动数据到一网通办办证库  lock fail: {} ", "锁未竞争到");
                }
            } catch (Exception e) {
                e.printStackTrace();
                log.info(" [办证库] 推送大保活动数据到一网通办办证库 exception {}", e.getLocalizedMessage());
                redisTemplate.delete(runKey);
            }
        }
    

    这里借助于redis,在定时任务开始后,在redis尝试设置一个值,设置成功,允许执行业务代码,设置失败,逻辑结束,简单地说就是竞争执行锁。这样确保定时任务始终只能有一个线程能在执行。抢占到锁的线程执行对应的业务逻辑,估算好该任务的执行总时长,将锁的时间设置的比任务执行的时间更长,以防止任务还在执行,锁没了,被其他线程抢占了资源。业务执行出现异常,可以先捕捉,让线程睡眠一段时间,期间不放锁,稍后继续重新尝试执行业务。

    相关文章

      网友评论

          本文标题:多节点下定时任务单个线程执行

          本文链接:https://www.haomeiwen.com/subject/ozfzvktx.html