去年年底,在 《TSRPC + Cocos,多人实时对战 So Easy!》 中我们分享了如何使用 TSRPC + Cocos 开发多人实时对战游戏,收到不少开发者的反馈。很多朋友都在问,上次的案例 怎么才能改成支持多房间的,性能怎么样,能支持多少用户同时在线等等。
3 月底,腾讯云发布公告 MGOBE 将于 2022 年 6 月 1 日正式下线,也给不少开发者带来一些困扰。没有了 MGOBE,还有哪些替代方案?
本篇文章,将在 上次的分享) 基础上介绍,如何利用 TSRPC 实现 MGOBE 同款的房间系统和匹配系统,并利用分布式架构轻松支持十万人同时在线。
首先来看看我们的需求:
- 房间系统:支持开房间,同时进行多局游戏
- 匹配系统:支持随机匹配,单排、组队匹配
- 全区全服:用户不需要选择服务器,用户感知就只有一个服务器
- 水平扩展:用户规模增长时,通过加机器即可完成扩容
- 平滑扩容:扩容时对运行中的服务不影响,不需要重启和停机
这里我们已经实现好了一个 Demo:
https://tsrpc.cn/room-management/
在 Demo 中我们可以创建房间和随机匹配,你会发现会自动在多个房间服务间自动切换,但用户没有感知。
![](/assets/images/demo-e35c3cb4ebb5512399576dc696e8bbee.jpg)
接下来看看实现思路,首先我们需要对分布式架构有一个基础了解。
分布式基础
负载均衡
部署多份
NodeJS 是单线程的,所以通常,一个服务 = 一个进程 = 一个线程。
单线程服务的可用资源是有限的,最多只能利用 1 个 CPU 核心,随着用户规模的增长,很快就不够用了。
同时,单点部署也满足不了高可用的协议。
怎么办呢?
答案其实很简单:多部署几份就可以了!
你可以在同一台机器多启动几个进程(更好的利用多核 CPU 的性能),也可以分散在多台服务器部署。
如此,你就拥有多个相同的服务了,例如:
下一步,你需要将客户端的请求分发到各个服务上去,这件事被我们称为 负载均衡。
分发策略
如同字面意思一样,负载均衡的目的就是要让你的多台服务器,在 CPU、内存、网络等使用率方面,达到相对均衡的状态。
例如你有 2 台服务器,A 服务器 CPU 90%+,B 服务器 CPU 20%,这肯定不是我们想要的结果。
理想情况下,当客户端的请求来了,肯定是看看所有服务器谁的资源占用最少,就分发给谁。
甚至于你可以实现的更精细一些,把 “负载” 的衡量指标精细到业务数据,例如 QPS、房间数量等。
但通常,简便起见,我们更多采用的是轮询或随机的方式来分发。对于大多数业务场景来说,这已经足够了,并且有很多现成的工具可以使用。
根据你的需求,丰俭由人。
前置代理
分发连接和请求,本质上就是一个代理服务,有很多现成的工具就可以实现,例如:
- PM2
- Nginx
- 阿里云 SLB
- Kubernetes
如果你只是在单服务器上部署多个进程,那么 PM2 就是一个绝佳的工具。
npm i -g pm2
pm2 start index.js -i max
像这样,就可以启动多份 index.js,启动份数等于你的 CPU 核心数。对于 NodeJS 单线程应用而言,进程数 = CPU 核心数有助于发挥最大性能。
使用 PM2 的好处是,你的多个进程,可以使用相同的端口而不会冲突。例如 10 个进程都监听 3000 端口,PM2 会作为前置代理对请求进行随机分发。
如果你是在多台服务器上部署,那么可以使用 Nginx 的 upstream;想省心的话,你也可以直接使用云厂商的负载均衡服务,例如 阿里云的 SLB。
如果你需要使用 HTTPS,可以顺手在 Nginx 或 云厂商的负载均衡中配置 HTTPS 证书。
当然,我们更推荐你学习使用 Kubernetes,它把服务发现的问题也解决了 —— 可以让你在扩缩容时,只需要点点加号和减号那样简单。Kubernetes 可以说是现阶段的通用 + 终极解决方案,目前主流云厂商都提供了 Kubernetes 的托管集群甚至 Serverless 集群,唯一不好的地方在于,它需要一定的学习成本。
会话保持
通常我们把服务分为两类:无状态服务和有状态服务。
例如你把一个 HTTP API 服务部署了 2 份,由于它们只是对数据库的增删改查,请求连接哪个服务都一样。
换句话说,这次请求连服务器 A,下次请求连服务器 B,一点问题都没有。
这样的服务我们称为无状态的。
另一种情况则不然,比如你部署了 10 个王者荣耀的房间服务,你连进服务器 A 在某房间进行游戏,突然网络断线了。那么此时断线重连后,你肯定还是需要连接到服务器 A,因为你玩到一半的游戏房间、你的队友(都是状态),都在服务器 A 呢!这种服务,我们称为有状态的。
显然,对于有状态服务,会有一个通用的需求:即上次连接到了哪个服务器,下次还要继续保持。这个特性,通常被我们称为 “会话保持”。
实现会话保持略微有一些麻烦,Nginx 和 云厂商的负载均衡都有类似功能支持,但是确实是没那么方便。
我们在实践过程中,还有另一种更轻量的做法,在下文的具体方案中会介绍。
负载均衡的部分就先到这里,总结一下就是如何把一个服务部署多份来实现水平扩展和高可用。
拆分服务
接下来介绍拆分服务,即如何把一个大服务拆成多个不同的小服务。
为什么要拆分
对于一个应用而言,我们往往会拆成好几个服务(例如现在流行的微服务架构),这到底是为什么呢?
这其中,有开发时刻的考量,例如方便团队的分工协作和项目模块解耦,把一个有 200 个接口的大项目,拆成 5 个各有 40 个接口的小项目。
同时,也有运行时刻的考量,例如不同模块的资源需求不同,可以部署 100 个实时游戏房间服务但只部署 5 个匹配服务,来实现资源的精细规划管理。
怎么拆分
首先,根据你的业务、组织结构,运行时的资源规划考量,设计好你要拆分出哪几个服务。
然后,有两种方式可以选择:
一般来说,项目跟项目之间不是完全独立的。会有相当一部分代码可以共用,例如数据库表结构定义、登录态鉴权逻辑、公共业务逻辑等等。
如果你选择了拆分为不同的项目,那么你就需要考虑如何在不同项目间共享代码,例如:
- 通过 Git Submodules 共享
- 通过 NPM 共享
- 通过 MonoRepo 的方式共享
- 通过 Git 流水线,自动将代码分发到多个项目
当然,无论上述哪个方式都会引入额外的学习和维护成本。如果你的情况允许,我们更推荐你 在同一项目下拆分项目。
- 首先根据不同项目拆分协议和 API 目录
![](/assets/images/拆分项目-0c713b5c0f9b9dc2612aee3bfc39f64a.png)
- 将原入口点
index.ts
拆分为多个 - 开发时,独立运行各个服务,有两种方式可选:
- 拆分为多个
tsrpc.config.ts
,在 npx tsrpc-cli dev --config xxx.config.ts
时指定 - 只保留单个
tsrpc.config.ts
,通过 entry
参数指定启动入口:npx tsrpc-cli dev --entry src/xxx.ts
在同一项目下拆分服务,有几点好处:
- 天然跨项目复用代码,没有额外的学习和维护成本
- 运维部署成本更低,你只需要构建一份程序或容器镜像,即可完成各个服务的部署(只是修改启动入口点就可以了)
动态配置
最后,你可以通过环境变量来控制运行时的动态配置(例如运行端口号等),以实现多份服务的灵活部署。
const port = parseInt(process.env['PORT'] || '3000');
运行时设置环境变量,在 Windows 和 Linux 下命令不同,此时可以借助跨平台的 cross-env
:
npm i -g cross-env
cross-env FIRST_ENV=one SECOND_ENV=two npx tsrpc-cli dev --entry src/xxx.ts
cross-env FIRST_ENV=one SECOND_ENV=two node xxx.js
如果你使用 PM2,也可以借助其 ecosystem.config.js
来完成配置:
ecosystem.config.js
module.exports = {
apps : [
{
name : 'AAA',
script : 'a.js',
env: {
PORT: '3000',
FIRST_ENV: 'One',
SECOND_ENV: 'Two'
}
},
]
};
pm2 start ecosystem.config.js
核心架构
项目结构
在同一项目下拆分为以下服务:
- 房间服务:WebSocket 服务,用于游戏房间内逻辑,为有状态服务
- 匹配服务:HTTP 服务,用于创建房间、随机匹配,视为无状态服务(下文详细说明)
房间本质上就是一堆 Connection 的聚合,将房间封装成 Class,管理好 Connection 的加入/退出,处理它们的消息收发逻辑即可。
匹配,本质上就是将匹配队列里的信息按一定的规则组合,然后返回这个结果。所以匹配操作,就是一次请求响应 —— 请求时将当前用户加入匹配队列,然后在定期运行的匹配逻辑中返回响应。因此用 HTTP 短连接就够了,当然,你可以将超时时间设置长一些。
全区全服分布式架构
房间组
通常,房间服务需要的服务器资源更多,匹配服务需要的服务器资源较少。
因此匹配服务与房间服务设计为 一对多 的关系,即由 1 个匹配服务来管理多个房间服务的房间创建和匹配,以此视为一个 房间组。
![](/assets/images/房间组-9f07d426b427b4d1c37cd660227f9073.png)
根据实际需要,你可以部署 1 个或多个房间组,就形成了一个分布式的房间群。
![](data:image/png;base64,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)
不设接入层服务
房间群,是不是看着有点像网游里的大区?但由于我们的需求是全区全服,所以不能让用户感知到选服。
经典全区全服的三层标准结构,是这样的:
![](/assets/images/经典三层架构-66008740869536b2b795b2b6d43750f2.png)
在接入层中统一完成鉴权,代理转发,会话保持等操作。显然,接入层服务非常重要,它的开发和维护也存在一定复杂性。
但是,如果我们使用的是 WebSocket,并且选择的是在同一项目下拆分服务,那么这套架构可以 大幅简化!我们可以直接 不设接入层服务。
![](/assets/images/不设接入层-0435d5fd7eeef54fbf10c744ca1c9d75.png)
由于是在同一项目下,各个服务共享鉴权等接入层逻辑,是件很容易的事。例如用户登录后生成了一个登录凭据,用它作为访问各个服务的凭据即可。
可参考:登录态和鉴权
由于没有接入层,所以也就不存在代理转发,不同的服务直接通过不同的 URL 直连即可。
对于无状态服务,通过负载均衡的前置代理,多份部署可以对外暴露为同一个 URL。
有状态服务,由于会话保持的需要,将它们暴露为不同的 URL 即可。
如此,我们既节省了复杂的接入层开发,还减少了中间代理的延迟损耗。
服务间 RPC
根据上述架构,匹配服务需要知道它旗下的所有房间的实时状态,来完成匹配逻辑。
这就需要匹配服务和房间服务之间相互有 RPC 通信。别忘了,TSRPC 是跨平台的,利用 TSRPC 的 NodeJS 版客户端即可完美实现。
由于我们的需求是平滑扩容,即增加服务部署时不需要重启现有服务,因此我们需要自行实现一个简单的服务注册机制,本方案的实现是这样的:
![](/assets/images/服务注册-98e43310597e75cae3ec8b29a9fc2d46.png)
- 房间服务启动前,需要通过配置指定其归属的匹配服务 URL。你可以指定为上面提到的无状态服务的统一 URL,这样就会在所有匹配服务中随机选择一个。或者你想实现更精细的控制,那就额外给每个匹配服务单独绑定一个 URL,然后根据你的规则去自行制定。
- 房间服务启动后,主动到匹配服务注册,然后二者建立 WebSocket 长连接,开启 RPC 通信。
- 房间服务定期向匹配服务同步实时的房间状态信息。
- 匹配服务通过 RPC 调用房间服务的
CreateRoom
等接口完成房间管理事项。
由于房间服务是一个服务一个 URL,所以启动之后,你需要更新前置代理(如 Nginx 或 Kubernetes Ingress)的配置,绑定对应 URL 到当前服务。
这个过程,当然也可以通过程序来自动完成~ (没有提供,可自行实现或手动修改)
效果验证
开房间
开房间的完整请求过程如下:
- 客户端向匹配服务发起
创建房间
请求 - 匹配服务从其 RPC 着的 N 个房间服务中,选择一个(比如房间数最少的那个),通过 RPC 创建房间,拿到房间 ID
- 向客户端返回房间 ID 和对应房间服务的 URL 地址
- 客户端直连房间服务,加入房间
- 客户端邀请其它好友加入,向他们发送
房间服务 URL
+ 房间 ID
- 其它好友同样直连房间服务加入游戏
可见,即便存在多个房间组,也不会影响玩家之间的开房互通。
随机匹配
随机匹配的完整请求过程如下:
- 客户端向匹配服务发起
随机匹配
请求 - 匹配服务将该连接加入匹配队列
- 匹配服务定期执行匹配,根据实时房间信息,选择用户适合的房间,返回
房间服务 URL
+ 房间 ID
- 客户端通过
房间服务 URL
+ 房间 ID
直连房间服务并加入房间
可能有朋友会问,如果存在多个房间组,那匹配时不是相当于只和部分玩家在一起匹配,并不是真的所有玩家匹配呀?
事实上,当你已经需要用到多个房间组的时候,说明你已经有相当的玩家基数了。
而匹配,其实真的不需要所有玩家一起匹配,一个房间组的内的用户数量,应该已经完全可以满足匹配的时间需要了。
特殊情况下,例如你需要将用户分群匹配,而非混在一个房间组里,那也可以将不同房间组绑定到几个不同的 URL 上,进行前置分流。
总而言之,所有接入层代理转发的活儿,交给 URL 即可。
水平扩展和平滑扩容
房间服务和匹配服务都可以水平扩展,并且它们都是支持平滑扩容的。
增加部署房间服务,只需要配置、启动房间服务,按照上述服务注册流程,匹配服务会自动将它纳入麾下,对现有服务没有任何影响。
增加部署匹配服务,就跟增加部署无状态服务一样,也是平滑无感知的。
所以,只要你的机器够多,其它依赖如数据库、Redis 撑得住,十万人同时在线也没有问题。
闲聊一嘴,因为有不少朋友来问 TSRPC、本方案与 Pomelo 的区别。
Pomelo 是非常优秀的框架,凭借 NodeJS 实现了接入层、服务注册与发现、服务间 RPC 等诸多机制。但在今天,其实也有新的变化。
在集群管理方面,出现了 Kubernetes 这样标准化的方案,无论是扩缩容、服务注册与发现、URL 路由等等方面,都更专业、可靠、高性能。
云厂商的托管服务和中间件也日趋完善……
更精细的分工诞生了更专业的的工具,所以今天,不是所有工作都需要你亲自在 NodeJS 中完成。善用这些工具链,能让你把有限的精力更专注在业务上,事半功倍。
Demo 体验地址:https://tsrpc.cn/room-management/index.html
包含前后端的完整工程如有需要可以去 Cocos Store 获取。
传送门:https://store.cocos.com/app/detail/3766
附上 7 天内有效的限时优惠福利:
![](/assets/images/优惠券-e700a593c6415ac5dc095f768502048e.jpg)
也欢迎扫码加入 TSRPC 和 TypeScript 全栈开发微信交流群:
![](/assets/images/wechat-3c078e02dce4593e7b3b7ad1ee3f56d4.png)