# websocket-cluster **Repository Path**: liupan1230/spring-cloud-websocket-cluster ## Basic Information - **Project Name**: websocket-cluster - **Description**: 基于spring-cloud与redis的websocket集群解决方案 - **Primary Language**: Java - **License**: Apache-2.0 - **Default Branch**: master - **Homepage**: None - **GVP Project**: No ## Statistics - **Stars**: 38 - **Forks**: 18 - **Created**: 2023-03-15 - **Last Updated**: 2026-04-07 ## Categories & Tags **Categories**: Uncategorized **Tags**: None ## README # Spring Cloud WebSocket Cluster 基于 Spring Cloud 与 Redis 的 WebSocket 集群解决方案,采用 Feign 直连调用替代传统 MQ 广播模式,实现高效精准的消息推送。内置五子棋游戏示例,完整演示匹配、游戏、推送等核心功能。 ## 项目特点 - **精准路由**:通过 Redis 存储用户-服务映射,直接 Feign 调用目标 ws-server,避免 MQ 广播的全量消费 - **高性能**:基于 Java 25 + Spring Boot 4.0.3 + Virtual Threads,消息查找在内存中完成 - **弹性扩展**:所有服务支持多实例部署,动态扩缩容 - **多端支持**:同一用户多设备在线,同设备挤下线 - **有状态服务支持**:自动绑定用户到游戏服务实例,确保会话粘性 - **完整示例**:内置五子棋游戏,演示匹配系统、游戏逻辑、实时推送 ## 架构设计 ### 整体架构 ``` ┌─────────────────┐ │ Nacos │ │ (服务发现) │ └────────┬────────┘ │ ┌────────────────────────────────────────┼────────────────────────────────────────┐ │ │ │ │ ┌──────────────┐ ┌──────────────┐ │ ┌──────────────┐ ┌──────────────┐ │ │ │ gateway-server│ │ ws-server │◄─┼──│ Client │ │ Client │ │ │ │ (8080) │ │ (18081) │ │ │ (Browser) │ │ (Browser) │ │ │ └───────┬──────┘ └──────┬───────┘ │ └──────────────┘ └──────────────┘ │ │ │ │ │ │ │ │ │ │ │ │ ▼ ▼ │ │ │ ┌──────────────────────────────────┐ │ │ │ │ Redis │ │ │ │ │ (用户会话存储 / 匹配队列 / PubSub) │ │ │ │ └──────────────────────────────────┘ │ │ │ │ │ │ ┌──────────────┐ ┌──────────────┐ │ ┌──────────────┐ ┌──────────────┐ │ │ │ match-server │ │ game-server │ │ │ open-server │ │ ws-facade │ │ │ │ (8085) │ │ (8084) │ │ │ (8081) │ │ (公共模块) │ │ │ │ (匹配服务) │ │ (游戏服务) │ │ │ (开放接口) │ │ │ │ │ └──────────────┘ └──────────────┘ │ └──────────────┘ └──────────────┘ │ │ │ │ └────────────────────────────────────────┴─────────────────────────────────────────┘ ``` ### 消息流转 ``` ┌─────────────┐ WebSocket ┌─────────────┐ │ Client │◄──────────────────►│ ws-server │ └─────────────┘ └──────┬──────┘ │ ┌───────────────┼───────────────┐ │ │ │ ▼ ▼ ▼ ┌───────────┐ ┌───────────┐ ┌───────────┐ │ match │ │ game │ │ 其他业务 │ │ server │ │ server │ │ server │ │ (无状态) │ │ (有状态) │ │ │ └───────────┘ └───────────┘ └───────────┘ ``` **服务端推送流程**: ``` game-server → PushService → Redis(查找用户所在ws-server) → Feign直连 → ws-server → Client ``` ### 模块说明 | 模块 | 端口 | 说明 | |--------------------|-------|---------------------------------| | **common** | - | 公共模块:DTO、枚举、常量、响应封装 | | **ws-facade** | - | Feign 客户端 + PushService,供其他服务集成 | | **ws-server** | 18081 | WebSocket 服务端,处理客户端连接与消息路由 | | **gateway-server** | 8080 | API 网关,统一入口,可在此完成鉴权 | | **match-server** | 8085 | 匹配服务,支持休闲/排位模式,FIFO/分数匹配策略 | | **game-server** | 8084 | 五子棋游戏服务(有状态),处理游戏逻辑 | | **im-server** | 8086 | 即时通讯服务,支持私聊和群聊 | | **open-server** | 8081 | 开放接口,供外部系统推送消息 | | **frontend** | - | 前端页面,包含五子棋游戏界面和IM聊天界面 | ### 核心类 | 类名 | 模块 | 职责 | |---------------------------|--------------|-----------------------------------------------| | `WebSocket.java` | ws-server | WebSocket 端点,处理连接生命周期和消息路由 | | `WebSocketUtil.java` | ws-server | 内存管理 `userId → DeviceEnum → WebSocketInfo` 映射 | | `MessageUserPool.java` | ws-server | 虚拟线程池(5000),保证单用户消息顺序 | | `PushService.java` | ws-facade | 服务端推送入口,含 LocalCache 优化 | | `ServerEnum.java` | common | 服务定义,`state` 字段标识是否需要粘性会话 | | `MatchService.java` | match-server | 匹配核心逻辑,支持多种匹配策略 | | `GameManagerService.java` | game-server | 游戏房间管理,下棋逻辑,胜负判定 | | `ChatService.java` | im-server | 聊天服务,处理私聊、群聊消息 | | `OnlineUserService.java` | ws-facade | 在线用户管理,查询用户在线状态 | ## 快速开始 ### 环境依赖 | 组件 | 版本要求 | 默认配置 | |-------|------|-----------------------------------| | JDK | 25+ | - | | Nacos | 3.x | 127.0.0.1:8848 (nacos/XXH9QLACU4) | | Redis | 8.x+ | 127.0.0.1:6379 | ### 构建运行 ```bash # 1. 构建(按依赖顺序) mvn clean install # 跳过测试构建 mvn clean install -DskipTests # 2. 启动服务(按顺序启动) # 基础服务 mvn spring-boot:run -pl ws-server # WebSocket服务 mvn spring-boot:run -pl gateway-server # 网关服务 # 业务服务 mvn spring-boot:run -pl match-server # 匹配服务 mvn spring-boot:run -pl game-server # 游戏服务 mvn spring-boot:run -pl im-server # IM服务 # 可选 mvn spring-boot:run -pl open-server # 开放接口 ``` ### 模块依赖顺序 ``` common → ws-facade → ws-server ↓ gateway-server, open-server, match-server, game-server, im-server ``` ### 访问前端 启动服务后: - **五子棋游戏**:浏览器打开 `frontend/game.html` - **IM聊天**:浏览器访问 `http://localhost:8080/im/index.html`(通过网关) ## 使用指南 ### WebSocket 连接地址 ``` ws://{gateway-host}:8080/websocket/ws/{language}/{userId}/{device} ``` 参数说明: - `language`: 语言标识(如 zh、en) - `userId`: 用户ID - `device`: 设备类型(PC、IPAD,参考 `DeviceEnum`) ### 消息协议 **客户端 → 服务端** ```json { "serverName": "match-server", "path": "join", "data": { "gameType": "gobang", "mode": "casual" } } ``` **注意**:`path` 不需要包含 `userId`,用户ID由 ws-server 从 WebSocket 连接信息获取,并通过 HTTP Header 传递给业务服务,确保安全性。 **服务端 → 客户端** ```json { "serverName": "game-server", "path": "match_success", "data": { "code": 200, "message": "匹配成功,游戏开始!", "data": { "roomId": "xxx", "blackPlayer": 1001, "whitePlayer": 1002, "myPiece": 1, "currentTurn": 1001, "board": [ ... ], "status": "PLAYING" } } } ``` ### 服务接口说明 #### match-server(匹配服务) | 接口 | 说明 | 请求体 | |----------------|--------|-----------------------------------------| | `POST /join` | 加入匹配队列 | `{ gameType, mode, score?, teamSize? }` | | `POST /cancel` | 取消匹配 | `{ gameType, mode, ticketId? }` | | `POST /status` | 查询匹配状态 | `{ gameType }` | | `POST /queue` | 获取队列长度 | `{ gameType, mode }` | #### game-server(游戏服务) | 接口 | 说明 | 请求体 | |-------------------|--------|------------| | `POST /play` | 下棋 | `{ x, y }` | | `POST /surrender` | 投降 | `{}` | | `POST /state` | 获取游戏状态 | `{}` | | `POST /leave` | 离开游戏 | `{}` | #### im-server(即时通讯服务) | 接口 | 说明 | 请求体 | |-----------------------|--------|-------------------------| | `POST /private` | 发送私聊消息 | `{ toUserId, content }` | | `POST /group/create` | 创建群组 | `{ groupName }` | | `POST /group/join` | 加入群组 | `{ groupId }` | | `POST /group/leave` | 离开群组 | `{ groupId }` | | `POST /group/chat` | 发送群消息 | `{ groupId, content }` | | `POST /group/list` | 获取我的群组 | `{}` | | `POST /group/members` | 获取群成员 | `{ groupId }` | | `GET /online/users` | 获取在线用户 | - | **注意**:所有接口的用户ID通过 HTTP Header 传递,由 ws-server 从 WebSocket 连接信息获取,前端无需在路径中传递。 ### 游戏服务推送事件 | 事件名 | 说明 | 触发时机 | |-----------------|------|-------------| | `match_success` | 匹配成功 | 双方匹配成功,游戏开始 | | `opponent_play` | 对手下棋 | 对手下棋后通知己方 | | `game_end` | 游戏结束 | 分出胜负或平局 | ### IM服务推送事件 | 事件名 | 说明 | 数据结构 | |-----------|------|------------------------------------------------------| | `private` | 私聊消息 | `{ fromUserId, toUserId, content, type, timestamp }` | | `group` | 群聊消息 | `{ fromUserId, groupId, content, type, timestamp }` | | `system` | 系统消息 | `{ content, type, timestamp }` | ### 有状态服务处理流程 有状态服务(如 game-server)需要确保同一局游戏的两个玩家后续请求都路由到同一个实例: ``` 1. match-server 匹配成功 ↓ 2. 发布匹配结果到 Redis (match:result:gobang) ↓ 3. game-server 监听并创建房间 ↓ 4. 调用 UserMqServer.build() 绑定用户到当前实例 ↓ 5. ws-server 缓存 用户→游戏服务 映射 ↓ 6. 后续请求自动路由到同一 game-server 实例 ``` ### 安全性设计 本系统采用**服务端身份验证**机制,确保用户身份的安全性: 1. **WebSocket 连接时验证**:用户在建立 WebSocket 连接时携带 userId(路径参数) 2. **服务端传递身份**:ws-server 从连接信息中获取 userId,通过 HTTP Header 传递给业务服务 3. **业务服务从 Header 获取**:业务服务的 Controller 使用 `@RequestHeader("userId")` 获取用户ID ``` Client ──WebSocket──> ws-server ──Feign──> Business Service /ws/zh/{userId}/PC Header: userId=123 POST /api/endpoint ``` **重要**: - 业务服务的 Controller **不应**从路径参数获取 userId - 前端 WebSocket 消息的 `path` **不应**包含 userId - 这防止了用户伪造身份访问他人数据的漏洞 ### 新增业务服务 1. 在 `common` 模块的 `ServerEnum` 添加服务定义: ```java MY_SERVICE("my-service","我的服务",false), // 无状态服务 MY_STATEFUL_SERVICE("my-stateful-service","有状态服务",true), // 有状态服务 ``` 2. 创建 Controller,endpoint 路径对应 Message 中的 `path`: ```java @RestController public class MyController { @PostMapping("/myPath") public ResponseVO handle(@RequestHeader("userId") Long userId, @RequestBody MyDTO request) { // 业务逻辑,userId 由 ws-server 从 WebSocket 连接信息获取并通过 Header 传递 } } ``` 3. 如需推送消息,添加 `ws-facade` 依赖并使用 `PushService`: ```java @Resource private PushService pushService; public void pushToUser(Long userId, Object data) { pushService.pushMessage(message, userId); } ``` ### 集成消息推送 其他服务需要给用户推送消息时: ```java // 1. 添加 ws-facade 依赖 com.lp ws-facade 1.0-SNAPSHOT // 2. 注入 PushService @Resource private PushService pushService; // 3. 推送消息 Message message = new Message<>(); message. setServerName("my-service"); message. setPath("notification"); message. setData(data); pushService. pushMessage(message, userId); ``` ## 项目优势对比 | 对比项 | 传统 MQ 广播方案 | 本项目方案 | |-------|------------------------|----------------------| | 消息路由 | 全量广播,每个 ws-server 都要消费 | 精准定位,只调用目标 ws-server | | 链路长度 | MQ 中转,链路长 | Feign 直连,链路短 | | 性能损耗 | 高(无效消息解析) | 低(内存查找 + 直连) | | 运维复杂度 | 需维护 MQ 集群 | 纯微服务架构 | | 有状态支持 | 复杂 | 内置用户-服务绑定机制 | ## 五子棋示例 ### 功能特点 - **匹配系统**:支持休闲模式和排位模式 - **实时对战**:WebSocket 实时推送对手下棋 - **游戏逻辑**:完整的五子棋胜负判定 - **状态同步**:自动同步棋盘状态、当前回合 ### 游戏流程 ``` 1. 用户连接 WebSocket ↓ 2. 点击「匹配对手」→ match-server 加入匹配队列 ↓ 3. 匹配成功 → game-server 创建房间 → 推送 match_success ↓ 4. 双方轮流下棋 → 推送 opponent_play ↓ 5. 游戏结束 → 推送 game_end ``` ## 应用场景 - 即时通讯(IM) - 在线游戏(棋牌、对战) - 客服系统 - 实时协作(文档编辑、白板) - 需要低延迟的场景(HTTP → WebSocket) - 有状态服务(游戏房间、协作会话) ## 技术栈 | 技术 | 版本 | 说明 | |----------------------|------------|-------------------------| | Java | 25 | LTS 版本,支持虚拟线程 | | Spring Boot | 4.0.3 | 应用框架 | | Spring Cloud | 2025.1.0 | 微服务框架 | | Spring Cloud Alibaba | 2025.1.0.0 | 阿里巴巴微服务组件 | | Nacos | 3.x | 服务发现 + 配置中心 | | Redis | 8.x+ | 用户会话存储 + 匹配队列 + Pub/Sub | | Virtual Threads | - | 虚拟线程,高并发支持 | ## 联系方式 如有问题,欢迎交流: - 微信:lpshiyue - 暗号:天王盖地虎 ## License MIT License