すべてのプロダクト
Search
ドキュメントセンター

Microservices Engine:Implement graceful shutdown based on MSE XXL-JOB

最終更新日:Nov 10, 2025

マイクロサービスエンジン (MSE) XXL-JOB は、オープンソースの XXL-JOB が提供する機能に加えて、グレースフルシャットダウン機能を提供します。MSE XXL-JOB は、アプリケーションをグレースフルシャットダウンする前に、サーバーに新しいジョブのディスパッチを停止するように通知し、すべての実行中のジョブが完了するまで待機します。このプロセス中にビジネスの中断は発生しません。このトピックでは、実際のアプリーケーションの再起動またはシャットダウンシナリオを処理するために、Alibaba Cloud XXL-JOB ベースのグレースフルシャットダウンを有効にする方法について説明します。

概要

実際のビジネスシナリオでは、アプリケーションプロセス内のジョブは一定の頻度でスケジュールされます。アプリケーションがリリースされて再起動されると、実行中のジョブは強制的に停止され、データが不完全になったり、スケジューリングの成功率が急激に低下したりする可能性があります。その結果、次の理由により、ビジネスデータの問題が発生します。

  • ジョブ実行の中断: ジョブの実行中にアプリケーションプロセスが終了し、ビジネス処理が停止します。これにより、ビジネスデータが不完全になる可能性があります。

  • ジョブスケジューリングの成功率の低下: アプリケーションのリリースと再起動のプロセス中に、スケジューラは終了したノードにジョブを配信します。これにより、ジョブスケジューリングが失敗し、ジョブ処理の全体的な効率が低下します。

ジョブスケジューリングにおける前述の問題に対処するには、アプリケーションのローリングデプロイメント再起動プロセス中にビジネスの継続性をシームレスに確保するために、ジョブのグレースフルシャットダウンを有効にする必要があります。

オープンソース XXL-JOB ベースのグレースフルシャットダウンの実装方法

オープンソース XXL-JOB ベースのグレースフルシャットダウンの原則と既存の問題

オープンソースの XXL-JOB を使用してジョブをスケジュールする場合、XXL-JOB Executor はグレースフルシャットダウン機能を適切に実装できません。したがって、オープンソースの XXL-JOB を使用してアプリケーションをグレースフルシャットダウンする場合は、カスタム変更が必要です。変更を実行する前に、オープンソース XXL-JOB でのジョブの配信と実行の全プロセスを分析できます。プロセス全体には、XXL-JOB AdminXXL-Job Executor の 2 つのモジュールが関係します。

次のコンテンツでは、オープンソースの XXL-JOB を使用してアプリケーションをグレースフルシャットダウンするときに発生する可能性のある既存の問題について説明します。

問題 1: オフラインノードにジョブがスケジュールされる

Executor がオフラインになった後、システムはジョブスケジューリングを完了せず、Executor リストをタイムリーに更新しません。この場合、ジョブがオフラインノードにスケジュールされ、スケジューリングが失敗する可能性があります。

論理処理操作 1: Executor の登録
  • 必要な XXL-JOB SDK が起動した後、ビジネスアプリケーションは ExecutorRegistryThread スレッドを初期化して、XXL-JOB Admin にハートビートメッセージを継続的に送信します。

  • ハートビートメッセージを受信すると、XXL-JOB Admin は、JobRegistryHelper を使用して、登録済み Executor に関する情報を xxl_job_registry という名前のデータベーステーブルに書き込みます。

  • JobRegistryHelper 内の特定のスレッドは、xxl_job_group テーブルの address_list フィールドを定期的にクエリして更新します。address_list は、登録済み Executor のリストを提供します。

image

image

論理処理操作 2: オンライン Executor の選択
  • スケジューリングスレッドがジョブをトリガーした後、XxlJobTrigger がジョブの実行を担当します。

  • ジョブを実行する前に、XxlJobTriggerxxl_job_group テーブルの address_list フィールドから Executor のリストを読み取ります。

  • ExecutorRouter は、指定されたルーティングポリシーに基づいて、Executor リストから Executor を選択します。

  • Executor が選択されると、XxlJobTrigger は RPC リクエストを送信して、指定された IP アドレスを持つノードにジョブを配信します。この場合、オフラインノードを選択すると、ジョブがトリガーされない可能性があります。

image

概要: Executor の登録中に取得された Executor リストは、ジョブの実行のためにリアルタイムで取得された Executor リストと同期されていません。その結果、オンライン Executor リストはタイムリーに更新されません。

問題 2: ジョブが強制的に停止される

XXL-JOB Executor が無効になると、ジョブの JobThread スレッドが停止し、ジョブは失敗としてマークされます。さらに、キュー内のすべてのジョブのリクエストが破棄され、ジョブは失敗としてマークされます。

論理処理操作 1: 特定の Executor にジョブを配信する
  • ビジネスアプリケーションの Executor がジョブリクエストを受信した後、Executor はジョブ ID に基づいてジョブごとに JobThread スレッドを作成します。JobThread スレッドは、ジョブを実行するために使用されます。

  • ジョブリクエストがトリガーされると、現在の JobThread スレッドが処理するためにキューに追加されます。ジョブごとにブロッキングポリシーが異なります。

  • JobThread スレッドは、キュー内のトリガー結果を継続的に読み取り、対応する JobHandler を実行して、ビジネスロジック処理を完了します。

  • ジョブが終了した後、JobThread スレッドは実行情報を TriggerCallbackThread の実行応答キューに送信し、次のジョブに進みます。

  • Executor が停止すると、XxlJobExecutor.destroy メソッドを実行して JobThread スレッドを停止し、スケジューリングリクエストのキューをクリアします。

論理処理操作 2: ジョブの実行結果をフィードバックする
  • TriggerCallbackThread スレッドは継続的に実行され、実行結果の現在のキューを読み込み、実行結果をバッチで XXL-JOB Admin に配信します。

  • TriggerCallbackThread スレッドが XXL-JOB Admin に実行結果を送信できなかった場合、TriggerCallbackThread スレッドは実行結果をローカルディスクに保存します。TriggerCallbackThread スレッドは、後で実行結果の再送信を試みます。

  • XXL-JOB Admin は実行結果を受信した後、データベースに書き込みます。

image

概要: 前述のシャットダウンプロセスでは、removeJobThread スレッドの下位レイヤーが JobThread スレッドを直接停止し、スレッドキュー内のジョブリクエストを無視し、ジョブを失敗としてマークします。

実装プロセス

オープンソース XXL-JOB ベースのグレースフルシャットダウンの原則と既存の問題 のプロセス分析に基づいて、アプリケーションのグレースフルシャットダウンには、トラフィックの削除、キュー内のジョブの完了の待機、アプリケーションのシャットダウンの 3 つのステップが含まれます。

XXL-JOB コアモジュールによって提供される com.xxl.job.core.executor.XxlJobExecutor#destroy メソッドは、Spring Boot モードでアプリケーションプロセスが終了すると、自動的にコールバックを実行します。このプロセスでは、アプリケーション Executor が無効になり、リソースが再利用されます。ただし、使用される関連ロジックでは、グレースフルシャットダウン機能を完全に実装できません。したがって、前述の分析に基づいて、グレースフルシャットダウンを実装するには、次の手順を実行する必要があります。

ステップ 1: アプリケーションノードからトラフィックを削除する

  • XxlJobExecutor#destroystopEmbedServer() メソッドは、ハートビート登録メカニズムを停止し、registryRemove リクエストを XXL-JOB Admin に送信して、現在の Executor を削除します。

  • XXL-JOB Admin はリクエストを受信した後、データベースの xxl_job_registry テーブルから現在の Executor を削除します。ただし、前述の分析に基づいて、xxl_job_group テーブルの address_list フィールドは同期されず、リアルタイムで更新されません。したがって、トラフィックは削除されません。

  • トラフィックを削除するには、XXL-JOB Admin サーバーによって提供される機能を変更する必要があります。次のいずれかの方法を使用して、機能を変更できます。

    • 後続の処理ロジックを JobRegistryHelper.registryRemove メソッドに追加して、xxl_job_group テーブルの address_list フィールドを更新します。freshGroupRegistryInfo メソッドで更新ロジックを実装することもできます。

    • XxlJobTrigger#trigger() メソッドを変更して、xxl_job_group テーブルの address_list フィールドを読み取る方法を調整します。これにより、XXL-JOB Admin サーバーは、自動登録プロセス中に xxl_job_registry テーブルから address_list を直接読み取ることができます。

前述の操作を実行した後、トラフィックが削除されます。

ステップ 2: キュー内のジョブが完了するまで待機する

  • XxlJobExecutor#destroy メソッドの後続の処理ロジックを変更して、キュー内のすべてのジョブが完了するまで待機します。次のサンプルコードは例を示しています。

    public void destroy(){
    
        // destroy executor-server
        stopEmbedServer();
    
        // destroy jobThreadRepository
        if (jobThreadRepository.size() > 0) {
            List keyList = new ArrayList(jobThreadRepository.keySet());
            for (int i=0; i < keyList.size(); i++) {
                JobThread jobThread = jobThreadRepository.get(keyList.get(i));
                // キュー内のすべてのジョブが完了するまで待機します。
                while (jobThread != null && jobThread.isRunningOrHasQueue()) {
                    try {
                        TimeUnit.SECONDS.sleep(1L);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
            }
        }
        jobHandlerRepository.clear();
    
        // destroy JobLogFileCleanThread
        JobLogFileCleanThread.getInstance().toStop();
    
        // destroy TriggerCallbackThread
        TriggerCallbackThread.getInstance().toStop();
    
    }
  • 実行結果のキューがすべての実行結果をフィードバックするまで待機します。オープンソースの XXL-JOB では、TriggerCallbackThread.getInstance().toStop() メソッドは、TriggerCallbackThread スレッドが停止した後、最終的に実行結果を一度に同期します。したがって、追加の処理は必要ありません。

このステップを実行した後、実行中のジョブが完了するまで待機できます。ジョブタイプに基づいて、カスタムシャットダウン操作を実行することもできます。

ステップ 3: アプリケーションプロセスを停止する

  • アプリケーションプロセスを停止するには、kill -15 をアプリケーションデプロイメントスクリプトに追加して、JVM シャットダウンフックをトリガーすることをお勧めします。ビジネス要件に基づいて、タイムアウト時にアプリケーションプロセスを強制的に停止することもできます。

  • Spring Boot Actuator 機能を使用してグレースフルシャットダウン機能を XXL-JOB に統合し、/actuator/shutdown インターフェースを使用してアプリケーションをシャットダウンできます。

前提条件

  • エンジンバージョンは 2.1.0 以降です。エンジンバージョンの詳細については、「XXL-JOB のリリースノート」をご参照ください。

  • SchedulerX プラグインパッケージに関連する依存関係が、クライアントの pom.xml ファイルに追加されます。XXL-JOB プラグインバージョンの詳細については、「XXL-JOB プラグインのリリースノート」をご参照ください。

    <dependency>
      <groupId>com.aliyun.schedulerx</groupId>
      <artifactId>schedulerx3-plugin-xxljob</artifactId>
      <version>最新バージョン</version>
    </dependency>

手順

このセクションでは、さまざまなビジネスフォームとデプロイシナリオに対して、グレースフルシャットダウンソリューションを構成して有効にする方法について説明します。手順は 2 つのステップで構成されます。

ステップ 1: さまざまなフレームワークを使用して Executor との初期統合を実行する

ビジネスアプリケーションのさまざまなデプロイモードでは、初期統合にさまざまな方法が必要です。

モード 1: Spring Boot を使用してビジネスアプリケーションを XXL-JOB Executor と統合する (推奨)

Spring Boot を使用してビジネスアプリケーションを XXL-Job Executor と統合する場合、システムはグレースフルシャットダウンの初期統合を自動的に実行できます。次の手順を実行します。

  1. SchedulerX プラグインの Maven 依存関係を追加します。プラグインバージョンの詳細については、「プラグインバージョンのリリースノート」をご参照ください。

    <dependency>
      <groupId>com.aliyun.schedulerx</groupId>
      <artifactId>schedulerx3-plugin-xxljob</artifactId>
      <version>最新バージョン</version>
    </dependency>
  2. アプリケーション構成パラメーターを追加し、グレースフルシャットダウンを有効にします。パラメーターの詳細については、「パラメーターの説明」をご参照ください。

    # グレースフルシャットダウンを構成する
    xxl.job.executor.shutdownMode=WAIT_ALL

モード 2: Spring を使用してビジネスアプリケーションを XXL-JOB Executor と統合する

ビジネスアプリケーションが Spring フレームワークを使用して起動される Web アプリケーションである場合は、POM 依存関係アプリケーション起動パラメーターを追加する必要があります。詳細については、「モード 1: Spring Boot を使用してビジネスアプリケーションを XXL-JOB Executor と統合する (推奨)」をご参照ください。また、次の XxlJobExecutorEnhancerInitializer 構成を web.xml ファイルに追加する必要があります。

<web-app>
  <context-param>
        <!-- Spring ApplicationContextInitializer は、XXL-JOB Executor の機能を向上させるために使用されます。 -->
        <param-name>globalInitializerClasses</param-name>
        <param-value>com.aliyun.schedulerx.xxljob.enhance.XxlJobExecutorEnhancerInitializer</param-value>
    </context-param>
</web-app>

モード 3: フレームレスモードでビジネスアプリケーションを XXL-JOB Executor と統合する

XXL-JOB Executor のユースケースで説明されているように、フレームレスモードでビジネスアプリケーションを XXL-JOB Executor と統合する場合、純粋な Java でビジネスアプリケーションを起動するときに、カスタムコードを使用してグレースフルシャットダウンの初期統合を実行できます。ビジネスアプリケーションの POM 依存関係アプリケーション起動パラメーターを追加する必要があります。詳細については、「モード 1: Spring Boot を使用してビジネスアプリケーションを XXL-JOB Executor と統合する (推奨)」をご参照ください。次のコードは例を示しています。

  • Executor を起動する前に、EnhancerLoader.load(xxlJobProp) を追加して、機能拡張を読み込みます。

  • Executor を起動する前に、Runtime.getRuntime().addShutdownHook(...) を追加して、現在のアプリケーションのシャットダウンフックを追加します。

サンプルコード

public static void main(String[] args) {
    try {
        // xxl-job-executor.properties ファイルを読み込みます。
        Properties xxlJobProp = FrameLessXxlJobConfig.loadProperties("xxl-job-executor.properties");

        // 初期統合中に XXL-JOB Executor の拡張機能を読み込みます。
        EnhancerLoader.load(xxlJobProp);

        // XXL-JOB Executor を起動します。
        FrameLessXxlJobConfig.getInstance().initXxlJobExecutor(xxlJobProp);

        // グレースフルシャットダウンのシャットダウンフックを追加します。
        Runtime.getRuntime().addShutdownHook(new Thread(){
            @Override
            public void run() {
                FrameLessXxlJobConfig.getInstance().destroyXxlJobExecutor();
            }
        });
        // 中断されるまでブロックします。
        while (true) {
            try {
                TimeUnit.HOURS.sleep(1);
            } catch (InterruptedException e) {
                break;
            }
        }
    } catch (Exception e) {
        logger.error(e.getMessage(), e);
    } finally {
        // destroy
        FrameLessXxlJobConfig.getInstance().destroyXxlJobExecutor();
    }
}

ステップ 2: アプリケーションをシャットダウンする

セルフビルド CD プロセスで kill -15 を使用する

セルフビルドの継続的デリバリー (CD) プロセスでは、1 つのノードを使用してアプリケーションプロセスを停止できます。このノードでは、stop.sh という名前のスクリプトを作成して、アプリケーションプロセスを停止または終了できます。スクリプトコンテンツには、アプリケーションのグレースフルシャットダウンのロジックが含まれている必要があります。次のコードは、アプリケーションプロセスのシャットダウンのサンプルスクリプトコンテンツを示しています。

アプリケーションプロセスのシャットダウンのサンプルスクリプトコンテンツ

# アプリケーションの起動後、プロセス ID を app.pid ファイルに書き込みます。
PID="{アプリケーションデプロイメントパス}/app.pid"
FORCE=1
if [ -f ${PID} ]; then
  TARGET_PID=`cat ${PID}`
  kill -15 ${TARGET_PID}
  loop=1
  while(( $loop<=5 ))
  do
    ## ヘルスチェックを使用して、現在のアプリケーションプロセスが終了していることを確認します。ロジックは、アプリケーションの特性に基づいてカスタマイズできます。
    health
    if [ $?  == 0 ]; then
      echo "check $loop times, current app has not stop yet."
      sleep 5s
      let "loop++"
    else
      FORCE=0
      break
    fi
  done
  if [ $FORCE -eq 1 ]; then
  	echo "App(pid:${TARGET_PID}) stop timeout, forced termination."
    kill -9 ${TARGET_PID}
  fi
  rm -rf ${PID}
  echo "App(pid:${TARGET_PID}) stopped successful."
fi

Kubernetes デプロイメントの preStop フックを使用する

Kubernetes ポッドのライフサイクル管理機能は、グレースフルシャットダウンを自動的に実装できます。また、preStop フックを使用して、exec を実行してスクリプトを実行し、HTTP リクエストを使用することにより、グレースフルシャットダウンロジックを実装することもできます。

  • 無効な変更: アプリケーションプロセスがコンテナー内のメインプロセス PID 1 である場合、システムは SIGTERM 信号をメインプロセスに自動的に送信して、アプリケーションをグレースフルシャットダウンします。

  • カスタム preStop: コンテナーに複雑なマルチプロセス関係がある場合は、kill -15 PID を使用してアプリケーションプロセスを停止するカスタム preStop スクリプトを構成できます。事前に構成された stop.sh スクリプトを呼び出して、アプリケーションプロセスを終了することもできます。

重要

このソリューションでは、ポッドの terminationGracePeriodSeconds パラメーターは、グレースフルシャットダウンの最大待機時間を秒単位で指定します。このパラメーターのデフォルト値は 30 です。ビジネス要件に基づいてパラメーターを構成する必要があります。

apiVersion: apps/v1
kind: Deployment
metadata:
  name: my-app
spec:
  replicas: 3
  selector:
    matchLabels:
      app: my-app
  template:
    metadata:
      labels:
        app: my-app
    spec:
      containers:
      - name: my-app-container
        image: my-app-image:latest
        lifecycle:
          preStop:
            exec:
              # command: ["/bin/sh", "-c", "kill -15 PID && sleep 30"]
              command: ["/bin/sh", "-c", "スクリプトパス/stop.sh"]

Alibaba Cloud のアプリケーションリリースプラットフォームで自動統合を使用する

このソリューションは近日中に利用可能になります。

パラメーターの説明

アプリケーションのグレースフルシャットダウンを有効にするには、次のパラメーターを構成する必要があります。このパラメーターは、グレースフルシャットダウンを実装するための 2 つのモードを提供します。

# グレースフルシャットダウンモード。有効な値: WAIT_ALL と WAIT_RUNNING。
# このパラメーターを構成しない場合、XXL-JOB の元のロジックが適用され、グレースフルシャットダウンはデフォルトで無効になります。
xxl.job.executor.shutdownMode=WAIT_ALL

グレースフルシャットダウンモード

説明

WAIT_ALL

このモードでは、実行中のジョブとキュー内のジョブを含むすべてのジョブが完了した後でのみ、アプリケーションが終了します。このモードをお勧めします。

WAIT_RUNNING

このモードでは、アプリケーションでスレッドが割り当てられている実行中のジョブが完了した後、アプリケーションが終了します。キュー内のジョブはドロップされます。