本ドキュメントでは、SchedulerX でジョブを管理する際の一般的な問題のトラブルシューティング方法について説明します。
Spring アプリケーションで bean が見つからない問題
[Application Management] ページで、接続されているマシンの起動モードを確認します。モードが Spring または Spring Boot であることを確認してください。
JobProcessorをbeanとしてインジェクトします。たとえば、@Componentアノテーションを追加します。POM の依存関係を確認します。プロジェクトが
spring-boot-devtoolsに依存している場合は、それを除外する必要があります。JobProcessor クラスまたは process メソッドがアスペクト指向プログラミング (AOP) アノテーションを使用している場合、SchedulerX エージェントを最新バージョンにアップグレードしてください。以前のバージョンは AOP をサポートしていません。
余分なプロキシレイヤーが原因で、bean の型が一致しなくなることがあります。トラブルシューティングするには、
DefaultListableBeanFactoryクラスにブレークポイントを設定してください。beanDefinitionNamesメンバー変数は、Spring に登録されているすべての bean をリストします。このリストをチェックして、予期しないアスペクトによって bean がプロキシ化されているかどうかを判断します。これは、問題のあるサードパーティライブラリが間接的にインポートされた場合に発生する可能性があります。この問題を解決するには、そのライブラリを除外してください。
ProxyFactory のログでは、TrackerInterceptorFilter が追加の AOP プロキシとして機能し、bean の型が一致しなくなっていることがわかります:
org.springframework.aop.framework.ProxyFactory: 1 interfaces [com.alibaba.schedulerx.worker.processor.JobProcessor]; 1 advisors [org.springframework.aop.support.DefaultPointcutAdvisor: pointcut [Pointcut.TRUE]; advice [com.alibaba.onetouch.tracker.filter.TrackerInterceptorFilter@7dc779bb]]; targetSource [SingletonTargetSource for target object [com.alibaba.onetouch.lc.dts.presentation.LcForexMessageRedoJob@11087483]]; proxyTargetClass=false; optimize=false; opaque=false; exposeProxy=false; frozen=false上記の方法で問題が解決しない場合は、ThreadContainer.start メソッドをデバッグしてください。存在するクラスに対して class.forName エラーが発生する場合、クラスローダーの不整合が原因である可能性が高いです。これは、アプリケーションが特定のフレームワークを使用している場合に発生することがあります。これを解決するには、SchedulerxWorker.setClassLoader を呼び出して正しいクラスローダーを指定してください。
「Unable to make field private」エラーによるジョブの失敗
MapReduce ジョブは、シリアル化およびデシリアライゼーションのフレームワークを使用します。Java 9 以降、プライベートフィールドへのリフレクションによるアクセスは明示的に有効にする必要があります。JVM 引数に次のパラメーターを追加してください:
--add-opens java.base/java.lang=ALL-UNNAMED「submit jobInstanceId to worker timeout」エラーによるジョブの失敗
このエラーが散発的に、またはアプリケーションのデプロイ中に発生する場合は、無視してかまいません。
エラーが継続し、エラーメッセージ内の workerAddr が常に同じマシンを指している場合、サーバーとエージェント間の永続的な接続が切断されています。この問題を解決するには、影響を受けるワーカーノードを再起動するか、SchedulerX エージェントを最新バージョンにアップグレードしてください。アップグレード後、切断された永続的な接続は自動的に回復できます。
「used space beyond 90.0%!」エラーによるジョブの失敗
ディスクがいっぱいです。ECS インスタンスまたはコンテナのディスク容量を解放してください。
「ClassNotFoundException」エラーによるジョブの失敗
このエラーは、ジョブを実行するワーカーノードでクラスが見つからないことを示します。Java ジョブの Processor には、単純名ではなく完全修飾クラス名を使用してください。
設定した jobProcessor のクラス名が正しい場合、そのクラスがワーカーノードに存在しないことになります。通常、不正なパッケージをデプロイした場合や、アプリケーションが他のユーザーのマシンに接続された場合にこの問題が発生します。ワーカーノードにログインし、逆コンパイラを使用して調査できます。
「jobInstance=xxx don't update progress more than 60s」エラーによるジョブの失敗
ジョブを実行しているワーカーノードが動作を停止したか、デプロイ中であり、60 秒以上進捗を報告しない場合、サーバーはジョブインスタンスを強制的に終了します。ワーカーノードが問題の原因であること、またはワーカーノードがすでに存在しないことを確認できる場合は、このエラーを無視できます。
エラーメッセージなしでのジョブの失敗
症状:
ジョブは失敗しますが、エラーメッセージは表示されません。
考えられる原因:
マシンの障害またはビジネスロジックの障害が原因である可能性があります。
解決策:
実行リストページに移動し、[Task Instance List] タブを選択します。失敗したジョブインスタンスを見つけ、[Operation] 列の [Details] をクリックして [Task Instance Details] ページに移動し、障害が発生したマシンを特定します。
タスクの詳細が利用できない場合、そのジョブはスタンドアロンジョブです。この場合、基本情報セクションで
workerAddrを見つけて、ジョブを実行したマシンを特定します。マシンにログインし、~/logs/schedulerx/worker.log ファイルを開きます。
grep <Instance ID> worker.logコマンドを実行して、ジョブインスタンスに関連するログを表示します。ERROR レベルの例外が存在する場合、スタックトレースで具体的な原因を確認します。エラーの説明が空の場合、エラーを報告しなかったビジネスロジックに障害の原因がある可能性が高いです。この場合、まずビジネスロジックを調査してください。
エラーの説明がフレームワークの例外を示している場合は、DingTalk グループ (ID: 23103656) に参加して SchedulerX のテクニカルサポートにお問い合わせください。
ジョブの失敗調査
例外をスローするスタンドアロンジョブについては、実行リストページに移動し、[Task Instance List] タブをクリックします。失敗したジョブインスタンスの [Operation] 列で [Details] をクリックしてエラーメッセージを表示します。
ジョブが例外をスローしない場合、または分散ジョブの場合は、Professional Edition を使用していれば Log Service を使用して問題をトラブルシューティングできます。
Basic Edition を使用している場合は、ワーカーノードにログインし、SchedulerX のログとアプリケーションのログを確認して問題をトラブルシューティングできます。
ジョブが実行中の状態でスタックする
症状:
スケジュールされたジョブが実行中のままで、終了しません。
考えられる原因:
ビジネスロジックの問題。
SchedulerX の問題。
解決策:
問題がビジネスロジックに関連している場合は、次の方法でトラブルシューティングしてください。その他の問題については、DingTalk グループ (ID: 23103656) に参加して SchedulerX のテクニカルサポートにお問い合わせください。
Professional Edition:コンソールの [View Stack Trace] 機能を使用して、ジョブ例外のスタックトレースを調べます。この機能は、エージェントバージョン 1.4.2 以降で利用できます。
Basic Edition:スタックしたワーカーノードにログインし、
jstackコマンドを実行してスタックトレースを表示します。jstack <pid> | grep <job instance id> -A 20次の出力例は、スレッドのスタックトレースを示しています:
$jstack 29191 |grep 58903617 -A 200 "Schedulerx-Container-Thread-58903617-0" #4093 prio=5 os_prio=0 tid=xxx nid=xxx waiting on condition [0x00002ad443101000] java.lang.Thread.State: WAITING (parking) at sun.misc.Unsafe.park(Native Method) - parking to wait for <0x000000072b837ae0> (a java.util.concurrent.CountDownLatch$Sync) at java.util.concurrent.locks.LockSupport.park(LockSupport.java:175) at java.util.concurrent.locks.AbstractQueuedSynchronizer.parkAndCheckInterrupt(AbstractQueuedSynchronizer.java:836) at java.util.concurrent.locks.AbstractQueuedSynchronizer.doAcquireSharedInterruptibly(AbstractQueuedSynchronizer.java:997) at java.util.concurrent.locks.AbstractQueuedSynchronizer.acquireSharedInterruptibly(AbstractQueuedSynchronizer.java:1304) at java.util.concurrent.CountDownLatch.await(CountDownLatch.java:231) at com.aliyun.ticket.importworker.WOImportJob.startImport(WOImportJob.java:353) at com.aliyun.ticket.importworker.WOImportJob.doImportForAll(WOImportJob.java:242) at com.aliyun.ticket.importworker.WOImportJob.process(WOImportJob.java:163) at com.alibaba.schedulerx.worker.container.ThreadContainer.start(ThreadContainer.java:90) at com.alibaba.schedulerx.worker.container.ThreadContainer.run(ThreadContainer.java:60) at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1142) at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:617) at java.lang.Thread.run(Thread.java:756) "Container-Batch-Statues-Retrieve-Thread-58903617" #4092 prio=5 os_prio=0 tid=xxx nid=xxx waiting on condition [0x00002ad44fe0e000] java.lang.Thread.State: TIMED_WAITING (sleeping) at java.lang.Thread.sleep(Native Method) at com.alibaba.schedulerx.worker.batch.BaseReqHandler$2.run(BaseReqHandler.java:74) at java.lang.Thread.run(Thread.java:756) "TDDL-Druid-ConnectionPool-DestroyScheduler--2-thread-237" #4052 daemon prio=5 os_prio=0 tid=xxx nid=xxx waiting on condition [0x00002ad462e00000] java.lang.Thread.State: TIMED_WAITING (parking) at sun.misc.Unsafe.park(Native Method) - parking to wait for <0x00000007436c7190> (a java.util.concurrent.locks.AbstractQueuedSynchronizer$ConditionObject) at java.util.concurrent.locks.LockSupport.parkNanos(LockSupport.java:215) at java.util.concurrent.locks.AbstractQueuedSynchronizer$ConditionObject.awaitNanos(AbstractQueuedSynchronizer.java:2078) at java.util.concurrent.ScheduledThreadPoolExecutor$DelayedWorkQueue.poll(ScheduledThreadPoolExecutor.java:1129) at java.util.concurrent.ScheduledThreadPoolExecutor$DelayedWorkQueue.poll(ScheduledThreadPoolExecutor.java:809) at java.util.concurrent.ThreadPoolExecutor.getTask(ThreadPoolExecutor.java:1066) at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1127) at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:617)
ジョブ実行が遅い場合のトラブルシューティング
Professional Edition を有効にして、分散トレーシングを使用してください。詳細については、「分散トレーシングを有効にする方法」をご参照ください。
ジョブインスタンスの上限到達
症状:
[Task Management] ページで Run Once をクリックすると、次のエラーメッセージが表示されます: The number of running job instances has reached the job concurrency limit. Please try again later.
考えられる原因:
ジョブのインスタンスがすでに実行中です。
実行中のジョブインスタンスの数が、ジョブに設定された最大同時実行数に達しています。
解決策:
設定された同時実行数が適切である場合、アクションは不要です。ジョブ管理ページに移動し、 をクリックすることで、実行中のジョブインスタンスを表示できます。
同時実行数が適切でない場合は、対象のジョブの Actions 列にある Edit をクリックしてください。[Advanced Configuration] セクションで、[インスタンスの同時実行数] を設定してください。
次回の実行時間までにジョブが終了しない問題
デフォルトでは、インスタンスの同時実行数は 1 であり、ジョブは直列に実行されることを意味します。長時間実行されるジョブが次回のスケジュールされた時間までに終了しない場合、SchedulerX は新しいジョブインスタンスをキューに入れるのではなく、破棄します。
インスタンスの同時実行数を 2 に設定すると、前のジョブインスタンスが終了していなくても、新しいジョブインスタンスを開始できます。最大で 2 つのジョブインスタンスが同時に実行できます。
ワンタイムジョブの作成
SchedulerX 2.0 では、ワンタイムジョブを作成できます。これを行うには、時間タイプを one_time に設定してください。ワンタイムジョブは実行履歴を保持しません。
ワンタイムジョブの実行履歴の表示
データの蓄積を防ぐため、SchedulerX はワンタイムジョブを実行後に自動的に破棄し、実行履歴を保持しません。履歴を保持する必要がある場合は、Log Service を有効にすると、すべてのジョブの実行ログを過去 2 週間保持できます。これはトラブルシューティングに役立ちます。Log Service を有効にする方法については、「アプリケーション管理」をご参照ください。
秒単位のスケジューリングの設定
SchedulerX は秒単位のスケジューリングをサポートしています。cron および fix_rate の時間タイプは、秒単位のスケジューリングをサポートしていません。second_delay 時間タイプを選択できます。これは、前の実行が完了してから指定された秒数後にジョブを実行します。
特定の時間にジョブがスケジューリングされない問題
スタンドアロンジョブが特定の時間にスケジューリングされない場合は、マシンリストにマシンがあるか、すべてのマシンがビジー状態でないかを確認してください。利用可能なマシンがないか、すべてのマシンがビジー状態の場合は、それに応じて問題をトラブルシューティングしてください。詳細については、「利用可能なワーカーがいない」および「すべてのワーカーがビジー状態である場合の対処法」をご参照ください。
ジョブに利用可能なマシンがない場合にアラートを設定してください。詳細については、「ジョブ管理」をご参照ください。
ジョブのタイムアウト設定
SchedulerX は、ジョブ全体のタイムアウト設定をサポートしていますが、ジョブ内の個々のタスクのタイムアウト設定はサポートしていません。コンソールでタイムアウト期間を動的に変更できます。詳細については、「ジョブ管理」をご参照ください。
停止後もジョブインスタンスが実行し続ける問題
症状:
ジョブインスタンスが停止された後も実行し続けます。
考えられる原因:
ジョブインスタンスを停止すると、SchedulerX はエージェントに kill シグナルを送信します。シグナルを受信した後、エージェントは新しいタスクのディスパッチを停止し、保留中のタスクの実行を停止し、インスタンスコンテキストとそのすべてのスレッドプールを破棄します。ただし、エージェントはすでに進行中のタスクを直ちに停止しません。エージェントはスレッドを中断するだけなので、タスクは完了するまで実行され続けます。
解決策:
通常、アクションは不要です。実行中のタスクが終了するのを待ってください。
インスタンスが停止されたときに実行中のすべてのタスクを直ちに終了させる必要がある場合は、スレッドの中断状態を処理するようにタスク処理ロジックを変更する必要があります。
ジョブの高度な設定の使用
詳細については、「ジョブ管理の高度な設定パラメーター」をご参照ください。
すべてのワーカーがビジー状態の場合
[Application Management] ページでインスタンスを表示して、ビジー状態のワーカーノードを特定できます。次に、[Busy] をクリックして、しきい値を超えたメトリクスを表示できます。
Application Management ページで [Edit Application Group] をクリックして、ビジー状態のしきい値を設定できます。
[Instance Busy Configuration] セクションには、次のしきい値が含まれています:
[load5]:デフォルト値は 0 です
[Memory usage]:デフォルト値は 90% です
[Disk usage]:デフォルト値は 95% です
[Trigger busy status]:デフォルトで有効です
ワーカーノードが高負荷のためにビジー状態であり、Kubernetes デプロイメントを使用している場合は、次の 2 つのパラメーターを設定する必要がある場合があります。そうしないと、収集された CPU 使用率が不正確になる可能性があります。詳細については、「Spring Boot アプリケーションを SchedulerX に接続する」をご参照ください。
パラメーター | 説明 | 値 | 最小バージョン |
spring.schedulerx2.enableCgroupMetrics | cgroups を使用してエージェントインスタンスのメトリクスを収集するかどうかを指定します。Kubernetes 環境では、これを手動で有効にする必要があります。 |
| 1.2.2.2 |
spring.schedulerx2.cgroupPathPrefix | コンテナ内の cgroup パス。 | デフォルトパスは | 1.2.2.2 |
分散トレーシングの有効化
ジョブスケジューリングは、エンドツーエンドの分散トレーシングをサポートしています。詳細については、「分散トレーシングを有効にする方法」をご参照ください。
デプロイ中にジョブがスタックまたは低速化する問題
症状:
アプリケーションのデプロイ中に、ジョブがスタックしたり低速化したりします。
考えられる原因:
分散ジョブの場合、タスクを処理しているマシンがオフラインになると、システムはそのタスクを再分配します。その後、システムはマシンがオンラインであるかどうかをポーリングで確認するため、プロセス全体が遅くなる可能性があります。
解決策:
エージェントを最新バージョンにアップグレードしてください。エージェントバージョン 1.7.9 以降には、この動作に対する最適化が含まれています。
[単発実行] 時のインスタンスパラメータープロンプト
ジョブ管理ページで、[Operation] 列の [単発実行] をクリックして、スケジュールされたジョブを 1 回実行できます。ダイアログボックスの [インスタンスパラメーター] フィールドはオプションであり、主にテスト用に使用されます。
ダイアログボックスには [Specify Machine] フィールドも含まれています。ドロップダウンリストを使用して、実行するインスタンスを選択できます。注:インスタンスパラメーターはジョブパラメーターを上書きしません。
インスタンスパラメーターとジョブパラメーターの比較
インスタンスパラメーターとジョブパラメーターは異なる概念です。コードが取得するパラメーターは、ビジネスロジックに依存します。
ジョブパラメーターまたはインスタンスパラメーターの取得
詳細については、次のコードをご参照ください:
@Component
public class JavaDemoProcessor extends JavaProcessor {
private static final Logger LOGGER = LoggerFactory.getLogger("schedulerxLog");
@Override
public ProcessResult process(JobContext jobContext) throws InterruptedException {
LOGGER.info(JSON.toJSONString(jobContext));
// ジョブパラメーターを取得します。
String jobParameters = jobContext.getJobParameters();
// インスタンスパラメーターを取得します。
String instanceParameters = jobContext.getInstanceParameters();
LOGGER.info("Job parameters: " + jobParameters);
LOGGER.info("Instance parameters: " + instanceParameters);
return new ProcessResult(InstanceStatus.SUCCESS);
}
}インスタンスパラメーターからジョブパラメーターへのフォールバック
詳細については、次のコードをご参照ください:
@Component
public class JavaDemoProcessor extends JavaProcessor {
private static final Logger LOGGER = LoggerFactory.getLogger("schedulerxLog");
@Override
public ProcessResult process(JobContext jobContext) throws InterruptedException {
String params;
if (StringUtils.isNotBlank(jobContext.getInstanceParameters())) {
params = jobContext.getInstanceParameters();
} else {
params = jobContext.getJobParameters();
}
LOGGER.info("JavaDemoProcessor params:{}", params);
return new ProcessResult(InstanceStatus.SUCCESS);
}
}