Consumer / ProducerとObserver / Observableの違い


15

私は、次の3つの部分で構成されるアプリケーションの設計に取り組んでいます。

  • 特定のイベントの発生(ファイルの作成、外部リクエストなど)を監視する単一のスレッド
  • これらのイベントを処理することでこれらのイベントに応答するN個のワーカースレッド(各ワーカーは単一のイベントを処理して消費し、処理に時間がかかる場合があります)
  • これらのスレッドを管理し、エラー処理を行うコントローラー(スレッドの再起動、結果のログ記録)

これは非常に基本的で実装するのは難しくありませんが、それを行うための「正しい」方法は何だろうと思っています(この具体的なケースではJavaですが、より高い抽象化の答えもありがたいです)。2つの戦略が思い浮かびます。

  • Observer / Observable:監視スレッドはコントローラーによって監視されます。イベントが発生した場合、コントローラーに通知され、再利用可能なキャッシュスレッドプールから新しいタスクを空きスレッドに割り当てることができます(または、すべてのスレッドが現在ビジーである場合、FIFOキューでタスクを待機してキャッシュします)。ワーカースレッドはCallableを実装し、結果(またはブール値)で成功を返すか、エラーを返します。その場合、コントローラーは何をすべきかを決定します(発生したエラーの性質に応じて)。

  • プロデューサー/コンシューマー:監視スレッドはコントローラーとBlockingQueueを共有し(イベントキュー)、コントローラーはすべてのワーカーと2つを共有します(タスクキューと結果キュー)。イベントの場合、監視スレッドはタスクオブジェクトをイベントキューに入れます。コントローラーは、イベントキューから新しいタスクを取得し、それらをレビューして、タスクキューに入れます。各ワーカーは新しいタスクを待機し、タスクキュー(先着順、キュー自体で管理)からそれらを取得/消費し、結果またはエラーを結果キューに戻します。最後に、コントローラーは結果キューから結果を取得し、エラーが発生した場合に対応する手順を実行できます。

両方のアプローチの最終結果は似ていますが、それぞれわずかな違いがあります。

オブザーバーを使用すると、スレッドの制御は直接行われ、各タスクは特定の新しく生成されたワーカーに割り当てられます。スレッド作成のオーバーヘッドは高くなる可能性がありますが、キャッシュされたスレッドプールのおかげではありません。一方、Observerパターンは、複数ではなく単一のObserverに縮小されます。これは、厳密に設計されたものではありません。

キュー戦略は拡張が容易なようです。たとえば、1つではなく複数のプロデューサーを追加するのは簡単で、変更を必要としません。欠点は、作業をまったく行わない場合でも、すべてのスレッドが無期限に実行され、エラー/結果の処理が最初のソリューションほど洗練されていないことです。

この状況で最もふさわしいアプローチは何ですか?その理由は?ほとんどの例は、Observerケースの新しい値で多くのウィンドウを更新したり、複数のコンシューマーとプロデューサーで処理したりするなど、明確なケースのみを扱っているため、この質問に対する答えをオンラインで見つけるのは難しいと感じました。どんな入力でも大歓迎です。

回答:


10

あなたはあなた自身の質問に答えることにかなり近づいています。:)

Observable / Observerパターン(反転に注意)では、3つの点に留意する必要があります。

  1. 通常、変更の通知、つまり「ペイロード」は監視可能です。
  2. オブザーバブルが存在します。
  3. オブザーバーは、既存のオブザーバブルに知られている必要があります(そうでない場合、オブザーバーには何も監視されていません)。

これらの点を組み合わせることで、暗示されるのは、オブザーバブルがその下流のコンポーネント、つまりオブザーバーが何であるかを知っていることです。データフローは本質的に観測可能なものから駆動されます。観測者は、観測対象によって単に「生きて死ぬ」だけです。

生産者/消費者パターンでは、非常に異なる相互作用が得られます。

  1. 一般に、ペイロードは、その作成を担当するプロデューサーとは無関係に存在します。
  2. 生産者は消費者がいつどのように活動するかを知りません
  3. 消費者はペイロードの生産者を知る必要はありません。

現在、プロデューサーとコンシューマーの間のデータフローは完全に切断されています。プロデューサーが知っているのは出力があり、コンシューマーが知っているのは入力があることだけです。重要なことは、これは生産者と消費者が他者の存在なしに完全に存在できることを意味します。

別のそれほど微妙な違いは、同じオブザーバブル上の複数のオブザーバーが通常同じペイロードを取得することです(型にはまらない実装がない限り)が、同じプロデューサーの複数のコンシューマーはそうではない場合があります。これは、仲介がキューのようなアプローチかトピックのようなアプローチかによって異なります。前者は消費者ごとに異なるメッセージを渡し、後者はすべての消費者がメッセージごとに処理することを保証(または試行)します。

それらをアプリケーションに適合させるには:

  • Observable / Observerパターンでは、監視スレッドが初期化されるたびに、コントローラーに通知する方法を知っている必要があります。オブザーバーとして、コントローラーは、スレッドに変更を処理させる前に、監視スレッドからの通知を待っている可能性があります。
  • Producer / Consumerパターンでは、監視スレッドはイベントキューの存在のみを知る必要があり、それだけで対話します。コンシューマーとして、コントローラーはイベントキューをポーリングし、新しいペイロードを取得すると、スレッドがそれを処理できるようにします。

したがって、質問に直接答えるには、監視スレッドとコントローラーをある程度独立させて、独立して操作できるようにしたい場合は、Producer / Consumerパターンに向かう必要があります。


2
詳細な回答ありがとうございます。残念ながら、評判が失われているため、それを支持することはできません。そのため、代わりにソリューションとしてマークしました。あなたが言及した両方の部分の間の一時的な独立は、私が今まで考えていなかったポジティブなものです。キューは、イベントが観察された後の直接的なアクションよりも、多くのイベントの短いバーストを管理できます(最大スレッド数が固定され、比較的少ない場合)。スレッド数は、現在のキュー項目数に応じて動的に増減することもできます。
user183536

@ user183536問題なく、喜んでお手伝いします!:)
hjk
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.