服务器组件之网关

提要

本文介绍网络游戏服务器中常用的组件:网关。主要讨论网关的作用,与网关相关的服务器架构设计,及我们项目中的具体实现(Go语言HTTP)。

网关的作用

1. 维持连接

我第一次接触网关的时候做的是MMORPG游戏,当时的服务器架构是分场景的,即每个game维护一个或多个游戏场景。而且MMORPG一般来说用的是TCP长连接做消息通信,于是当玩家从一个场景切进另一个场景时需要断开与前一个服务器的连接,再去连接后一个服务器,这时候如果网络不通畅,常常就掉线了。

引入网关做代理可以很好的解决了这个问题。客户端启动后连接的是网关,网关再去连接场景服务器并转发所有消息包。切换场景时客户端与网关的连接是不断的,由网关负责去连接新的场景,因为网关和游戏服务器通常在同一局域网内,所以掉线的问题大大改善了。

2. 串并转换

网关可以更进一步增加串并转换的功能。网关和每个game只维护单一连接,将多个客户端的并发连接转为串行方式发往game。这样做的好处是客户端切换场景时网关不用重新建立TCP连接了,可靠性更高。

另外game处理消息的逻辑可以更加简洁,不用处理I/O复用,因为所有的消息来自单个TCP连接(单网关)或者固定数量的几个TCP连接(多网关的情况)。

3. 权限控制

在多进程的网络游戏架构中,game除了要处理客户端的消息,同时还会处理其他进程的消息,比如账号、GM工具等。出于安全原因,一定需要把客户端的消息隔离开来加以限制。

一种方法是考虑进程开启后不同的服务监听不同的ip和端口,内部使用的服务只绑定内网ip,从而起到隔离的效果。

我们采用的另一种做法是使用网关来做权限控制,直接将客户端的请求限制在HTTP的某个路径下,这样一来所有的服务器进程都只监听单一ip和端口,简化了架构设计。

4. 运维方便

游戏服务器经常需要开服、合服,还有硬件故障等原因导致不得不做服务器迁移。如果game直接对客户端提供服务,服务发生变更时往往需要客户端退出重新走登录流程。如果使用网关,在服务器内部发生地址变更时只需要保证网关得到更新并发起重连,客户端甚至根本觉察不到。

部署时可以把所有网关绑定至同一个域名,于是客户端连接服务器时通过统一的域名即可,不用关心具体的game服务器地址。同时网关可以很轻松地动态增减,也有了简单的网络负载均衡功能。

5. 省钱

增加新的网关进程意味着我们需要购买更多的服务器,怎么会省钱呢?

因为将外网带宽这种比较昂贵的资源集中管理可以使其利用率更高,自然就达到省钱的效果了。

以我们使用的阿里云ECS为例,外网带宽作为一种资源单独收费,目前(2015年3月)价格列举如下,其中带主机费用指的是如果使用独立主机(选择最低配置)运行网关来提供网络接入功能对应的价格:

带宽 带宽费用(元/月) 单价(元/M/月) 带主机费用(元/月) 带主机单价(元/M/月)
1M 23 23 55 55
2M 46 23 78 39
3M 71 23.67 103 34.33
4M 96 24 128 32
5M 125 25 157 31.4
6M 205 34.17 237 39.5
7M 285 40.71 317 45.29

注意两点:1. 带宽的单价不是固定不变的,单台主机需要的带宽越大,单价越高。2. 如果使用独立主机接入外网,选择5M带宽单价最便宜。

假设我们每台game服务器需要0.5M带宽,共计20台。如果不使用网关,那么需要给20台game配置1M带宽,总费用是23*20=460元/月;而使用网关的情形,我们配置2台5M的独立服务器,总费用是157*2=314元/月。

再假设随着游戏功能增加,用户量活跃度增大,每台game需要的带宽升至1.2M。如果不使用网关,那么只能将每台game的带宽升为2M,总费用是46*20=920元/月;使用网关的情形下,我们需要增加2台5M、1台4M的独立服务器,总费用是157*4+128=756元/月,而且扩容的过程只做添加操作,更加简单。

设计与实现

我们的游戏项目使用的是HTTP短连接,引入网关的主要作用是做权限控制和方便运维。

1

如图所示,我们定义了两种不同的网关,gate用来提供客户端连接服务,admin-gate是仅限内部使用的,用来做服务器的维护监控等,例如运营人员做GM操作。

所有的gate都绑定在同一域名下,根据客户端提供的用户id把消息转发到对应的game服务器上,并对客户端能调用的接口做好权限控制。

admin-gate不做权限控制,能访问内网的所有服务,但是需要输入用户名和动态密码才允许使用,我们引入了TOTP来保证安全。

其他进程如game、GM等都运行在内网环境中。我们还运行了etcd来做服务发现,当服务迁移等操作时,gate能立刻感知变化并把消息转发到新的ip/端口。

Go语言的httputil包里已经有了一份比较完善的http proxy实现,我把它稍稍封装了一下,使其可以更方便地在运行时更新host->addr映射,代码放在Github:huangml/gate。监听etcd并更新配置的部分因为和我们游戏的具体配置相关,所以现在没有开源。