实战指南:使用Socket.IO和Redis构建实时多人在线PK系统

实战指南:使用Socket.IO和Redis构建实时多人在线PK系统

内容纲要

实战指南:使用Socket.IO和Redis构建实时多人在线PK系统

近期开发了一个知识PK系统,主要包括创建房间、加入房间、开始游戏、答题等功能。对此我选用了SocketIO,实现客户端和服务器之间低延迟,双向和基于事件的通信。

技术方案

  • Hyperf框架

一个基于Swoole的PHP协程框架

  • SocketIO组件

Socket.IO是一款非常流行的应用层实时通讯协议和框架,可以轻松实现应答、分组、广播

组件基于WebSocket实现,房间适配器基于Redis驱动,可以适应多进程乃至分布式场景

  • Redis

存储房间和玩家状态

  • Postman

一个强大的客户端工具,支持Socket.IO连接,用它来做功能测试十分方便

状态存储

Redis存储

数据结构 Key Value 说明
房间信息 Hash rooms:{room_no} {"id": 1, "state": "created", "game_number": 1, "sid":"6626326272d7b#1"} 存储房间ID、状态等基本信息
玩家集合 Set rooms:{room_no}:playerIds player1,player2 存储房间内所有玩家ID
玩家状态 Hash rooms:{room_no}:players:{player_id} {"id": 1, "nickname": "卡夕洛", "avatar": "", "state":"ready", "score":0, "answer_count":0,"time":1713779921} 存储玩家的ID、昵称、状态、得分等信息
暂存房间 String staging_room:{user_id} room_no_xx 房主断线后保留房间,X秒过期,重连后直接使用该房间

Context-连接上下文

实例化后的Roomer/Player服务类可以存储在连接上下文

事件

基本流程

房主:创建房间 - 即将开始 - 开始游戏 - 答题 - 对局完成 - 返回房间/解散房间

玩家:加入房间 - 准备/取消准备/离开房间 - 答题 - 对局完成 - 返回房间/离开房间

事件 说明
create-room 房主:创建房间并加入,响应玩家列表和房间信息
join-room 玩家:加入房间,响应玩家列表和房间信息,向房间内广播player-join
leave-room 玩家:离开房间,向房间内广播player-leave
player-ready 玩家:准备,向房间内广播player-ready
player-cancel 玩家:取消准备,向房间内广播player-cancel
game-ready 房主:游戏即将开始,向房间内广播game-ready
game-start 房主:游戏开始,向房间内广播game-start,消息为PK的题目
answer 答题,向房间内广播分数等信息
return-room 返回房间,响应为玩家列表和房间信息
room-dissolve 房主:销毁房间,向房间内广播room-dissolve

掉线情况

心跳机制来检测玩家是否掉线,如果客户端在一段时间内没有发送心跳包,则认为客户端已掉线

Socket.IO服务器2.x版本,客户端与服务器建立连接时,服务器向客户端发送pingIntervalpingTimeout参数,用来控制客户端发送ping消息的频率和超时时间

  • 玩家掉线后,直接向房间内广播player-leave

  • 房主掉线后,在某些情况下(如APP切换到后台被系统杀掉),保留房间一段时间

启动一个定时器,X秒后执行销毁房间和广播事件。如果客户端重新连接并创建房间,优先判断是否有保留房间,有则直接加入房间并清除定时器

对局记录持久化

每场PK结束后需成绩排名,并把对局记录存储到数据库,即结算

  • 所有玩家答题完毕

当玩家答题到最后一道时,判断是否所有玩家都已答完,若是则结算

这里可能会有多人同时触发结算,我们使用Redis分布式锁避免并发问题,只有加锁成功的玩家才能进入结算逻辑

  • 所有玩家掉线

当玩家掉线时,如果房间状态为PK中,判断该玩家是否为房间最后一人,若是则结算

  • 定时任务

定时巡查结算超时的房间

部署

经过测算,每名玩家约占用1kbRedis内存

Docker + 蓝绿部署

蓝绿部署是一种发布新版本软件的策略,它将新版本软件部署在一个与生产环境完全隔离的环境中,称为“蓝环境”。经过测试验证后,再将流量从“绿环境”切换到“蓝环境”,完成新版本软件的发布。

每次发版切换流量后,客户端与旧版本之间建立的长连接不受影响,待等到旧版本上没有连接后再关闭服务即可。

发表回复

您的电子邮箱地址不会被公开。