项目中使用携程Apollo进行配置管理,需要实现动态配置Job的执行间隔。
使用动态修改cron表达式的方式可以实现,但是cron表达式不支持毫秒,所以采用以下方式进行配置。
每次配置改动时,将旧的定时任务停掉,重新启动一个定时任务。
/**
* shang
* 2020-02-11 15:41
*/
@Slf4j
@Component
public class OrderJob {
// 订单宽表定时同步间隔,单位ms
@Value("${order.schedule.interval:1000}")
private Long orderScheduleInterVal;
private static final String lock = "lock";
private final Set<String> orderIdSet = Sets.newHashSet();
@Resource
private OrderManager orderManager;
@Resource
private ThreadPoolTaskScheduler threadPoolTaskScheduler;
@ApolloConfig
private Config config;
private ScheduledFuture<?> future;
@ApolloConfigChangeListener
private void configChangeListener(ConfigChangeEvent changeEvent) {
Set<String> keyNames = config.getPropertyNames();
for (String key : keyNames) {
if (StringUtils.equals(key, "order.schedule.interval")) {
String interVal = config.getProperty(key, "1000");
if (future != null) {
future.cancel(true);
log.info("cancel order task");
}
PeriodicTrigger periodicTrigger = new PeriodicTrigger(Long.valueOf(interVal), TimeUnit.MILLISECONDS);
periodicTrigger.setFixedRate(true);
periodicTrigger.setInitialDelay(1000);
future = threadPoolTaskScheduler.schedule(this::readOrderIdSet, periodicTrigger);
log.info("start order task");
}
log.info("{}:{}", key, config.getProperty(key, null));
}
}
/**
* 写入缓冲set
*
* @param orderIds
*/
public void writeOrderIdSet(Set<String> orderIds) {
if (CollectionUtils.isEmpty(orderIds)) {
return;
}
synchronized (lock) {
orderIdSet.addAll(orderIds);
}
}
/**
* 定时任务从缓冲set里取
*/
// 这样配置无法动态修改执行间隔
// @Scheduled(fixedRate = 2000)
public void readOrderIdSet() {
if (CollectionUtils.isEmpty(orderIdSet)) {
log.info("缓冲set为空,定时任务不执行");
return;
}
synchronized (lock) {
log.info("发送订单宽表消息:{}条", CollectionUtils.size(orderIdSet));
// 缓冲区的orderId发kafka消息
orderManager.sendMessage(orderIdSet);
// 清空缓冲区
orderIdSet.clear();
}
}
@PostConstruct
public void postConstruct() {
PeriodicTrigger periodicTrigger = new PeriodicTrigger(orderScheduleInterVal, TimeUnit.MILLISECONDS);
periodicTrigger.setFixedRate(true);
periodicTrigger.setInitialDelay(1000);
future = threadPoolTaskScheduler.schedule(this::readOrderIdSet, periodicTrigger);
}
}