本文已参与「新人创作礼」活动,一起开启掘金创作之路。
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. 总结
运行过程中,第一个运行的实例会执行定时任务,如果宕机了则由后面运行的实例执行。充分利用到了注册中心服务监听上下线功能,做到分布式不间断定时任务。当然我们基于此也可以实现任务切片,故障转移等。