MERN栈结合Socket如何实现单次请求等待MongoDB匹配成功后响应
MERN栈+Socket实现无轮询游戏匹配系统方案
你已经接入了Socket,完全没必要用HTTP长连接挂起等待结果的方案——HTTP默认有超时限制,用户切后台、网络波动都容易断连,用Socket做事件驱动的匹配逻辑稳定性高很多,具体实现如下:
核心流程设计
- 前端点击「play」按钮后,不需要单独发HTTP请求修改
inQueue字段,直接向后端发送名为join-match-queue的Socket事件,携带当前用户ID、最新ELO分值即可,同时把按钮置灰显示匹配中状态,渲染取消匹配按钮。 - 后端收到入队事件后做3件事:
- 校验用户当前是否已经在队列中,避免重复点击导致重复入队
- 更新数据库对应用户的
inQueue字段为true,同时把用户信息(userId、elo、入队时间戳、当前socketId)存入内存维护的匹配队列Map中,不要每次匹配都全表扫数据库,内存读写性能比查库高几个量级 - 将当前Socket连接加入
waiting-match的公共房间
- 后端单独启动一个间隔2-3秒的定时调度任务跑匹配逻辑,不要写死循环占满CPU:
- 先把内存队列里的玩家按ELO分值排序
- 按入队时间从早到晚遍历玩家,入队时间越长的玩家,ELO匹配容忍区间动态放宽(比如刚入队允许±50分差,等10秒放宽到±100,等30秒放宽到±200,最长等待1分钟以上就固定±300分差,避免边缘分段玩家永远匹配不到人)
- 两两比对玩家分差,只要分差在当前容忍区间内、且双方Socket连接都处于活跃状态,就判定为匹配成功
- 匹配成功后立即执行后续操作:
- 数据库更新两个玩家的
inQueue字段为false,生成新的对局记录,关联双方用户ID - 把两个玩家从内存匹配队列中移除
- 分别向两个玩家的Socket推送
match-found事件,携带对局ID、对手昵称、对手ELO等前端需要的基础信息 - 把两个玩家的Socket加入同一个对局专属房间,后续对局内的实时操作都走这个房间通信
- 数据库更新两个玩家的
异常场景处理
这部分是上线后最容易出bug的地方,一定要做
- 连接断开清理:监听Socket的
disconnect事件,只要用户断连时处于匹配队列中,立即把数据库对应用户的inQueue改回false,同时从内存队列中移除,避免出现人离线了还占队列位置的脏数据 - 服务重启兜底:服务启动时先执行一次全库更新,把所有
inQueue为true的用户字段重置为false,避免服务重启后内存队列清空、数据库里残留脏数据 - 重连状态同步:前端监听Socket的
reconnect事件,重连成功后立即发送check-queue-status事件,后端查询用户当前是否在队列中、是否已经匹配到对局,把状态同步给前端,避免重连后前端状态和后端不一致 - 取消匹配逻辑:前端点取消匹配按钮时发送
leave-match-queue事件,后端收到后把用户移出队列、更新数据库inQueue为false即可
参考代码片段
动态ELO区间计算逻辑:
// waitTime为用户已等待时长,单位:秒 const getEloTolerance = (waitTime) => { if (waitTime < 10) return 50 if (waitTime < 30) return 100 if (waitTime < 60) return 200 return 300 }
附数据库schema示例:
内容的提问来源于stack exchange,提问作者Μελέτης Πέππας
相关产品推荐
相关产品推荐

