@RefreshScope 停止 @Scheduled 任务 [英] @RefreshScope stops @Scheduled task
问题描述
我有一个监控应用程序,我正在其中运行一个 fixedRate 任务.这是拉入使用 Consul 配置的配置参数.我想引入更新的配置,所以我添加了@RefreshScope.但是一旦我更新了 Consul 上的配置值,fixedRate 任务就会停止运行.
I have a monitoring app wherein I am running a fixedRate task. This is pulling in a config parameter configured with Consul. I want to pull in updated configuration, so I added @RefreshScope. But as soon as I update the config value on Consul, the fixedRate task stops running.
@Service
@RefreshScope
public class MonitorService {
@Autowired
private AppConfig appConfig;
@PostConstruct
public void postConstRun() {
System.out.println(appConfig.getMonitorConfig());
}
@Scheduled(fixedRate = 1000)
public void scheduledMonitorScan() {
System.out.println("MonitorConfig:" + appConfig.getMonitorConfig());
}
}
AppConfig 类只有一个 String 参数:
AppConfig class just has a single String parameter:
@Configuration
@Getter
@Setter
public class AppConfig {
@Value("${monitor-config:default value}")
private String monitorConfig;
}
一旦我更新了 consul 中的值,计划任务就会停止运行(显示在 sheduledMonitorScan 方法中)停止显示.
As soon as I update the value in consul, the scheduled task just stops running (display in sheduledMonitorScan method) stop showing up.
推荐答案
以下是我们解决此问题的方法.
Here's how we've solved this issue.
/**
* Listener of Spring's lifecycle to revive Scheduler beans, when spring's
* scope is refreshed.
* <p>
* Spring is able to restart beans, when we change their properties. Such a
* beans marked with RefreshScope annotation. To make it work, spring creates
* <b>lazy</b> proxies and push them instead of real object. The issue with
* scope refresh is that right after refresh in order for such a lazy proxy
* to be actually instantiated again someone has to call for any method of it.
* <p>
* It creates a tricky case with Schedulers, because there is no bean, which
* directly call anything on any Scheduler. Scheduler lifecycle is to start
* few threads upon instantiation and schedule tasks. No other bean needs
* anything from them.
* <p>
* To overcome this, we had to create artificial method on Schedulers and call
* them, when there is a scope refresh event. This actually instantiates.
*/
@RequiredArgsConstructor
public class RefreshScopeListener implements ApplicationListener<RefreshScopeRefreshedEvent> {
private final List<RefreshScheduler> refreshSchedulers;
@Override
public void onApplicationEvent(RefreshScopeRefreshedEvent event) {
refreshSchedulers.forEach(RefreshScheduler::materializeAfterRefresh);
}
}
所以,我们定义了一个接口,它没有做任何特别的事情,但允许我们调用一个刷新的作业.
So, we've defined an interface, which does nothing in particular, but allows us to call for a refreshed job.
public interface RefreshScheduler {
/**
* Used after refresh context for scheduler bean initialization
*/
default void materializeAfterRefresh() {
}
}
这里是实际作业,其参数from.properties
可以刷新.
And here is actual job, whose parameter from.properties
can be refreshed.
public class AJob implements RefreshScheduler {
@Scheduled(cron = "${from.properties}")
public void aTask() {
// do something useful
}
}
更新:当然AJob bean必须在@Configuration
UPDATED: Of course AJob bean must be marked with @RefreshScope in @Configuration
@Configuration
@EnableScheduling
public class SchedulingConfiguration {
@Bean
@RefreshScope
public AJob aJob() {
return new AJob();
}
}
这篇关于@RefreshScope 停止 @Scheduled 任务的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!