単一プロセスで動く WebSocket サーバーは簡単です。しかし、ロードバランサーの背後に 2 つ以上のサーバーインスタンスを置いた瞬間、何かが静かに壊れます。各クライアントはちょうど1 つのノードにしか接続しないため、ノード A で publish されたメッセージはノード B にいるサブスクライバーには決して届きません。チャネル、ブロードキャスト、そして Presence のすべてが半分見えなくなります。これは、チームが WebSocket ライブラリの限界に達して別の手段へ移行する、最も一般的な理由です。
新しい TsgcWSCluster コンポーネントは、そのギャップを埋めます。サーバーの隣に配置し、ノード同士を互いに向け合わせるだけで、いずれかのノードでの publish や Presence イベントがすべてのノードに接続しているサブスクライバーに届き、既存の Publish / チャネル / Presence のコードはそのまま変更不要です。この記事では、2 つのバックプレーンエンジン、設定方法、そしてそのまま貼り付けて使える Delphi と .NET のサンプルを順に見ていきます。
2 つのバックプレーンエンジン
クラスタリングは、すべてのノードが接続するバックプレーンを介して機能します。クライアントがあるノードで publish すると、そのノードはメッセージを自身のローカルサブスクライバーに配信し、バックプレーンへ転送します。他のすべてのノードがそれを受け取り、自身のローカルサブスクライバーへファンアウトします。各メッセージに送信元ノード ID をタグ付けすることで、ループは防止されます。
バックプレーンは EngineType プロパティで選択します。
- Mesh (
clusterMesh) — 外部インフラ不要。各ノードはクラスタポートでリッスンし、ピアに直接接続します。追加でインストールするものが何もなく「ただ動く」べき小規模なクラスタに最適です。 - Redis (
clusterRedis) — すでに運用している Redis サーバーを Pub/Sub バックプレーンとして使用します。より大規模なデプロイ向けです。
メッシュバックプレーン(ゼロインフラ)
メッシュエンジンは、最も手早くクラスタ化できる方法です。各ノードにリッスンする ClusterPort とピアの host:port を与え、sgc プロトコルを Attach すれば、そのチャネルの pub/sub(と Presence)がクラスタ化されます。
uses
sgcWebSocket, sgcWebSocket_Protocols, sgcWebSocket_Cluster;
var
oServer: TsgcWebSocketServer;
oProtocol: TsgcWSPServer_sgc;
oCluster: TsgcWSCluster;
begin
oServer := TsgcWebSocketServer.Create(nil);
oServer.Port := 8080;
oProtocol := TsgcWSPServer_sgc.Create(nil);
oProtocol.Server := oServer;
oCluster := TsgcWSCluster.Create(nil);
oCluster.EngineType := clusterMesh; // zero-infrastructure backplane
oCluster.ClusterPort := 5410; // this node's mesh listener
oCluster.Peers.Add('192.168.1.101:5410'); // the other nodes
oCluster.Peers.Add('192.168.1.102:5410');
oCluster.Attach(oProtocol); // cluster this protocol's pub/sub
oCluster.Start;
oServer.Active := True;
end;
変更はこれだけです。このノードに接続したクライアントはチャネルに Publish でき、他の 2 つのノードのクライアントは、まるで全員が 1 台のサーバー上にいるかのようにそれを受け取ります。スティッキーセッションは不要です。
Redis バックプレーン
より大規模なデプロイでは、エンジンを clusterRedis に切り替えて Redis サーバーを指定します。バックプレーンの上にあるすべて — チャネル、Presence、アプリケーションコード — はそのままです。
oCluster.EngineType := clusterRedis;
oCluster.RedisHost := '127.0.0.1';
oCluster.RedisPort := 6379;
oCluster.RedisPassword := ''; // optional
oCluster.RedisChannel := 'sgccluster'; // the Pub/Sub channel
oCluster.Attach(oProtocol);
oCluster.Start;
.NET
マネージド版の sgcWebSockets は、同じコンポーネントを公開しています。クラスタをサーバーの sgc プロトコルに Attach して開始すれば — あるノードでの publish が他のノードのサブスクライバーに届きます。
var server = new TsgcWSServer { Port = 8080 };
var protocol = new TsgcWSPServer_sgc { Server = server };
var cluster = new TsgcWSCluster {
EngineType = ClusterEngineType.Mesh, // zero-infrastructure backplane
ClusterPort = 5414,
Protocol = protocol
};
cluster.Peers.Add("192.168.1.101:5414");
cluster.Peers.Add("192.168.1.102:5414");
cluster.Start();
server.Active = true;
このリリースでは、.NET 版はメッシュエンジンを提供します。Redis バックプレーンは Delphi / C++Builder 版で利用できます。
クラスタ全体の Presence
Presence —「このチャネルに誰がオンラインか」— は、各ノードが自分自身のメンバーしか見えないため、サーバーをスケールアウトしたときに最も影響を受ける機能です。TsgcWSCluster はそれを修正します。sgc プロトコルを Attach するのと同じように Presence プロトコルを Attach すると、メンバーの名簿はすべてのノードにまたがる和集合になります。Join / Leave イベントはクラスタ全体に伝播し、ダウンしたノードのメンバーは自動的に削除されるため、ゴーストメンバーが表示されることはありません。
oCluster.Attach(oPresenceProtocol); // TsgcWSPServer_Presence
// GetMembers now returns the cluster-wide roster, not just this node's.
クラスタの準備完了を知る
各クラスタは自身の接続状態を公開しているため、publish を開始する前にバックプレーンを待ったり、ダッシュボードでノードの健全性を表示したりできます。
oCluster.OnPeerConnected := procedure(Sender: TObject; const aPeer: string)
begin
// a peer link came up
end;
if oCluster.Ready then // at least one peer connected (or no peers configured)
// ConnectedPeerCount tells you how many links are up
.NET では、同じシグナルは OnPeerConnected / OnPeerDisconnected、ConnectedPeerCount プロパティ、そして IsReady です。
ライブラリの他の機能との組み合わせ方
クラスタリングはプロトコルコードの下に位置するため、それ以外は何も変わりません。すでに使っている同じ Publish、チャネルサブスクリプション、QoS の確認応答、そして Presence の API は、今やすべてのノードにまたがってそのまま動作し続けます。メッシュエンジンは外部サービスを必要としないため、2 ノードや 3 ノードのデプロイが簡単になります。より大きくスケールするときのために Redis が用意されています。2 ノードのデモがライブラリに同梱されており(Delphi では 02.WebSocket_Protocols\14.MultiNode_Clustering、.NET では samples\ClusterDemo)、自分のマシン上でメッセージがノードをまたぐ様子を見ることができます。
提供状況
マルチノードクラスタリングは Enterprise エディションの機能で、Delphi 7 から 13(Win32/Win64、Linux64、macOS、Android、iOS)およびマネージド .NET 版でコンパイルできます。
有効なサブスクリプションをお持ちのお客様は、カスタマーエリアから新しいビルドをダウンロードできます。トライアルユーザーの方は、更新されたインストーラーを esegece.com/products/websockets/download から入手できます。
ご質問、フィードバック、またはデプロイへの組み込みのお手伝いが必要ですか? お問い合わせください — コードを書いた本人から返信が届きます。
