很早前即有想法设计一套稳定、高效、安全的弱交互移动网络游戏服务器端基础框架,前些天初步完成简单的初稿文档。初版设计参考了印象里以前的一些工作经历经验。这些经历经验虽已日渐模糊,但从它们这里,自己获益良多。
初稿文档暂只是简单记录了目前想到,或觉得比较重要的内容(或许会更新),具体细节等涉及较少。可能我会在业余时间里一点点实现本文所述框架,只是开发计划暂无法预期,毕竟精力很有限。
1、功能描述
1.1) 弱交互移动休闲类游戏服务器端基础框架(以房间为游戏单位,诸如棋牌类游戏)
1.2) 相较传统的 MMORPG 服务器框架,简化结构
2、框架构成
2.1) LoginGate(登录网关)
运行在公网环境,用于转发客户端与 LoginServer 间的登录交互等。LoginGate 如果被攻击,可新开其它地址(如在别的机房)的 LoginGate。当然也可部署多个 LoginGate。
2.2) LoginServer(登录服务器)
运行在内网环境,只接受合法的 LoginGate 连接,自身也连接至 DBServer,用于客户端登录验证及登录状态管理等。
2.3) GameGate(游戏网关)
运行在公网环境,主要用于转发客户端与 GameServer 之间的通讯封包,同时也能处理诸如外挂、恶意连接等(如新连上来的客户端若 15 秒还不发数据,或若有客户端以非正常速度发数据则直接踢掉甚至加入黑名单)。
通常会有多个 GameGate 进程部署在相同或不同的物理机上,甚至可部署在不同的机房。
2.4) GameServer(游戏服务器)
运行在内网环境,游戏逻辑核心服务器,只接受合法的 GameGate 连接,自身也连接至 DBServer,且会通过 UDP 协议发送日志信息给 LogServer 保存。
2.5) DBServer(数据服务器)
运行在内网环境,主要负责游戏数据存取、登录验证(可选)等逻辑,只接受 LoginServer 与 GameServer 的连接。
2.6) LogServer(日志服务器)
运行在内网环境,用于保存 GameServer(甚至 DBServer、LoginServer 等)通过 UDP 发过来的日志信息。
各服务器间连接图如下。
GameServer、DBServer、LoginServer 及 LogServer 可放在同一局域网甚至同一台物理机上(如果负载不大的话)。通过模块功能划分框架构成,各模块职责明确,代码结构清晰,对于网络攻击防范及减轻譬如 GameServer 的压力等大有裨益。
3、客户端进入游戏流程
3.1) 客户端启动时,向某指定的(固定不变的)地址(域名)发起一个 HTTP 请求,获取 LoginGate 地址(IP&Port)
3.2) 客户端连接至 LoginGate,之后输入账号密码并提交
3.3) LoginGate 转发账号密码等信息给 LoginServer,由 LoginServer 验证(与 DBServer 交互)
3.4) 若验证通过,DBServer 准备玩家数据,LoginServer 通过(分布式)负载均衡算法返回某 GameGate 地址,然后 LoginGate 断开和客户端的连接
3.5) 客户端断开与 LoginGate 的连接,并连接至 GameGate
3.6) 客户端通过 GameGate 与 GameServer 建立起连接并进入&开始游戏
4、技术实现
4.1) 开发技术
4.1.1) Golang
游戏后台开发(至少核心及基础框架),用编译型(原生)语言来编写应是比较合理的做法。动态语言当然也能,但明显这并非它们所长(如代码组织、代码可读性、调试便利度、部署复杂度以及安全和效率等方面)。
Java 等语言技术当然也能,而且可用的资源很多,人才也非常之多,但它带 VM,部署麻烦,框架厚重,相对而言并不算理想的选择。
C++ 太复杂(即便只用其很小的一部分子集),对人员的技术要求较高(写出稳健的 C++ 程序并不容易),编译太慢,对于中小技术团队来说并非好的选择。
而 Delphi,单从其品质中下、人才难寻等方面考虑,更非合适选择。
至于以替代 C++ 为目标的 Rust,综合其语言复杂度(似乎至今语法还未成型)及开发支持团队、社区、使用案例等,至少短期内,可能观望一下比较合适。
原生、语法简单且以实用为导向、标准及三方库强大(譬如网络等方面)、编译迅速、编译器优化主流水平(GC 改进已很大且一直在持续改进)、跨平台,说 Golang 是天生的后端开发利器并不为过。
而早已有不少 Web后台、游戏后端、KV数据库等(成功)使用案例。
通常情况下,Golang 能比较快上手,熟悉其它语言技术的开发人员转到 Golang 基本会很快。
4.1.2) Lua(可选)
不止在游戏界,业务逻辑采用脚本语言(较常用的是 Lua、Python 等)来开发非常常见,这种方式的好处在于:
4.1.2.1) 灵活,业务逻辑变动可能较频繁,而脚本代码修改执行很方便
4.1.2.2) 划分核心层和业务层,普通开发人员接触不到核心的代码,只能拿到脚本接口文档用脚本写业务逻辑
4.1.2.3) 脚本代码出错了,最多影响局部逻辑(可上报脚本错误以方便后续解决问题),用脚本做一个安全的调用层可以避免整个进程崩溃
4.1.2.4) 热更新
综上,采用 Golang + Lua 相结合的方式,或许不失为可尝试的开发模式。
4.1.3) Protobuf(可选)
高性能的网络传输协议格式,序列化及反序列化速度甚至远胜 JSON、XML 等,其简单、精简(数据描述文件非常小),不依赖于平台,支持多种编程语言。但其实,自实现紧凑高效的封包格式也并不难。
4.1.4) 加解密
PC 网游一般采用 动态加解密模式(编写数套加解密算法并编译,然后抠出每套加解密函数的机器码,每次客户端上线时,随机选取一套加解密算法发给客户端,之后客户端与服务器的通讯封包皆通过此算法加解密),但移动游戏估计不容易实现。
如果封包较小且不密集,可考虑采用不对称加解密算法,增加封包破解难度。
否,应考虑 TEA、AES 等对称加解密算法(如 QQ 的聊天信息本质上就是用 TEA 加密的)。
譬如可考虑随机使用几种加解密算法中的一种(每次客户端上线时,服务器通过约定的 ID 告知应使用哪套加解密算法),增加灵活性及破解难度。
4.2) 数据库存储
4.2.1) MySQL Community x64
4.2.2) MongoDB(备选)
4.3) 运行平台
Linux 64bit(CentOS / Red Hat)
5、后记
以上思路,是以单个的游戏为设计立足点,较有局限。
譬如登录,统一走登录平台的方式更通行及合理(此时可由 LoginGate 与 平台进行交互验证)。而日志处理等,能有专门的处理后台也不失是好的做法。
只是如此会涉及很多方方面面的细节和做法,于初衷有悖,是以本文所述设计,仅供参考而已。但很明显,基于以上设计,纵使需作扩展,改进起来也较简单(毕竟,此服务器框架专为弱交互移动游戏而设计)。
6、一些讨论
与某基友讨论如斯设计,基友认为无需三层结构(Client-->GameGate-->GameServer),而是让客户端直连 GameServer(GameServer 负责逻辑处理,可以线性扩展),且加一个中央服务器(CenterServer),做简单的游戏状态记录和逻辑处理,以及支配各 GameServer 等。这种设计当然很好,只是在考虑到诸如框架简明及服务器安全等因素,短期内估计无意以此思路来改动初版设计。