![](/img/trans.png)
[英]Execute two schedulers simultaneously with spring-boot @scheduled annotation
[英]Modify scheduler timing dynamically based on the condition used with spring-boot @Scheduled annotation
我有一個調度程序,它以 5 秒的固定延遲觸發。
我計划擁有多個調度程序,但現在,讓我們只使用一個調度程序。
需求:根據業務條件調度器的fixedDelay應該改變。
**例如,**默認的fixedDelay是5secs ,但根據條件可以是6, 8, 10secs 。
因此,為了實現這一點,我正在嘗試修改fixedDelay 。 但這對我不起作用。
代碼:
接口,帶有延遲方法。
public abstract class DynamicSchedule{
/**
* Delays scheduler
* @param milliseconds - the time to delay scheduler.
*/
abstract void delay(Long milliseconds);
/**
* Decreases delay period
* @param milliseconds - the time to decrease delay period.
*/
abstract void decreaseDelayInterval(Long milliseconds);
/**
* Increases delay period
* @param milliseconds - the time to increase dela period
*/
abstract void increaseDelayInterval(Long milliseconds);
}
實現位於 spring-context 項目中 org.springframework.scheduling 的 Trigger 接口。
import org.springframework.scheduling.TaskScheduler;
import org.springframework.scheduling.Trigger;
import org.springframework.scheduling.TriggerContext;
import java.util.Date;
import java.util.concurrent.ScheduledFuture;
public class CustomDynamicSchedule extends DynamicSchedule implements Trigger {
private TaskScheduler taskScheduler;
private ScheduledFuture<?> schedulerFuture;
/**
* milliseconds
*/
private long delayInterval;
public CustomDynamicSchedule(TaskScheduler taskScheduler) {
this.taskScheduler = taskScheduler;
}
@Override
public void increaseDelayInterval(Long delay) {
if (schedulerFuture != null) {
schedulerFuture.cancel(true);
}
this.delayInterval += delay;
schedulerFuture = taskScheduler.schedule(() -> { }, this);
}
@Override
public void decreaseDelayInterval(Long delay) {
if (schedulerFuture != null) {
schedulerFuture.cancel(true);
}
this.delayInterval += delay;
schedulerFuture = taskScheduler.schedule(() -> { }, this);
}
@Override
public void delay(Long delay) {
if (schedulerFuture != null) {
schedulerFuture.cancel(true);
}
this.delayInterval = delay;
schedulerFuture = taskScheduler.schedule(() -> { }, this);
}
@Override
public Date nextExecutionTime(TriggerContext triggerContext) {
Date lastTime = triggerContext.lastActualExecutionTime();
return (lastTime == null) ? new Date() : new Date(lastTime.getTime() + delayInterval);
}
}
配置:
@Configuration
public class DynamicSchedulerConfig {
@Bean
public CustomDynamicSchedule getDinamicScheduler() {
ThreadPoolTaskScheduler threadPoolTaskScheduler = new ThreadPoolTaskScheduler();
threadPoolTaskScheduler.initialize();
return new CustomDynamicSchedule(threadPoolTaskScheduler);
}
}
測試類,測試使用情況。
@EnableScheduling
@Component
public class TestSchedulerComponent {
@Autowired
private CustomDynamicSchedule dynamicSchedule;
@Scheduled(fixedDelay = 5000)
public void testMethod() {
dynamicSchedule.delay(1000l);
dynamicSchedule.increaseDelayInterval(9000l);
dynamicSchedule.decreaseDelayInterval(5000l);
}
}
我得到了https://stackoverflow.com/a/51333059/4770397 的幫助,
但不幸的是,這段代碼對我不起作用。
調度程序在fixedDelay運行,沒有變化。
請幫忙..
使用@Scheduled
將只允許使用靜態計划。 您可以使用屬性使計划可配置,如下所示
@Scheduled(cron = "${yourConfiguration.cronExpression}")
// or
@Scheduled(fixedDelayString = "${yourConfiguration.fixedDelay}")
但是,一旦您的 spring 上下文初始化(應用程序啟動),結果計划將被修復。
要對預定執行進行細粒度控制,您需要實現一個自定義Trigger
- 類似於您已經做過的。 與要執行的任務一起,可以通過在@Configuration
類中使用ScheduledTaskRegistrar.addTriggerTask
實現SchedulingConfigurer
來注冊此觸發器:
@Configuration
@EnableScheduling
public class AppConfig implements SchedulingConfigurer {
@Override
public void configureTasks(ScheduledTaskRegistrar taskRegistrar) {
taskRegistrar.setScheduler(taskScheduler());
taskRegistrar.addTriggerTask(() -> myTask().work(), myTrigger());
}
@Bean(destroyMethod="shutdown")
public Executor taskScheduler() {
return Executors.newScheduledThreadPool(42);
}
@Bean
public CustomDynamicSchedule myTrigger() {
new CustomDynamicSchedule();
}
@Bean
public MyTask myTask() {
return new MyTask();
}
}
但是不要在CustomDynamicSchedule
注冊任何任務,只需使用它來計算下一次執行時間:
public class CustomDynamicSchedule extends DynamicSchedule implements Trigger {
private long delayInterval;
@Override
public synchronized void increaseDelayInterval(Long delay) {
this.delayInterval += delay;
}
@Override
public synchronized void decreaseDelayInterval(Long delay) {
this.delayInterval += delay;
}
@Override
public synchronized void delay(Long delay) {
this.delayInterval = delay;
}
@Override
public Date nextExecutionTime(TriggerContext triggerContext) {
Date lastTime = triggerContext.lastActualExecutionTime();
return (lastTime == null) ? new Date() : new Date(lastTime.getTime() + delayInterval);
}
}
但請記住使CustomDynamicSchedule
線程安全,因為它會被 spring 創建為單例,並且可能被多個線程並行訪問。
Spring 的@Scheduled
注釋不提供這種支持。
這是我使用基於隊列的解決方案的類似功能的首選實現,它允許時間的靈活性和此調度程序功能的非常健壯的實現。
cron
和一個單線程執行器服務,負責根據 cron 將消息發布到隊列。 task-cron
映射持久保存在database
,並在啟動期間進行初始化。 此外,我們公開了一個API
,用於在運行時更新任務的 cron。我們只需關閉舊的計划執行器服務,並在我們通過 API 觸發 cron 更改時創建一個。 此外,我們在數據庫中更新相同。
這種方法有多種優點。 這將調度程序與調度管理任務分離。 現在,調度程序可以單獨關注業務邏輯。 此外,我們可以根據需要編寫任意數量的調度程序,所有調度程序都偵聽相同的隊列並相應地執行操作。
使用注釋,您只能通過找到一個公分母並對其進行輪詢來進行近似。 我稍后會告訴你。 如果你想要一個真正的動態解決方案,你不能使用注解,但你可以使用編程配置。 該解決方案的優點是您甚至可以在運行時更改執行周期! 這是一個關於如何做到這一點的例子:
public initializeDynamicScheduledTAsk (ThreadPoolTaskScheduler scheduler,Date start,long executionPeriod) {
scheduler.schedule(
new ScheduledTask(),
new Date(startTime),period
);
}
class ScheduledTask implements Runnable{
@Override
public void run() {
// my scheduled logic here
}
}
有一種方法可以欺騙並實際使用注釋做一些事情。 但只有在精度不重要的情況下才能這樣做。 精度是什么意思。 如果您知道要每 5 秒啟動一次,但 100 毫秒或多或少並不重要。 如果您知道需要每 5-6-8 秒或 10 秒啟動一次,您可以配置一個每秒執行一次的作業,並在一個 if 語句中檢查自上次執行以來已經過去了多長時間。 它非常蹩腳,但它可以工作:) 只要您不需要高達毫秒的精度。 下面是一個例子:
public class SemiDynamicScheduledService {
private Long lastExecution;
@Value(#{yourDynamicConfiguration})
private int executeEveryInMS
@Scheduled(fixedDelay=1000)
public semiDynamicScheduledMethod() {
if (System.currentTimeMS() - lastExecution>executeEveryInMS) {
lastExecution = System.currentTimeMS();
// put your processing logic here
}
}
}
我有點蹩腳,但會在簡單的情況下完成這項工作。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.