@RefreshScope stops @Scheduled task
spring refresh bean periodically
configurationproperties vs refreshscope
dynamic cron expression from database in spring scheduler
spring scheduler run once
spring boot dynamic properties
spring change property value at runtime
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 class just has a single String parameter:
@Configuration @Getter @Setter public class AppConfig { @Value("${monitor-config:default value}") private String monitorConfig; }
As soon as I update the value in consul, the scheduled task just stops running (display in sheduledMonitorScan method) stop showing up.
I have done workaround for this kind of scenario by implementing SchedulingConfigurer interface. Here I am dynamically updating "scheduler.interval" property from external property file and scheduler is working fine even after actuator refresh as I am not using @RefreshScope anymore. Hope this might help you in your case also.
public class MySchedulerImpl implements SchedulingConfigurer { @Autowired private Environment env; @Bean(destroyMethod = "shutdown") public Executor taskExecutor() { return Executors.newScheduledThreadPool(10); } @Override public void configureTasks(final ScheduledTaskRegistrar taskRegistrar) { taskRegistrar.setScheduler(this.taskExecutor()); taskRegistrar.addTriggerTask(() -> { //put your code here that to be scheduled }, triggerContext -> { final Calendar nextExecutionTime = new GregorianCalendar(); final Date lastActualExecutionTime = triggerContext.lastActualExecutionTime(); if (lastActualExecutionTime == null) { nextExecutionTime.setTime(new Date()); } else { nextExecutionTime.setTime(lastActualExecutionTime); nextExecutionTime.add(Calendar.MILLISECOND, env.getProperty("scheduler.interval", Integer.class)); } return nextExecutionTime.getTime(); }); } }
@RefreshScope and @Scheduled · Issue #567 · spring-cloud/spring , When I use the @RefreshScope annotation to dynamically refresh the internal private properties of the class, if the @scheduled annotation is used in this class or the SchedulingConfigurer interface is used to define the scheduled task, the scheduled task will be invalidated while the property is refreshed. Joy and pain with @Scheduled and @RefreshScope in SpringBoot | TLDR; @Scheduled and @RefreshScope are powerful tools but do not work out of the box together causing dangerous inconsistencies. Find out how to get them to play nicely and more advanced scheduling opporunities.
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() { } }
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 } }
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(); } }
If @RefreshScope is added, @Scheduled does not work any more , BUT, the moment I then post to the /refresh endpoint, the schedule task dies/goes on vacation/something (i.e console output stops dead). Current Spring provides excellent support for both task scheduling and asynchronous method execution based on cron expression using @Scheduled annotation. The @Scheduled annotation can be added to a method along with trigger metadata. In this post, I will show the means to use @Scheduled feature in 4 different ways. Read More : Spring timer tasks Table …
My solution consists of listening to EnvironmentChangeEvent
@Configuration public class SchedulingSpringConfig implements ApplicationListener<EnvironmentChangeEvent>, SchedulingConfigurer { private static final Logger LOGGER = LoggerFactory.getLogger(SchedulingSpringConfig.class); private final DemoProperties demoProperties; public SchedulingSpringConfig(DemoProperties demoProperties) { this.demoProperties = demoProperties; } @Override public void configureTasks(ScheduledTaskRegistrar taskRegistrar) { LOGGER.info("Configuring scheduled task with cron expression: {}", demoProperties.getCronExpression()); taskRegistrar.addTriggerTask(triggerTask()); taskRegistrar.setTaskScheduler(taskScheduler()); } @Bean public TriggerTask triggerTask() { return new TriggerTask(this::work, cronTrigger()); } private void work() { LOGGER.info("Doing work!"); } @Bean @RefreshScope public CronTrigger cronTrigger() { return new CronTrigger(demoProperties.getCronExpression()); } @Bean public ThreadPoolTaskScheduler taskScheduler() { return new ThreadPoolTaskScheduler(); } @Override public void onApplicationEvent(EnvironmentChangeEvent event) { if (event.getKeys().contains("demo.config.cronExpression")) { ScheduledTasksRefresher scheduledTasksRefresher = new ScheduledTasksRefresher(triggerTask()); scheduledTasksRefresher.afterPropertiesSet(); } } }
Then I use the ContextLifecycleScheduledTaskRegistrar
to recreate the task.
public class ScheduledTasksRefresher extends ContextLifecycleScheduledTaskRegistrar { private final TriggerTask triggerTask; ScheduledTasksRefresher(TriggerTask triggerTask) { this.triggerTask = triggerTask; } @Override public void afterPropertiesSet() { super.destroy(); super.addTriggerTask(triggerTask); super.afterSingletonsInstantiated(); } }
Properties definition:
@ConfigurationProperties(prefix = "demo.config", ignoreUnknownFields = false) public class DemoProperties { private String cronExpression; public String getCronExpression() { return cronExpression; } public void setCronExpression(String cronExpression) { this.cronExpression = cronExpression; } }
Main definition:
@SpringBootApplication @EnableConfigurationProperties(DemoProperties.class) @EnableScheduling public class DemoApplication { public static void main(String[] args) { SpringApplication.run(DemoApplication.class, args); } }
Joy and pain with @Scheduled and @RefreshScope in SpringBoot, | TLDR; @Scheduled and @RefreshScope are powerful tools but do not work out of the box together causing dangerous inconsistencies. Find out Enables Spring's scheduled task execution capability, similar to functionality found in Spring's <task:*> XML namespace. To be used on @ Configuration classes as follows: @Configuration @EnableScheduling public class AppConfig { // various @Bean definitions } This enables detection of @ Scheduled annotations on any Spring-managed bean in
@RefreshScope and @Scheduled, When I use the @RefreshScope annotation to dynamically refresh the internal private properties of the class, if the @scheduled annotation is used in this class or the SchedulingConfigurer interface is used to define the scheduled task, the scheduled task will be invalidated while the property is refreshed. Hello. I have a simple Spring Boot application in which I just included the spring-boot-admin-starter-client (version 1.5.2). Everything works fine except for the scheduled method detailed below: @
Spring Cloud, A Spring @Bean that is marked as @RefreshScope will get special treatment when /pause and /resume for calling the Lifecycle methods ( stop() and start() on absolute paths in Windows require an extra "/", e.g. file:///${user.home}/config-repo Don't use the EurekaClient in @PostConstruct method or in a @Scheduled With above conditional flag, we're able to trigger the scheduled Spring Batch job with the scheduled task alive. If we don't need to resume the job, then we can actually stop the scheduled task to save resources. Let's take a look at two options in the next two subsections. 3.1. Using Scheduler Post Processor
1. Spring Cloud Context: Application Context Services, configuration files and has endpoints for common management and monitoring tasks. changes (although you could set it up with a @Scheduled annotation). Refresh scope beans are lazy proxies that initialize when they are used (that is, /actuator/pause and /actuator/resume for calling the Lifecycle methods ( stop() Running background scheduled tasks in Spring. the task will print out the current time Console and it will stop fixedDelay second after the task is finished to
Comments
- Have you tried separating them?
- Separating what? Do you mean move RefreshScope out of MonitorService class?
- You just need the
@RefreshScope
inAppConfig
, and that will refresh the injected bean in your service. - I tried adding @ RefreshScope in AppConfig. Reading Sprind documentation I found that the public refresh method in the annotation is exposed in the /refresh endpoint. So I added spring actuator dependency. This now refreshes my configuration, but my @ Scheduled stops working when I add the spring actuator.