NServiceBus を使用して処理パイプラインを構築していますが、プロセスの各ステップをスケーラブルにするためにディストリビューターの構成に問題があります。ここにいくつかの情報があります:
- パイプラインには、WorkItem の「OK、開始時間です」というマスター プロセスがあり、フローチャートのようなプロセスが開始されます。
- フローチャートの各ステップは計算コストがかかる可能性があるため、各ステップをスケールアウトできる機能が必要です。これは、各ステップに Distributor が必要であることを示しています。
- 後で追加のアクティビティをイベントにフックできるようにしたいと考えています。これは、Send() ではなく、完了時にメッセージを Publish() する必要があることを示しています。
- プロセスは、条件に基づいて分岐する必要がある場合があります。これは、プロセスが複数の種類のメッセージを発行できる必要があることを示しています。
- プロセスがフォークに参加する必要がある場合があります。これにはSagasを使用する必要があると思います。
これらの仮定が適切であることを願っています。
簡単にするために、分岐や結合については忘れて、ステップ A の後にステップ B が続き、ステップ C で終わる単純なパイプラインを考えてみましょう。各ステップは独自のディストリビューターを取得し、メッセージを処理する多くのノードを持つことができます。
- NodeA ワーカーには IHandleMessages プロセッサが含まれており、EventA を発行します
- NodeB ワーカーには IHandleMessages プロセッサが含まれており、イベント B を発行します
- NodeC ワーカーには IHandleMessages プロセッサが含まれており、パイプラインは完了しています。
構成ファイルの関連部分を次に示します。# はワーカーの番号を示します (つまり、入力キュー NodeA.1 と NodeA.2 があります)。
NodeA:
<MsmqTransportConfig InputQueue="NodeA.#" ErrorQueue="error" NumberOfWorkerThreads="1" MaxRetries="5" />
<UnicastBusConfig DistributorControlAddress="NodeA.Distrib.Control" DistributorDataAddress="NodeA.Distrib.Data" >
<MessageEndpointMappings>
</MessageEndpointMappings>
</UnicastBusConfig>
NodeB:
<MsmqTransportConfig InputQueue="NodeB.#" ErrorQueue="error" NumberOfWorkerThreads="1" MaxRetries="5" />
<UnicastBusConfig DistributorControlAddress="NodeB.Distrib.Control" DistributorDataAddress="NodeB.Distrib.Data" >
<MessageEndpointMappings>
<add Messages="Messages.EventA, Messages" Endpoint="NodeA.Distrib.Data" />
</MessageEndpointMappings>
</UnicastBusConfig>
NodeC:
<MsmqTransportConfig InputQueue="NodeC.#" ErrorQueue="error" NumberOfWorkerThreads="1" MaxRetries="5" />
<UnicastBusConfig DistributorControlAddress="NodeC.Distrib.Control" DistributorDataAddress="NodeC.Distrib.Data" >
<MessageEndpointMappings>
<add Messages="Messages.EventB, Messages" Endpoint="NodeB.Distrib.Data" />
</MessageEndpointMappings>
</UnicastBusConfig>
そして、ディストリビューター構成の関連部分は次のとおりです。
Distributor A:
<add key="DataInputQueue" value="NodeA.Distrib.Data"/>
<add key="ControlInputQueue" value="NodeA.Distrib.Control"/>
<add key="StorageQueue" value="NodeA.Distrib.Storage"/>
Distributor B:
<add key="DataInputQueue" value="NodeB.Distrib.Data"/>
<add key="ControlInputQueue" value="NodeB.Distrib.Control"/>
<add key="StorageQueue" value="NodeB.Distrib.Storage"/>
Distributor C:
<add key="DataInputQueue" value="NodeC.Distrib.Data"/>
<add key="ControlInputQueue" value="NodeC.Distrib.Control"/>
<add key="StorageQueue" value="NodeC.Distrib.Storage"/>
各ノードの 2 つのインスタンスを使用してテストしていますが、ノード B の途中で問題が発生しているようです。基本的に次の 2 つのことが発生する可能性があります。
- ノード B の両方のインスタンスは、それが EventA をサブスクライブしていること、および NodeC.Distrib.Data@MYCOMPUTER がノード B がパブリッシュする EventB をサブスクライブしていることを報告します。この場合、すべてがうまく機能します。
- ノード B の両方のインスタンスは、EventA をサブスクライブしていると報告しますが、一方のワーカーは NodeC.Distrib.Data@MYCOMPUTER が 2 回サブスクライブしていると言い、もう一方のワーカーはそれについて言及しません。
ディストリビューターがサブスクリプション メッセージをルーティングする方法によってのみ制御されるように見える 2 番目のケースでは、「オーバーアチーバー」ノードが EventA を処理する場合、すべてがうまくいきます。「未達成者」が EventA を処理すると、EventB のパブリッシュにはサブスクライバーがなく、ワークフローは停止します。
だから、私の質問:
- このような設定は可能ですか?
- 構成は正しいですか? 単純な 1 レベルのパブリッシャー/2 ワーカーのセットアップを超えて、ディストリビューターを使用した構成の例を見つけるのは困難です。
- 計算集約型ではないすべてのトラフィック監視操作を実行し、タスクが長時間実行され、負荷分散が必要な場合にのみ、ディストリビューターの背後にあるプロセスにメッセージを送信する 1 つの中央ブローカー プロセスを持つ方が理にかなっていますか?
- そうすれば、負荷分散されたノードは単純に中央ブローカーに返信することができます。これは簡単に思えます。
- 一方で、それはNServiceBusの強みである分散化とは相反するように思えます。
- そして、これが答えであり、長期実行プロセスの完了イベントが応答である場合、発行されたイベントで後で拡張できるようにする発行をどのように保持しますか?