フォールトトレランスを向上させ、システム全体の負荷を軽減するには、中央サーバーを使用せずにデータを交換する別の方法があります。
私が紹介したい実装の例。
ちょっとした用語:メッセージバス(メッセージバス)、メッセージブローカー(メッセージブローカー)-これらはすべて、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層にあるため考慮しません)。
このアプローチの次の利点を区別できます。
- 複数のノードでの負荷分散。
- 耐障害性。 システムは、少なくとも1人の送信者と1人の受信者がいる限り機能します。
- 潜在的に高速。
以下の欠点に注意することができます。
- 集中管理の欠如;
- 保証された配信を提供することはほとんど不可能です。
- ITビジネスにおけるこのようなシステムの普及率が低く、標準が存在しない。
UDPは接続を必要としないため、実装によく使用されます。 また、UDPマルチキャスト(以下、単にマルチキャスト)を使用すると、PUB / SUBパターンを非常に簡単に実装できます。 発行ノード(PUB)が指定されたトピック(トピック)のデータをサブスクライブノード(SUB)に発行/配布するとき。 このテクノロジーを使用して、MICEXは交換データ配信(FIX FAST)および他の多くのシステムと連携します。
そのようなシステムの実装を検討してください。 要件は次のとおりです。
- PUB / SUBテンプレートの実装。
- 主な目的は、小さい(最大1KB)メッセージのある警告システムです。
- システムは、受信者に関係なく、中央サーバーなしで動作するはずです。
- メインOSはLinux 2.6以降です。
開始するには、最も簡単なオプションを選択してください。 1つのマルチキャストアドレスを使用して、トピック名を示すメッセージをすべてのサブスクライバーに送信します。 サブスクライバーは、サブスクリプションの個々のセットに従ってデータをフィルターする必要があります。
UDPパケットの内容を定義します。
- トピック名。
- データ。
加入者アルゴリズムは次のように説明できます。
- マルチキャストグループに接続します。
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に進みます。
- メッセージを処理します。
- ポイント2に戻ります。
出版社の仕事はさらに簡単です:
- メッセージにトピック名を追加します。
- マルチキャストアドレスにメッセージを送信します。
このアルゴリズムはシンプルで機能しますが、不快な瞬間があります。大量のトラフィックが存在すると、大量の無駄なデータがノードに送られ、ノードはそれらをドロップする前に処理を強制されます。
トピックに異なるマルチキャストアドレスを割り当てることにより、受信者の負担を軽減します。 グループを計算するには、CRC-32などのハッシュ変換を使用して、必要なIPアドレスを取得します。
サブスクライバーアルゴリズム:
- 関心のあるトピックから値のハッシュを計算します。
unsigned int addr_value = 4009754625 + (crc32_hash(subject) % 16777215);
- 受信したマルチキャストアドレスに接続します。 それらを操作する機能は、 このスレッドで詳しく説明されています 。
- メッセージを受信します。
- メッセージのトピックが興味のあるリストにない場合は、ステップ3に進みます。
- メッセージを処理します。
- 手順3に戻ります。
出版社:
- メッセージにトピック名を追加します。
- トピックハッシュを計算します。
- 受信したマルチキャストアドレスにメッセージを送信します。
使用可能なマルチキャストグループの範囲は16777214アドレスであるため、ハッシュ関数が十分に一致する場合、3,300万の異なるトピックに対して約2つの一致があります。
Linuxはマルチキャストグループごとに1つのソケットしか正しく使用できないため、epollを使用してデータを取得することをお勧めします。
その結果、特定の受信者アドレスを気にせずに名前でデータをトピックに送信できる分散メッセージシステムが実現し、さらに拡張するための膨大な能力を備えています。 追加の利点は、アプリケーションが特別なライブラリを必要としないという事実であり、メッセージを送信するだけのデバイスでは、ライブラリをマイクロコントローラに移植することさえできます(ネットワークスタックがある場合)。
実装とソースコードはここにあります 。
PS
私はロシア語の母国語が大好きですが、英語を絶えず使用しているため、テキストに問題があるかもしれません。 あなたが間違いに気付いた場合、あなたが個人的なメッセージでそれについて私に書いてくれたら私は非常に感謝します。