Quartzスケジューラが1秒間に実行できるジョブの数には制限があるようです。このシナリオでは、1秒あたり約20ジョブが24時間365日起動し、クォーツは1秒あたり最大10ジョブ(JDBCでバックアップされたJobStoreの場合は100クォーツスレッドと100データベース接続プールサイズ)で正常に機能しましたが、20に増やすと1秒あたりのジョブ数が非常に遅くなり、トリガーされたジョブは実際のスケジュールされた時間に比べて非常に遅くなり、多くの失火が発生し、最終的にシステムの全体的なパフォーマンスが大幅に低下します。興味深い事実の1つは、このような遅延トリガーのJobExecutionContext.getScheduledFireTime().getTime()
は、スケジュール時刻から10〜20分、さらにはそれ以上になることです。
クォーツスケジューラーがジョブのスケジュールされた時間に影響を与えることなく1秒間に実行できるジョブの数と、そのような負荷に最適なクォーツスレッドの数はいくつですか?
それとも私はここで何かが足りないのですか?
ほぼ1万個のアイテム(2つ以上のカテゴリに分類され、現在の場合は2つのカテゴリがあります)があり、特定の頻度で処理する必要があります。 15,30,60 ...分であり、これらのアイテムは、1分あたりの所定のスロットルでその頻度内で処理する必要があります。例えばたとえば、60分間、各カテゴリの5kアイテムは、1分あたり500アイテムのスロットルで処理する必要があります。したがって、理想的には、これらのアイテムは1日の各時間の最初の10(5000/500)分以内に処理され、1分ごとに500個のアイテムが処理され、1分ごとに均等に分散されるため約8分になります。 1つのカテゴリで1秒あたり9アイテム。
これを実現するために、これらのアイテムを処理するためのジョブをトリガーするスケジューラーとしてQuartzを使用しました。ただし、Job.executeメソッドで各アイテムを処理することはありません。これは、Webサービス呼び出しを含むアイテム処理ごとに5〜50秒(平均で30秒)かかるためです。 [〜#〜] jms [〜#〜]キューで処理するアイテムごとにメッセージをプッシュし、別々のサーバーマシンがそれらのジョブを処理します。 Job.executeメソッドにかかる時間がミリ秒を超えないことに気づきました。
16GBのスケジューラー用の8/16コア/スレッドCPUを備えたSolarisSparc64ビットサーバーRAMそして、スケジューラークラスターにはそのようなマシンが2つあります。
以前のプロジェクトで、私は同じ問題に直面しました。私たちの場合、クォーツは1秒の粒度で良好に機能しました。 1秒未満のスケジューリングは一気に進み、観察しているように、失火が頻繁に発生し、システムの信頼性が低下しました。
2つのレベルのスケジューリングを作成することで、この問題を解決しました。Quartzは、n個の連続するジョブのジョブ「セット」をスケジュールします。クラスター化されたQuartzの場合、これは、システム内の特定のサーバーがこのジョブを「設定」して実行することを意味します。セット内のn個のタスクは、「マイクロスケジューラー」によって取り込まれます。基本的には、ネイティブJDK APIを使用して、ジョブを10ミリ秒の粒度までさらに計測するタイミング機能です。
個々のジョブを処理するために、マスターワーカーの設計を使用しました。この設計では、マスターがマルチスレッドのワーカープールへのジョブのスケジュールされた配信(スロットリング)を処理していました。
今日もこれを行う必要がある場合は、 ScheduledThreadPoolExecutor を使用して「マイクロスケジューリング」を管理します。あなたの場合、それは次のようになります:
ScheduledThreadPoolExecutor scheduledExecutor;
...
scheduledExecutor = new ScheduledThreadPoolExecutor(THREAD_POOL_SIZE);
...
// Evenly spread the execution of a set of tasks over a period of time
public void schedule(Set<Task> taskSet, long timePeriod, TimeUnit timeUnit) {
if (taskSet.isEmpty()) return; // or indicate some failure ...
long period = TimeUnit.MILLISECOND.convert(timePeriod, timeUnit);
long delay = period/taskSet.size();
long accumulativeDelay = 0;
for (Task task:taskSet) {
scheduledExecutor.schedule(task, accumulativeDelay, TimeUnit.MILLISECOND);
accumulativeDelay += delay;
}
}
これにより、JDK機能を使用してタスクをマイクロスケジュールする方法に関する一般的なアイデアが得られます。 (免責事項:失敗したタスクのチェック、再試行の管理(サポートされている場合)など、本番環境に対してこれを堅牢にする必要があります...)。
いくつかのテストと調整を行った結果、Quartzジョブと1つのスケジュールされたセット内のジョブの量との間の最適なバランスが見つかりました。
このようにして、スループットが100倍向上しました。ネットワーク帯域幅が実際の制限でした。
まず最初に、Quartzドキュメントで JDBC-JobStoreのパフォーマンスを向上させるにはどうすればよいですか? を確認してください。
ご想像のとおり、絶対値と明確なメトリックがあります。それはすべてあなたの設定に依存します。ただし、ここにいくつかのヒントがあります。
1秒あたり20ジョブは、更新とロックを含め、1秒あたり約100のデータベースクエリを意味します。それはかなりたくさんです!
Quartzセットアップをクラスターに配布することを検討してください。ただし、データベースがボトルネックである場合、それは役に立ちません。多分 TerracottaJobStore
救助に来るでしょうか?
システムにK
コアがあると、K
未満のすべてがシステムを十分に活用できなくなります。ジョブがCPUを集中的に使用する場合は、K
で問題ありません。外部のWebサービスを呼び出している場合、ブロックしている場合、またはスリープしている場合は、はるかに大きな値を検討してください。ただし、100〜200を超えるスレッドは、コンテキストの切り替えによりシステムの速度を大幅に低下させます。
プロファイリングを試しましたか?あなたのマシンはほとんどの時間何をしていますか?スレッドダンプを投稿できますか? CPUではなくデータベースのパフォーマンスが低いと思われますが、それはユースケースによって異なります。
スレッドの数は、n
とn*3
の間のどこかに制限する必要があります。ここで、n
は使用可能なプロセッサの数です。より多くのスレッドをスピンアップすると、ほとんどのスレッドがほとんどの時間ブロックされるため、多くのコンテキスト切り替えが発生します。
1秒あたりのジョブ数に関しては、実際には、ジョブの実行時間と、ネットワークやディスクIOなどの操作でジョブがブロックされる頻度によって異なります。
また、考慮すべきことは、おそらくクォーツはあなたが必要とするツールではないということです。 1日に100万から200万のジョブを送信する場合は、カスタムソリューションを検討することをお勧めします。 1日200万の仕事で何をしているの?
別のオプションは、問題に取り組むための本当に悪い方法ですが、時々機能します...それが実行されているサーバーは何ですか?古いサーバーですか?それはラムをぶつけているかもしれません、またはそれに他のスペックはあなたにいくつかの余分な「アンフ」を与えるでしょう。確かに、それは問題を遅らせるので、対処するのではなく、最善の解決策ではありませんが、あなたが危機に瀕しているなら、それは役立つかもしれません。
1秒あたりのジョブ数が多い状況では、SQLサーバーがテーブルロックではなく行ロックを使用していることを確認してください。 mysqlでは、これはInnoDBストレージエンジンを使用して実行され、テーブルロックのみを提供するデフォルトのMyISAMストレージエンジンではありません。
基本的に、一度に1つの項目を実行するというアプローチは、非常に多くのことを短時間で処理する場合、運命にあり、非効率的です。物事をグループ化する必要があります-個々のジョブをマイクロスケジュールするジョブセットを使用するという提案されたアプローチは最初のステップですが、それでもジョブごとにほとんど何もしないことを意味します。 Webサービスを改善して、一度にN個のアイテムを処理するように指示し、処理するアイテムのセットを使用してWebサービスを呼び出すことをお勧めします。さらに良いのは、Webサービスを介してこの種のことを行うのを避け、それらすべてをデータベース内でセットとして処理することです。これは、データベースに適しています。一度に1つのアイテムを処理するあらゆる種類のジョブは、基本的にスケーラブルでない設計です。
Redisをジョブストアとして使用するMaybeeは良い考えです https://github.com/RedisLabs/redis-quartz