运行在单个进程上的 WebSocket 服务器很简单。可一旦你把两个或更多服务器实例放到负载均衡器后面,就会有东西悄悄地坏掉:每个客户端都只连接到一个节点,因此在节点 A 上发布的消息永远不会到达坐落在节点 B 上的订阅者。频道、广播和 Presence 全都成了半瞎子。这正是各团队从某个 WebSocket 库迁移到别处时最常见的单一原因。
全新的 TsgcWSCluster 组件弥补了这一缺口。把它放在你的服务器旁边,让各节点彼此指向对方,那么任意节点上的发布或 Presence 事件就会到达连接在每一个节点上的订阅者,而你现有的 Publish / 频道 / Presence 代码无需改动。本文将逐一介绍这两种背板引擎、配置方式,以及可直接粘贴的 Delphi 和 .NET 示例。
两种背板引擎
集群通过每个节点都会连接的背板来工作。当一个客户端在某节点上发布消息时,该节点会把消息投递给它自己的本地订阅者,并转发到背板;其他每个节点都会收到这条消息,再把它扇出给各自的本地订阅者。通过给每条消息打上来源节点 id 的标签来防止循环。
你可以用 EngineType 属性来选择背板:
- Mesh(
clusterMesh)— 零外部基础设施。每个节点监听一个集群端口,并直接连接到它的对等节点。非常适合那些应当“开箱即用”、无需额外安装任何东西的小型集群。 - Redis(
clusterRedis)— 使用你已经在运行的 Redis 服务器作为 Pub/Sub 背板,适用于更大规模的部署。
Mesh 背板(零基础设施)
mesh 引擎是最快速的集群方式。给每个节点指定一个用于监听的 ClusterPort,以及它各个对等节点的 host:port,然后 Attach 上 sgc 协议,使其频道的 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,另外两个节点上的客户端就会收到它,就好像它们全都在同一台服务器上一样。无需粘性会话。
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 协议上并启动它 — 在一个节点上发布的消息便会到达其他节点上的订阅者:
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 版提供 mesh 引擎;Redis 背板可在 Delphi / C++Builder 版上使用。
集群范围的 Presence
Presence — “谁在这个频道里在线” — 是服务器横向扩展时受影响最严重的功能,因为每个节点都只能看到自己的成员。TsgcWSCluster 解决了这一点:用附加 sgc 协议同样的方式去 Attach Presence 协议,成员名册就会变成跨所有节点的并集。加入 / 离开事件会在集群范围内传播,而宕机的节点会自动清除其成员,因此你永远不会显示出幽灵成员。
oCluster.Attach(oPresenceProtocol); // TsgcWSPServer_Presence
// GetMembers now returns the cluster-wide roster, not just this node's.
知道集群何时就绪
每个集群都会公开其连通性,因此你可以先等待背板就绪再开始发布,或者在仪表盘中呈现节点健康状况:
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 都会继续工作,只是现在跨越了每一个节点。mesh 引擎不需要任何外部服务,这让两三个节点的部署变得轻而易举;当你扩展到更大规模时,还有 Redis 可用。库中附带了一个双节点演示(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 获取更新后的安装程序。
有疑问、反馈,或需要帮助把它接入你的部署?联系我们 — 你将收到编写这些代码的人的回复。
