web-dev-qa-db-ja.com

spring-boot @Scheduledアノテーションで使用される条件に基づいてスケジューラのタイミングを動的に変更します

5秒の固定遅延でトリガーするスケジューラがあります。
複数のスケジューラを計画していますが、今のところ、1つのスケジューラのみに固執しましょう。

要件:ビジネス条件スケジューラーのfixedDelayに基づいて変更する必要があります。
**例、**デフォルトfixedDelay5secsですが、6、8、10秒、条件に基づく。

これを実現するために、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で実行されているため、変更はありません。

助けてください..

9
mayank bisht

@Scheduledを使用すると、静的なスケジュールのみを使用できます。プロパティを使用して、このようにスケジュールを構成可能にすることができます

@Scheduled(cron = "${yourConfiguration.cronExpression}")

// or

@Scheduled(fixedDelayString = "${yourConfiguration.fixedDelay}")

ただし、春のコンテキストが初期化されると(アプリケーションが開始されると)、結果のスケジュールは修正されます。

スケジュールされた実行をきめ細かく制御するには、カスタム 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スレッドは、スプリングによってシングルトンとして作成され、複数のスレッドによって並行してアクセスされる可能性があるため、スレッドセーフにすることを忘れないでください。

9
dpr

注釈を使用すると、一般的な分母を見つけてそれをポーリングすることにより、近似によってのみそれを行うことができます。後でお見せします。実際の動的ソリューションが必要な場合は、アノテーションは使用できませんが、プログラムによる構成を使用できます。 このソリューションの良い点は、実行時でも実行期間を変更できることです!これを行う方法の例を次に示します。

  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秒ごとに開始することがわかっているが、100ms程度は重要ではない場合。 5-6-8または10秒ごとに開始する必要があることがわかっている場合は、毎秒実行される1つのジョブを構成し、1つの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
   }
}

}

私は少し下手ですが、単純なケースでは仕事をします。

0

春の@Scheduledアノテーションはこのサポートを提供しません。

これは、タイミングの柔軟性とこのスケジューラ機能の非常に堅牢な実装を可能にするキューベースのソリューションを使用した、同様の機能の私の好ましい実装です。

これはパイプラインです enter image description here

  1. Cron Maintainer and Publisher-すべてのタスクに、関連するcronと、メッセージのパブリッシュを担当するシングルスレッドのエグゼキューターサービスがありますクーロンごとにキューに。 task-cronマッピングはdatabaseに保持され、起動時に初期化されます。また、実行時にタスクのcronを更新するためにAPIが公開されています。

    APIを介してcronの変更をトリガーするたびに、古いスケジュール済みのexecutorサービスをシャットダウンし、作成します。また、データベース内でも同じように更新します。

  2. Queue-パブリッシャーによって発行されたメッセージを格納するために使用されます。
  3. Scheduler-これは、スケジューラのビジネスロジックが存在する場所です。 Queue(この場合はKafka)のリスナーは、着信メッセージをリッスンし、同じスレッドのメッセージを受信するたびに、新しいスレッドで対応するスケジューラタスクを呼び出します。

このアプローチにはさまざまな利点があります。これにより、スケジューラがスケジュール管理タスクから切り離されます。これで、スケジューラはビジネスロジックのみに集中できます。また、必要な数のスケジューラを記述して、すべて同じキューをリッスンし、それに応じて動作させることができます。

0
Mukul Bansal