1. 概要

シャットダウン時に、デフォルトでは、Springの TaskExecutor は実行中のすべてのタスクを中断するだけですが、代わりに実行中のすべてのタスクが完了するのを待つと便利な場合があります。 このは、各タスクがシャットダウンの安全を確保するための対策を講じる機会を提供します。

このクイックチュートリアルでは、スレッドプールを使用して実行されるタスクが含まれる場合に、Spring Bootアプリケーションをより適切にシャットダウンする方法を学習します。

2. 簡単な例

単純なSpring Bootアプリケーションについて考えてみましょう。 デフォルトのTaskExecutorbeanを自動配線します。

@Autowired
private TaskExecutor taskExecutor;

アプリケーションの起動時に、スレッドプールのスレッドを使用して1分間のプロセスを実行しましょう。

taskExecutor.execute(() -> {
    Thread.sleep(60_000);
});

shutdown が開始されると、たとえば、起動から20秒後に、この例のスレッドは中断され、アプリケーションはすぐにシャットダウンします。

3. タスクが完了するのを待つ

カスタムThreadPoolTaskExecutor beanを作成して、タスクエグゼキュータのデフォルトの動作を変更しましょう。

このクラスは、実行中のタスクの中断を防ぐためにフラグsetWaitForTasksToCompleteOnShutdownを提供します。 trueに設定しましょう。

@Bean
public TaskExecutor taskExecutor() {
    ThreadPoolTaskExecutor taskExecutor = new ThreadPoolTaskExecutor();
    taskExecutor.setCorePoolSize(2);
    taskExecutor.setMaxPoolSize(2);
    taskExecutor.setWaitForTasksToCompleteOnShutdown(true);
    taskExecutor.initialize();
    return taskExecutor;
}

また、以前のロジックを書き直して、それぞれ1分間のタスクを実行する3つのスレッドを作成します。

@PostConstruct
public void runTaskOnStartup() {
    for (int i = 0; i < 3; i++) {
        taskExecutor.execute(() -> {
            Thread.sleep(60_000);
        });
    }
}

起動後最初の60秒以内にシャットダウンを開始しましょう。

アプリケーションは起動後わずか120秒でシャットダウンすることがわかります。 プールサイズが2の場合、同時に実行できるタスクは2つだけなので、3番目のタスクはキューに入れられます。

フラグを設定すると、現在実行中のタスクとキューに入れられたタスクの両方が完了します。

シャットダウン要求を受信すると、タスクエグゼキュータはキューを閉じ 、新しいタスクを追加できないことに注意してください。

4. 終了までの最大待機時間

進行中のキューに入れられたタスクが完了するのを待つように構成しましたが、Springは残りのコンテナーのシャットダウンを続行します。 これにより、タスクエグゼキュータが必要とするリソースが解放され、タスクが失敗する可能性があります。

コンテナの残りの部分のシャットダウンをブロックするために、 ThreadPoolTaskExecutorで最大待機時間を指定できます:

taskExecutor.setAwaitTerminationSeconds(30);

これにより、指定された期間、コンテナレベルでのシャットダウンプロセスがブロックされます

setWaitForTasksToCompleteOnShutdownフラグをtrueに設定する場合、キュー内の残りのすべてのタスクも実行されるように、大幅に高いタイムアウトを指定する必要があります。

5. 結論

このクイックチュートリアルでは、実行中のタスクと送信されたタスクを最後まで完了するようにタスクエグゼキュータBeanを構成することにより、SpringBootアプリケーションを安全にシャットダウンする方法を説明しました。 これにより、すべてのタスクが指定された時間をかけて作業を完了することが保証されます。

明らかな副作用の1つは、シャットダウンフェーズが長くなる可能性があることです。 したがって、アプリケーションの性質に応じて、使用するかどうかを決定する必要があります。

いつものように、この記事の例はGitHubから入手できます。