web-dev-qa-db-ja.com

制約付きのジョブキューを最適に設計するにはどうすればよいですか?

次の状況を考慮してください。

  • 処理する必要のある多数の「ジョブ」を作成し、それらをキューに入れるプログラムがあります。
  • 他のワーカープログラムで次の「ジョブ」を取得して、そのジョブを処理できるようにします。
  • 各ジョブにはカテゴリがあります。
  • カテゴリはいくつあってもかまいません。
  • 同じカテゴリの2つのジョブを別々のワーカーで同時に処理することはできません。
  • ワーカーは一度に1つのジョブを処理できます。

同じカテゴリの複数のジョブが同時に処理される可能性があるため、この状況では従来のキューは機能しませんが、これは許可されていません。

取得するジョブをワーカーに確認させ、そのジョブカテゴリに現在処理中の別のワーカーがあるかどうかを確認し、その場合は後で処理するためにジョブをキューに再送信します。これは、この問題を解決する非効率的な方法のようです。この問題を解決できるデータ構造または設計パターンはありますか?

さらに説明が必要な場合は、お知らせください。

9
merp

この問題には2つの部分があります。

1つ:可能なカテゴリの不明なリスト。

2つ:同じカテゴリの2つのジョブが同時に処理されるのを防ぐための、ワーカー間のプロセス間通信。

カテゴリの既知のリストがある場合は、カテゴリごとに1つのキューと1つのワーカーを持つことができます。

カテゴリが不明な場合でも、カテゴリごとにキューを設定できますが、カテゴリごとにキューワーカーがある場合は、すべてのキューを監視し、新しいカテゴリが表示されたときに新しいワーカーを起動する必要があります。

これは、仕事を配る「マスター」ワーカーで達成できます

すべてのジョブは「マスター」キューに入れられます。

カテゴリワーカーはプライベートキューを作成し、作業に使用できるようにマスターに登録します。

マスターワーカーはジョブを取得し、カテゴリをチェックし、利用可能なワーカーをチェックし、プライベートキューに入れることにより、ジョブの1つに割り当てます。

マスターは、ワーカーに割り当てられたカテゴリを追跡できます。

マスターは次のジョブをピックアップしますが、同じカテゴリであり、ワーカーはまだビジーであるため、ジョブをカテゴリ固有の保留キューに入れます

マスターは次のジョブを取得します。その新しいカテゴリーは、別のカテゴリーワーカーに割り当てます。

カテゴリーワーカーはジョブを完了し、作業に再登録します

マスターは保留キューとマスターキューの次のジョブをチェックし、使用可能なカテゴリワーカーに割り当てます。

カテゴリワーカーがジョブ中にクラッシュした場合、再登録されません。そのため、マスターは、タイムアウトロジックを使用して、あきらめて、別のワーカーにカテゴリを割り当て始めることができます。

また、常にマスターワーカーが1人だけになるように注意する必要があります。これは、何らかのマスターキューの排他ロックを必要とします

3
Ewan

あなたの非効率的な提案の欠点は、カテゴリに2つのジョブがある場合です。今、一人は働いています。そして、他の皆は忙しい待機をしています。

これを十分に行うには、ワーカーにキューをスキャンして次の実行可能なタスクをスキャンさせ、見つかった場合はそれ以外のすべてをキューに返します。代わりにすべてを返し、次にスリープします。スリープにランダム性と指数バックオフがある場合、「ビジー待機」はビジーではありません。

より効率的なアプローチのために、仕事をつかむ労働者は、そのカテゴリーから他に何もすることがなくなるまで、そのカテゴリーを行う責任があります。次に、正規労働者に戻ります。しかし、いくつかの微妙な点があります。

これらを確認するために、tryおよびreleaseロック(両方とも非ブロッキング)が可能であり、キュー操作がaddgetおよび_is_empty_ getはブロックおよび待機操作です。

一般的なキューを想定し、次に各カテゴリに対してキューとロックを想定します。

基本的なワーカーフローは次のとおりです。

_while obj = get from main queue:
    if try category lock:
        do obj job
        do_whole_category_queue()
    else:
        add obj to category queue
        if try category lock:
            do_whole_category_queue()
_

どこ

_procedure do_whole_category_queue
    while not category queue is_empty:
        obj = get from category queue
        do obj job
    release category lock
    if not is_empty category queue:
        if try category lock:
            do_whole_category_queue()
_

ここでの注意深いロックハンドシェイクに注意してください。ワーカーはロックをテストし、ロックされている場合はジョブをキューに追加します。ただし、ロックを再度テストして、実際にジョブを実行するのがまだ他の誰かの責任であることを確認する必要があります。キューの操作中にカテゴリワーカーが終了した場合に備えて。

(これは、人々がしばしば失敗する、ロックの詳細の一種です。エラーは確実に再現することは不可能ですが、本番環境では無作為かつ不可解にねじ込まれます...)

2
btilly