feat(ws): implement WebSocket cluster mode with Redis Pub/Sub
All checks were successful
Build Backend / build (push) Successful in 2m0s
Build Backend / build-docker (push) Successful in 1m26s

Introduce a new WebSocket messaging architecture that supports both standalone and cluster modes. This allows for horizontal scaling of WebSocket servers by using Redis Pub/Sub to synchronize messages across multiple instances.

Key changes:
- Added `ws.MessagePublisher` interface to abstract message distribution.
- Implemented `ws.Bus` to handle cluster-mode messaging via Redis.
- Added `ws.OnlineTracker` to manage user online status across the cluster.
- Refactored multiple services (Chat, Group, Push, Call, etc.) to use the new `MessagePublisher` instead of a concrete `ws.Hub`.
- Added WebSocket configuration options (mode, instance ID, channel, TTL, heartbeat) to `config.yaml` and `config.go`.
- Updated dependency injection with Wire to support the new publisher and Redis client.
- Improved logging by replacing standard `log` with `zap` in several service components.
This commit is contained in:
2026-05-06 12:39:11 +08:00
parent d481742790
commit c630cbf4d0
22 changed files with 1082 additions and 120 deletions

View File

@@ -46,8 +46,8 @@ var InfrastructureSet = wire.NewSet(
ProvideBuiltinHooks,
ProvideModerationHooks,
// WebSocket Hub
ProvideWSHub,
// WebSocket MessagePublisher
ProvideWSMessagePublisher,
// 外部服务客户端
ProvideOpenAIClient,
@@ -163,9 +163,32 @@ func ProvideModerationHooks(
return moderationHooks
}
// ProvideWSHub 提供 WebSocket Hub
func ProvideWSHub() *ws.Hub {
return ws.NewHub()
// ProvideWSMessagePublisher 提供 WebSocket 消息发布器
// standalone 模式返回 *Hubcluster 模式返回 *Bus组合 Hub + Redis Pub/Sub
func ProvideWSMessagePublisher(cfg *config.Config, redisClient *redis.Client) ws.MessagePublisher {
hub := ws.NewHub()
if cfg.WebSocket.Mode == "cluster" && redisClient != nil {
bus := ws.NewBus(hub, redisClient.GetClient(), &ws.WSClusterConfig{
InstanceID: cfg.WebSocket.Cluster.InstanceID,
MsgChannel: cfg.WebSocket.Cluster.MsgChannel,
OnlineTTL: cfg.WebSocket.Cluster.OnlineTTL,
HeartbeatInterval: cfg.WebSocket.Cluster.HeartbeatInterval,
})
if err := bus.Start(); err != nil {
zap.L().Error("Failed to start WebSocket Bus, falling back to standalone mode",
zap.Error(err),
)
return hub
}
zap.L().Info("WebSocket running in cluster mode",
zap.String("instance_id", bus.InstanceID()),
)
return bus
}
zap.L().Info("WebSocket running in standalone mode")
return hub
}
// ProvideOpenAIClient 提供 OpenAI 客户端