I have a microservice that will receive a message, something like this:
["00:12", "12:20", "15:40"]
Are there any out-of-the-box solutions to allow methods to run at a given time each day? Using cron from Spring does not involve changing the time at runtime. I need to use a more flexible option (change in runtime, multiple launch starts)
Thanks!
CodePudding user response:
Spring has the @Scheduled
annotation. There a several ways to configure it, one way is to configure it like unix cron job.
For example like this:
@Scheduled(cron = "0 15 10 15 * ?")
public void scheduleTaskUsingCronExpression() {
long now = System.currentTimeMillis() / 1000;
System.out.println(
"schedule tasks using cron jobs - " now);
}
The task here is scheduled to be executed at 10:15 AM on the 15th day of every month.
But there a also ways to configure the delay or rate dynamically at Runtime, for example like this (taken from baeldung.com):
@Configuration
@EnableScheduling
public class DynamicSchedulingConfig implements SchedulingConfigurer {
@Autowired
private TickService tickService;
@Bean
public Executor taskExecutor() {
return Executors.newSingleThreadScheduledExecutor();
}
@Override
public void configureTasks(ScheduledTaskRegistrar taskRegistrar) {
taskRegistrar.setScheduler(taskExecutor());
taskRegistrar.addTriggerTask(
new Runnable() {
@Override
public void run() {
tickService.tick();
}
},
new Trigger() {
@Override
public Date nextExecutionTime(TriggerContext context) {
Optional<Date> lastCompletionTime =
Optional.ofNullable(context.lastCompletionTime());
Instant nextExecutionTime =
lastCompletionTime.orElseGet(Date::new).toInstant()
.plusMillis(tickService.getDelay());
return Date.from(nextExecutionTime);
}
}
);
}
}
The line Instant nextExecutionTime = ...
could be replaced with your own logic for setting the next execution time, like parsing the times from your array.
Take look at this baeldung tutorial about it: https://www.baeldung.com/spring-scheduled-tasks
Also take look here for the cron notation: https://www.netiq.com/documentation/cloud-manager-2-5/ncm-reference/data/bexyssf.html
CodePudding user response:
The best solution depends (as almost always) on the context in which your infrastructure will have to work.
You can use @Scheduled
(i.e. Spring Scheduled Task running in clustered environment ) but it does not perform well in clusters (there is usually only one node running jobs).
I am going to suggest a solution with a good balance between simplicity, efficiency and reliability. I will discuss some of its properties later.
Suggested solution:
- uses an ordered list with guaranteed atomicity (message queue, redis sorted list, db table, ...).
- a request from a customer, it will simply add an item to that list.
- you will raise as many workers as you need, they can be one, several or with auto-scaling as needed.
- each worker will check (atomically) if he has to process a job. NOTE: the optimal way is to have a hook against the list, so that they get a push when there are changes in the list, so that recurring reads on the list are not necessary (the workers simply wait for the nearest moment).
Noteworthy properties of this approach:
- there may be one or more nodes running jobs simultaneously.
- a node can become corrupted, take a long time, etc... without affecting the pending jobs (you can do autoscaling just by seeing that there are no idle workers).
- you can easily check if a job was lost (e.g. the node running it got corrupted), if it has been tried to run several times without success, etc...
- the status is persistent and you can check if jobs were missed, resume them, etc...
- depending on how efficient you need your solution to be, it can be very elaborate (hooks, auto-scaling state, etc...) or very simple (a simple
begin trans; select 1 id from jobs where time >= now and running = 0 order by time limit 1; ... update jobs set running = 1 where id = ?; ...; commit trans
).