——蔡剑彬 C++服务器开发工程师([email protected])
最简单的服务器莫过于在一个while循环里面不停地accept新的sockset,然后做相应的处理。虽然个人是比较推崇简单的事物,毕竟简单才是终极的复杂。但是如果那样做,在实际应用中就难免不适用。
所以,在不把服务器变得十分复杂的前提下,一步步地从一个while循环拓展为能够适用于实际情况的服务。
首先,先确定下游戏服务器的类别,玩游戏的时候一般操作都是点击客户端某个地方,然后等待相应的反应,这种就是应答式服务器(C/S)。应答式服务器核心部分,就是消息通信,需要双方指定要一套协议,然后客户端发送相关的协议ID,以及各种数据,这些数据我是通过Json包来装载。接着服务器接收到消息后,解析Json包,然后做相应的处理,再根据情况需要返回一个结果给客户端。
然后,按照游戏逻辑来将整个while循环服务器划分为若干个能够异步通信的服务器。
玩游戏的流程一般都是这样:注册游戏账号->登陆游戏->玩游戏->退出游戏。这个基本流程可以分为两个功能:管理账号信息和处理玩游戏的逻辑,那么就按这个逻辑将一个while循环服务器划分为两个while循环服务器,分别是登陆服务器(LoginServer)和逻辑服务器(LogicServer)。如图1所示。
图1 登陆服务器和逻辑服务器
客户端连接到LoginServer,进行注册、登陆等功能,LoginServer把玩家的账号信息保存到数据库中。玩家登陆成功之后,LoginServer就通过服务器间的中间层,将玩家的Socket、玩家账号、角色ID等等基本信息发给LogicServer,那么LogicServer就能够发消息给玩家了。而玩家每次要发消息给LogicServer得先把消息给LoginServer,由LoginServer转发给LogicServer,毕竟玩家连接的服务器是LoginServer而不是LogicServer,所以也只能这样了。LogicServer就负责游戏的业务逻辑实现,以及将玩家在游戏过程中产生的数据保存到数据库中。
(注:关于服务器如何与数据库通信,这里简单讲一下。我用的数据库是MySql,服务器是用C++写的,MySql提供了C接口,供程序调用,调用相关接口就能够连接到数据库,并且往数据库读写数据。)
接着,在仔细分析,如果在LogicServer中直接访问数据库,也就是有处理数据保存的动作。那么处理业务逻辑的时间在人数多一点的情况下肯定不够了,这时候就应该把游戏数据管理的功能分出来,由另外一个服务器(DBServer)负责。LogicServer就专注于业务逻辑的实现,一旦有数据需要保存,就马上发消息给DBServer,让它把数据保存到数据库中。现阶段服务器如图2。
图2 分离游戏数据管理功能图
现阶段暂时没什么问题了,但是如果仔细思考LoginServer的功能,是否觉得消息转发的功能不适合放在数据管理账号的服务器呢?而且,将能够直接操作数据库的服务器呈现给客户端是否会引起安全危机呢?以上两个问题是肯定的,所以,为了解决这个问题,需要引入一个能够承担消息转发功能以及保证信息安全的服务器。在日常上网时,有“网关”这么一个东西,负责的事务很符合我们的需求。因此,引入一个网关服务器(GatewayServer),将LoginServer的消息转发功能分离出来,并在与Client连接时判断玩家的信息是否正常,不正常的均不给于登陆。引入网关服务器后的架构图如图3所示。
图3 添加网关服务器图
现在整个服务器的架构接近完美了,但是还少了点东西,如果服务器在运行中出问题了,该怎么判断哪里出问题了呢?问题回溯该怎么做?为了解决这一问题,需要引入一个日志服务器(LogServer),用来记载服务器的日常操作。最终服务器的架构如图4所示。
图4 服务器完整架构图
这个服务器架构是比较实用的,逻辑处理均在各自的while循环中,可以看成是单线程服务器,避免了多线程服务器竞争资源而消耗时间的问题,实际实用情况也表明安全性和稳定性都不错。缺点就是并发量不足,多个用户同时注册登录时,会有登录不上的现象发生,在新开服的前几天,该现象比较严重,可以利用负载均衡技术来解决这个问题。
既然提到负载均衡技术,那就顺便就提一下DNS负载均衡(负载均衡技术的一种)。开启多个LoginServer用于处理大量玩家注册登录的业务,添加一个DNSServer,用于管理多个LoginServer,判断当前负载最小的LoginServer,然后将事务处理分发给该LoginServer,让所有LoginServer的负载均保持在一个平衡状态。DNS负载均衡如图5所示。
图5 DNS负载均衡图
这篇文章只是简略地介绍服务器的架构,以及负载均衡技术,也算是对这个月在服务器方面的学习总结,至于各个模块的详细讨论,会在后续逐渐写出来。