メッセージ処理ノードおよびパーサーは、 マルチインスタンス、マルチスレッド化環境で作業することが必要です。 それぞれが多くの構文エレメントを持つ、 多くのノード・オブジェクトまたはパーサー・オブジェクトがあり、 これらのオブジェクト上にメソッドを実行する多くのスレッドがあり得ます。 メッセージ・ブローカー設計では、メッセージ・オブジェクトとそれが所有するその他のオブジェクトが、 メッセージ・フローを介してメッセージを受け取り、処理する スレッドによってのみ使用されるようにします。
メッセージ・フロー処理ノードのインスタンスは、 ノードが定義されるメッセージ・フローを保守するすべてのスレッドによって共有および使用されます。 パーサーの場合、パーサーのインスタンスは 1 つのメッセージ・フロー・スレッドによってしか使用されません。
ユーザー定義拡張機能はこのモデルを固守し、セマフォーがスレッド間で アクセスを逐次化する必要のあるグローバル・データやリソースの使用を避けるべきです。 そのような逐次化は、パフォーマンスのボトルネックの原因となることがあります。
ユーザー定義拡張機能のインプリメンテーション関数は再入可能でなければならず、 それらが呼び出すその他の関数も再入可能であることが必要です。 すべてのユーザー定義拡張機能のユーティリティー関数は、完全に再入可能です。
ユーザー定義拡張機能は、必要であれば追加のスレッドを生み出すことができますが、 インプリメンテーション関数の完了時に、 同じ スレッドがブローカーに制御を戻すことが不可欠です。 これに失敗すると、ブローカーの保全性に妥協が生じ、予測不能な結果になります。
次のメッセージ・フローの例は、独自のユーザー定義ノードを設計およびインプリメントする際に 意識すべきスレッド化の考慮事項を理解するのに役立ちます。 ここでは、ユーザー定義入力ノードの例を考慮します。
メッセージ・フローは、スレッドの集合で実行するように構成できます。 これはメッセージ・フロー中の入力ノード数、 およびメッセージ・フローの additionalInstances プロパティーの値によって決定されます。 これらの 2 つのエレメントが、メッセージ・フローが使用できるスレッド・プールの最大容量を決定します。 したがってメッセージ・フローに、単一スレッド化実行を指示する特定の処理要件がある場合、 これがそのケースであることを確認する必要があります。
additionalInstances プロパティーを使用して個別のスレッドでブローカーがメッセージ・フローの追加のコピーを 開始するのは、入力キューがボトルネックになるのを防ぐ最も有効な方法です。 しかし、別々のスレッドを作成すると、メッセージ・キューからのメッセージの並列処理が実行されるので、 このプロパティーは、処理されるオーダー・メッセージが重要でない場合のみに使用を制限するべきです。
スレッドは、入力ノード構成および操作の結果として作成されます。 スレッドはスレッド・プールでアクティブまたはアイドル状態になり、 アイドル状態のスレッドは入力ノードによってディスパッチされるか、 またはブローカーがシャットダウンされるまでプールに残ります。
下記の図は、メッセージ・フローのスレッド割り振りを例示しています。
メッセージ・フローでのスレッド割り振り
最初にスレッド 1 が要求され (A)、メッセージを待機します。 メッセージが受け取られると (B)、スレッド 1 がメッセージを伝搬し、スレッド 2 をディスパッチします。 スレッド 2 は即座にメッセージを受け取り (C)、メッセージを待機するスレッド 3 を伝搬およびディスパッチします (D)。 スレッド 1 が完了し (E)、スレッド・プールに戻ります。 次にスレッド 3 がメッセージを受け取り (F)、スレッド 1 をディスパッチしてメッセージを伝搬します。 ここでスレッド 1 がメッセージがキューに到着するのを待機します (G)。
H のマークには意味がありません。メッセージ・フローのこのインスタンスでは、 スレッド 1 がメッセージを獲得しますが、その時点で他のすべてのスレッドはアクティブなので、 ディスパッチはしません。 メッセージが伝搬されます。
このメッセージが伝搬された後、スレッド 2 が完了し (I)、 キューから新しいメッセージを受け取って、この新規メッセージを伝搬します。 その後スレッド 3 が完了し (J)、プールに戻ります。 次いでスレッド 2 も完了します (K)。 これはスレッド 1 の完了時にディスパッチしなかったので (L)、 キュー上にメッセージがなくてもスレッド・プールに戻ることはできず、 メッセージがキューに到着するのを待機します。