web-dev-qa-db-ja.com

ThreadPoolTask​​Executorプールとキューサイズを決定する方法は?

これは、スレッドプールサイズの決定方法に関するより一般的な質問かもしれませんが、この場合はSpring ThreadPoolTaskExecutorを使用してみましょう。プールのコアと最大サイズ、およびキューの容量について、次の構成があります。これらすべての構成の意味についてはすでに読んでいます-良い答えがあります ここ

    @SpringBootApplication
    @EnableAsync
    public class MySpringBootApp {

        public static void main(String[] args) {
            ApplicationContext ctx = SpringApplication.run(MySpringBootApp.class, args);
        }

        @Bean
        public TaskExecutor taskExecutor() {
            ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
            executor.setCorePoolSize(5);
            executor.setMaxPoolSize(10);
            executor.setQueueCapacity(25);
            return executor;
        }

    }

上記の数値はランダムに見えるので、環境に基づいて正しく設定する方法を理解したいと思います。私が持っている次の制約について概説します。

  1. アプリケーションは2コアCPUボックスで実行されます
  2. エグゼキュータは、通常1〜2秒で完了するタスクを実行します。
  3. 通常、800/minのタスクが実行プログラムに送信され、2500/minで急上昇することを期待しています
  4. タスクはいくつかのオブジェクトを構築し、Google pubsubへのHTTP呼び出しを行います。

理想的には、他に検討する必要がある制約を理解し、それらに基づいて、プールとキューサイズの適切な構成を知りたいです。

12
Anton Belev

更新:この回答は何年にもわたって数票を獲得したので、私の奇妙なメタファーを読む時間がない人のために短縮バージョンを追加しています:

TL; DR回答:

実際の制約は、(論理)CPUコアが同時に実行できるのは単一のスレッドのみであることです。したがって:

  • コアの数:CPUの論理コア数* 1 /(ratio_of_time_your_thread_is_runnable_when_doing_your_task

したがって、マシンに8つの論理コアがある場合は、8つのスレッドをthreadPoolに安全に配置できます(使用できる他のスレッドを除外することを忘れないでください)。次に、さらに追加できるかどうかを自問する必要があります。スレッドプールで実行する予定のタスクの種類をベンチマークする必要があります。スレッドが平均50%の時間しか実行されていないことに気付いた場合、CPUはその時間の50%で自由に別のスレッドで作業し、さらにスレッドを追加できます。

  • キューサイズ:待機できる数だけ。

キューのサイズは、スレッドプールが拒否する前に受け入れるアイテムの数です。ビジネスロジックです。それはあなたが期待する振る舞いに依存します:10億のタスクを受け入れるポイントはありますか?いつタオルを投げますか? 1つのタスクが完了するまでに1秒かかり、スレッドが10個ある場合、キュー内の10,000番目のタスクが1000秒で完了すると期待されます。それは受け入れられますか?最悪の事態は、クライアントがタイムアウトして、最初のタスクを完了する前に同じタスクを再送信することです。

元のELI12回答:

それは最も正確な答えではないかもしれませんが、私は試してみます:

簡単なアプローチは、2コアCPUが同時に2つのスレッドでのみ動作することを認識することです。

比較的最近のIntel CPUがあり、ハイパースレッディングがある場合(別名[〜#〜] ht [〜#〜](TM)、[ 〜#〜] htt [〜#〜](TM)、[〜#〜] smt [〜#〜])が(BIOSの設定により)オンになっていると、オペレーティングシステムにCPU内の物理コアの数の2倍の使用可能なコアの数。

どちらの方法でも、Javaから、操作できるコアの数(または他のスレッドを同時に横取りしない)を検出するには、int cores = Runtime.getRuntime().availableProcessors();を呼び出すだけです。

アプリケーションをWorkshop(実際のもの)として表示しようとすると、次のようになります。

  • プロセッサーは従業員によって表されます。製品に付加価値を与えるのは物理的な単位です。
  • タスクは原材料の塊になります(およびいくつかの指示リスト)
  • あなたのスレッドは、従業員がタスクを実行して作業できる机です。
  • キューサイズは、原材料をデスクに運ぶコンベヤーベルトの長さです。

したがって、あなたの質問は「従業員の数が不変である場合、どのくらいの数のデスクと私のコンベヤーベルトを工場内に置くことができるかをどのように選択できますか? 」.

デスク(スレッド)の数:

従業員は一度に1つのデスクでのみ作業でき、デスクごとに1人の従業員しか持てません。したがって、基本的なセットアップでは、少なくとも従業員と同じ数のデスクを配置します(従業員(プロセッサ)が仕事をする可能性がないまま取り残されるのを避けるため)。

しかし、あなたの活動に応じて、従業員1人あたりのデスクを増やすことができます。

従業員が常にエンベロープ内にメールを置くことが予想される場合、十分な注意が必要な操作(プログラミングでは:コレクションの並べ替え、オブジェクトの作成、カウンターの増加) 、より多くのデスクを持っていることは役に立ちません、そしてあなたの従業員がいつかデスクを変更しなければならないので有害でさえあるかもしれません(switching context、それは少し時間がかかります) 、したがって、彼らが作業していた方を残して、もう一方の作業を進めます。

しかし、あなたの仕事が陶器を作ることであり、粘土がオーブンで調理されるのを待つ従業員に依存している場合(理解ファイルシステム、Webサービスなどの外部リソースへのアクセスを取得する)、あなたの従業員は別の机に粘土をモデル化して、最初の机に後で戻る余裕があります。

したがって、タスクに十分な大きさのアクティブな作業/待機比率(running/waiting)がある限り、従業員あたりのデスクを増やすことができます。そして、デスクの数は、待機時間中に従業員がいくつのタスクを進めることができるかということです。

コンベヤベルトの場合(queue)サイズパーツ:

キューサイズは、(例外をスローすることによって)これ以上のタスクの拒否を開始する前にキューに入れることを許可するアイテムの数を表します。したがって、「OK、私はすでにオーバーブッキングされており、これからは発生しません。遵守できる」

まず、あなたのコンベヤーベルトはワークショップの中に収まる必要があると思います。コレクションがメモリ不足エラーを防ぐために十分に小さくなければならないことを意味します(明らかに)。

その後、それはあなたの会社の方針に基づいています。クライアントが注文するたびにタスクがベルトに追加されると仮定しましょう(別のサービスがAPIを呼び出します)。呼び出し側が実行の遵守に十分な時間をかけて十分に信頼していることを気にしていない場合、ベルトのサイズを制限しても意味がありません。

しかし、最初の注文が失われ、最初の注文があったかどうかを確認する必要がないと仮定して、1か月間陶器を待った後にクライアントが不快になり、別の陶器を同時注文または再注文することを期待できる場合完了...最初の注文は無料で行われ、支払いは行われません。クライアントが対応するのが遅すぎるときに別の注文をすると、すべての新しい注文が遅くなるため、フィードバックループに入ります。プロセス全体。

したがって、その場合は、クライアントに「申し訳ありませんが、オーバーブッキングされています。現在、新しい注文を行うことはできません。許容可能な時間範囲内で対応することができないため」という看板を立てる必要があります。

その場合、キューのサイズは次のようになります:許容時間範囲/タスクを完了する時間

具体的な例:クライアントサービスが、送信するタスクが100秒未満で完了する必要があると予想し、すべてのタスクに1〜2秒かかることがわかっている場合、キューを50〜100タスクに制限する必要があります。タスクがキューで待機している場合、次のタスクが100秒未満で完了しないことが確実であるため、サービスが何も待機しないようにタスクを拒否します。

12
Jeremy Grand