一般springboot用的定时任务都是@Scheduled(cron = ""),这个方法固然可以实现定时任务,但是有一种局限,就是不能做统一管理配置定时任务,而且调试的时候如果修改时间,还需要重新启动服务,我在做的时候我就觉得很繁琐,所以我去网上找了很多,终于找到了几种方法,结合了一下,实现了在nacos上动态配置定时任务的方法。
syncTime: 0 0 1 * * ?
只需在nacos上配置这一句,定时任务就可以触发。
这种方法最大的好处就是不用修改代码,如果用户要求调整定时任务,也可以直接去nacos上修改,不需要在代码上修改,然后重新发包。
以下是实现的逻辑及代码(只适合用nacos配置的):
思路:
- 实现SchedulingConfigurer方法,重写configureTasks
-
执行scheduledTaskRegistrar.addTriggerTask();
-
在addTriggerTask()方法里实现自己的业务逻辑(调用业务方法)
() -> {
try {
syncUserList();
log.debug("定时器触发成功!");
} catch (Exception e) {
throw new RuntimeException("接口调用失败");
}
}
- 在addTriggerTask()实现触发器
//实现触发器逻辑
triggerContext -> {
//从nacos中取动态的cron表达式
String cron = getSyncUserCron();//获取配置自己实现nacos方法
//判断nacos是否有配置cron表达式,如果没有给一个默认配置
if (StringUtils.isEmpty(cron)) {
cron = "0 0 1 * * *"; //默认配置 比如每1小时执行一次
}
//这边获取动态配置后执行 计算下次促发时间
CronTrigger trigger = new CronTrigger(cron);
return trigger.nextExecutionTime(triggerContext);
}
- 获取nacos上的配置文件信息,获取到时候把nacos转成对象,通过对象获取定时任务的表达式
public String getSyncUserCron() {
Properties properties = new Properties();
// 此时ConfigService对象就知道能从哪里获取配置
properties.put("serverAddr", nacosHost + ":" + nacosPort);
properties.put("namespace", nacosNs);
ConfigService configService;
String syncTime;
try {
configService = NacosFactory.createConfigService(properties);
// 这一步其实就是获取配置 参数:String dataId,String group,long timeoutMs超时时间
// 获取配置的时候我们指定dataId与group之后,ConfigService就能获取到信息的配置内容
String dataId = name + "-" + active + ".yaml";
String contont = configService.getConfig(dataId, nacosGrup, 5000);
Yaml yaml = new Yaml();
Object load = yaml.load(contont);
ObjectMapper objectMapper = new ObjectMapper();
try {
syncTime = JSON.parseObject(objectMapper.writeValueAsString(load), SyncUser.class).getSyncTime();
} catch (JsonProcessingException e) {
throw new RuntimeException("获取nacos数据失败!");
}
} catch (NacosException e) {
throw new RuntimeException(e);
}
return syncTime;
}
以下是完整代码
@Component
public class SyncUserTask implements SchedulingConfigurer {
@Value("${NACOS_NS}")
private String nacosNs; //命名空间
@Value("${NACOS_HOST}")
private String nacosHost; //IP
@Value("${NACOS_PORT}")
private String nacosPort; //端口
@Value("${spring.application.name}")
private String name; //Data Id 的前缀 如admin-dev中的admin
@Value("${spring.profiles.active}")
private String active; //Data Id 的后缀 dev
@Value("${NACOS_GROUP}")
private String nacosGrup; //Group
public void syncUserList() throws Exception {
//业务逻辑
}
@Override
public void configureTasks(ScheduledTaskRegistrar scheduledTaskRegistrar) {
//执行任务 使用addTriggerTask 方法
scheduledTaskRegistrar.addTriggerTask(
//第一个方法 实现自己的调度任务业务逻辑
() -> {
try {
syncUserList();
log.debug("定时器触发成功!");
} catch (Exception e) {
throw new RuntimeException("接口调用失败");
}
}, //业务逻辑
//实现触发器逻辑
triggerContext -> {
//从nacos中取动态的cron表达式
String cron = getSyncUserCron();//获取配置自己实现nacos方法
//判断nacos是否有配置cron表达式,如果没有给一个默认配置
if (StringUtils.isEmpty(cron)) {
cron = "0 0 1 * * *"; //默认配置 比如每1小时执行一次
}
//这边获取动态配置后执行 计算下次促发时间
CronTrigger trigger = new CronTrigger(cron);
return trigger.nextExecutionTime(triggerContext);
});
}
public String getSyncUserCron() {
Properties properties = new Properties();
// 此时ConfigService对象就知道能从哪里获取配置
properties.put("serverAddr", nacosHost + ":" + nacosPort);
properties.put("namespace", nacosNs);
ConfigService configService;
String syncTime;
try {
configService = NacosFactory.createConfigService(properties);
// 这一步其实就是获取配置 参数:String dataId,String group,long timeoutMs超时时间
// 获取配置的时候我们指定dataId与group之后,ConfigService就能获取到信息的配置内容
String dataId = name + "-" + active + ".yaml";
String contont = configService.getConfig(dataId, nacosGrup, 5000);
Yaml yaml = new Yaml();
Object load = yaml.load(contont);
ObjectMapper objectMapper = new ObjectMapper();
try {
syncTime = JSON.parseObject(objectMapper.writeValueAsString(load), SyncUser.class).getSyncTime();
} catch (JsonProcessingException e) {
throw new RuntimeException("获取nacos数据失败!");
}
} catch (NacosException e) {
throw new RuntimeException(e);
}
return syncTime;
}
}
注意:
- 一定要记得在启动类加上@EnableScheduling
- @Value的值都是从bootstrap.yml上获取的