分散型メッセージングシステム

IT開発の世界は渦巻いています。 UNIXの創設者は、多くのプログラムがあると信じていましたが、それぞれがそのタスクを完璧に実行します。 2000年代初頭の主な傾向は、可能なすべてを実行するプログラムを組み合わせることでした。 現在、開発の方向のベクトルは反対方向に動き始めています。 また、以前の場合、システムがますます分散化され、特殊な統合コンプレックス(メッセージバスまたはメッセージブローカー)がノード間のデータ転送に従事しているという事実により、標準入出力ストリームは主にデータ交換に使用されていました。

フォールトトレランスを向上させ、システム全体の負荷を軽減するには、中央サーバーを使用せずにデータを交換する別の方法があります。

私が紹介したい実装の例。

ちょっとした用語:メッセージバス(メッセージバス)、メッセージブローカー(メッセージブローカー)-これらはすべて、1つのノードから別のノードへデータを受信、処理、転送するソフトウェアパッケージを示す類似の(ただし、まったく同じではない)概念です。
サブスクライバ-合意されたプロトコルに従ってメッセージを送信または受信するアプリケーション。

中央ノードを備えたシステム(master-slave、master-masterという形式の冗長性を備えたシステムを含む)について簡単に説明します。
典型的なエンタープライズシステム:TibcoEMS、IBM MQ、JBossなど。 オープンソースシステムから:RabbitMQ、Apache ActiveMQ、Apollo、Redis。 クラウドサービスもあります:IronMQ。 最も一般的に使用されるプロトコル:AMQP、STOMP。
基本的な考え方は、サブスクライバーが、接続されたクライアント間でメッセージをルーティングする共通サーバー(サーバークラスター)に接続することです。

利点:


それにもかかわらず、多くの欠点があります。


データ損失のコストは、より強力なハードウェアを購入するコストよりもはるかに高いため、すべての欠点にもかかわらず、「大」企業はこの特定のタイプのメッセージブローカーを使用します。

それでも、多くのタスクでは、保証された配信は必要ありません。モノのインターネット、信頼できるデータ伝送を独立して提供するシステム、許容可能なデータ損失を伴う高負荷システムです。 このような場合、上記のソリューションの機能は冗長であり、パフォーマンスの問題を解決することはできません。

別のアプローチは、ブローカーなしでデータを交換することです(eng。Broker-less)。 通常、このようなアーキテクチャには、専用ライブラリおよび/または加入者のサイトに追加のソフトウェアが必要です。
私の知る限り、企業セグメントから見ると、TIBCO Rendezvousの製品は1つしかありません(だれかが代替案をアドバイスしてくれたら、とても感謝しています)。
非営利システムからは、中央サーバーを必要としないZeroMQを指定できます。 ただし、このライブラリはネットワーク上の抽象化を提供せず、多くの場合、独自の中央集中型システムの作成につながり、分散化の概念全体を無効にします。
分散型アーキテクチャの基本的な考え方は、P2Pの考え方に似ています。つまり、サブスクライバは、共通の調整サーバーを使用せずに他のサブスクライバにデータを転送します。 (DHCP、DNSなどは、異なるOSI層にあるため考慮しません)。

このアプローチの次の利点を区別できます。


以下の欠点に注意することができます。


UDPは接続を必要としないため、実装によく使用されます。 また、UDPマルチキャスト(以下、単にマルチキャスト)を使用すると、PUB / SUBパターンを非常に簡単に実装できます。 発行ノード(PUB)が指定されたトピック(トピック)のデータをサブスクライブノード(SUB)に発行/配布するとき。 このテクノロジーを使用して、MICEXは交換データ配信(FIX FAST)および他の多くのシステムと連携します。

そのようなシステムの実装を検討してください。 要件は次のとおりです。



開始するには、最も簡単なオプションを選択してください。 1つのマルチキャストアドレスを使用して、トピック名を示すメッセージをすべてのサブスクライバーに送信します。 サブスクライバーは、サブスクリプションの個々のセットに従ってデータをフィルターする必要があります。

UDPパケットの内容を定義します。


加入者アルゴリズムは次のように説明できます。

  1. マルチキャストグループに接続します。

    struct ip_mreq mreq; struct sockaddr_in sin; sin.sin_family = AF_INET; sin.sin_port = htons(PORT); sin.sin_addr.s_addr = ADDR; mreq.imr_multiaddr = addr; mreq.imr_interface.s_addr = htonl(INADDR_ANY); setsockopt(fd, SOL_SOCKET, SO_REUSEPORT, &optval, sizeof (optval)); if (bind(fd, (struct sockaddr *) &sin, sizeof (struct sockaddr_in)) < 0) { perror("Bind"); return -1; } if (setsockopt(fd, IPPROTO_IP, IP_ADD_MEMBERSHIP, &mreq, sizeof (mreq)) < 0) { perror("Join"); return -2;; } 

  2. メッセージを受信します。
  3. メッセージトピックが目的のリストにない場合は、手順2に進みます。
  4. メッセージを処理します。
  5. ポイント2に戻ります。

出版社の仕事はさらに簡単です:

  1. メッセージにトピック名を追加します。
  2. マルチキャストアドレスにメッセージを送信します。

このアルゴリズムはシンプルで機能しますが、不快な瞬間があります。大量のトラフィックが存在すると、大量の無駄なデータがノードに送られ、ノードはそれらをドロップする前に処理を強制されます。

トピックに異なるマルチキャストアドレスを割り当てることにより、受信者の負担を軽減します。 グループを計算するには、CRC-32などのハッシュ変換を使用して、必要なIPアドレスを取得します。

サブスクライバーアルゴリズム:

  1. 関心のあるトピックから値のハッシュを計算します。

      unsigned int addr_value = 4009754625 + (crc32_hash(subject) % 16777215); 

  2. 受信したマルチキャストアドレスに接続します。 それらを操作する機能は、 このスレッドで詳しく説明されています
  3. メッセージを受信します。
  4. メッセージのトピックが興味のあるリストにない場合は、ステップ3に進みます。
  5. メッセージを処理します。
  6. 手順3に戻ります。

出版社:

  1. メッセージにトピック名を追加します。
  2. トピックハッシュを計算します。
  3. 受信したマルチキャストアドレスにメッセージを送信します。


使用可能なマルチキャストグループの範囲は16777214アドレスであるため、ハッシュ関数が十分に一致する場合、3,300万の異なるトピックに対して約2つの一致があります。
Linuxはマルチキャストグループごとに1つのソケットしか正しく使用できないため、epollを使用してデータを取得することをお勧めします。

その結果、特定の受信者アドレスを気にせずに名前でデータをトピックに送信できる分散メッセージシステムが実現し、さらに拡張するための膨大な能力を備えています。 追加の利点は、アプリケーションが特別なライブラリを必要としないという事実であり、メッセージを送信するだけのデバイスでは、ライブラリをマイクロコントローラに移植することさえできます(ネットワークスタックがある場合)。

実装とソースコードはここにあります

PS

私はロシア語の母国語が大好きですが、英語を絶えず使用しているため、テキストに問題があるかもしれません。 あなたが間違いに気付いた場合、あなたが個人的なメッセージでそれについて私に書いてくれたら私は非常に感謝します。

Source: https://habr.com/ru/post/J240053/


All Articles