使用Nacos轻松实现分布式单点定时任务(下)

1,674 阅读2分钟

本文已参与「新人创作礼」活动,一起开启掘金创作之路。

2. 定义定时任务

定时任务基于SpingBoot的Schedule实现

运行规则

  • 没有运行过则判断是否在当前节点运行
  • 已经运行过则直接运行
@Configuration
@Slf4j
//开启定时任务,通常写在项目启动类上
@EnableScheduling
public class AutoSubmitTask {

    @Autowired
    private DistributedTask distributedTask;

    /**
    * Dubbo远端服务,真实业务逻辑运行服务
    */
    @DubboReference(group = "nx")
    private ILogicService iLogicService;

    //标记当前是否正在运行
    private boolean currentRun=false;

    //调度规则
    @Scheduled(fixedRate = 1000)
    private void configureTasks() {
        //判断当前是否已经在本机运行job
        if(currentRun){
            //有则运行
            run();
        }else{
            //没有则判断是否需要运行,具体实现看集群容错算法章节
            if (distributedTask.isRunInCurrent()) {
                currentRun=true;
                run();
            }
        }
    }

    private void run(){
        log.info("执行PaperSubmitTask定时任务: " + LocalDateTime.now());
        iLogicService.autoSubmit();
    }
}

3. 集群实例选择算法

算法对于应用中多个定时任务都是通用的,所以在DistributedTask实现获取集群实例的方法。

public class DistributedTask implements ApplicationContextAware {
    /**
    * 是否在当前应用中运行
    */
		public boolean isRunInCurrent(){
        try {
            //通过简单的算法得出应该运行的实例
            Instance instance = findInstance();
            //当前环境对于应该运行的实例
            if (instance!=null && instance.getIp().equals(NetUtils.getLocalHost()) && instance.getPort()==serverPort) {
                return true;
            }
        } catch (Throwable e) {
            e.printStackTrace();
        }
        return false;
    }
    
    /**
    * 获取定时任务所在实例
    */
    private Instance findInstance(){
        Instance instance=null;
        try {
            //通过api获取定时任务服务的所有运行实例
            List<Instance> allInstances = naming.getAllInstances(SERVICENAME);
            if (CollectionUtils.isNotEmpty(allInstances)) {
                //通过运行时间戳排序,获取所有实例中最先运行的实例为真实运行实例
                allInstances.sort(Comparator.comparing((Instance a) -> a.getMetadata().get("timestamp")));
                instance=allInstances.get(0);
                log.debug("distributed task run in host:{},port:{}",instance.getIp(),instance.getPort());
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
        return instance;
    }
}

4. 总结

运行过程中,第一个运行的实例会执行定时任务,如果宕机了则由后面运行的实例执行。充分利用到了注册中心服务监听上下线功能,做到分布式不间断定时任务。当然我们基于此也可以实现任务切片,故障转移等。