spring - @RefreshScope 停止 @Scheduled 任务

标签 spring spring-cloud spring-cloud-consul

我有一个监控应用程序,其中我正在运行一个 fixedRate 任务。这是拉入使用 Consul 配置的配置参数。我想引入更新的配置,所以我添加了@RefreshScope。但是一旦我更新 Consul 上的配置值,fixedRate 任务就会停止运行。

@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 参数:
@Configuration
@Getter
@Setter
public class AppConfig {

    @Value("${monitor-config:default value}")
    private String monitorConfig;
}

一旦我更新了 consul 中的值,计划任务就会停止运行(在 sheduledMonitorScan 方法中显示)停止显示。

最佳答案

以下是我们解决此问题的方法。

/**
 * 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);
    }
}

所以,我们定义了一个接口(interface),它什么都不做,但允许我们调用一个刷新的工作。
public interface RefreshScheduler {
    /**
     * Used after refresh context for scheduler bean initialization
     */
    default void materializeAfterRefresh() {
    }
}

这是实际工作,其参数from.properties可以刷新。
public class AJob implements RefreshScheduler {
    @Scheduled(cron = "${from.properties}")
    public void aTask() {
        // do something useful
    }
}

更新:
当然 AJob bean 必须在@Configuration 中用@RefreshScope 标记
@Configuration
@EnableScheduling
public class SchedulingConfiguration {
    @Bean
    @RefreshScope
    public AJob aJob() {
        return new AJob();
    }
}

关于spring - @RefreshScope 停止 @Scheduled 任务,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/50440468/

相关文章:

java - 具有复合模式的 Spring 中的循环引用

spring-cloud - 如何为生产者设置路由键

java - 了解 Spring Boot 2 的默认 logback 配置

java - 如何在Spring Boot中对同级包进行组件扫描

Spring 注释配置未启动

java - 将 spring-cloud 与 netflix Eureka 一起使用时,如何让 Discovery Client 工作?

java - 使用 Keycloak 保护 Spring Cloud 网关

kubernetes - Spring Cloud Consul-服务未在K8s上注册可联系地址

php - 使用 consul 进行跨平台服务发现