通信数据大小端需要注意:java收发的都是网络格式的数据是大端数据,c++运行在Linux中的服务如果没有转成网络格式数据,服务器一般都是小端格式的数据,这个在编解码的时候需要留心,否则可能解不出对方发来的数据,自己发的数据对方也解不出来。如何确认双方发送的数据是否一致呢,一般来说可以在收到一个完整的消息包后,把该消息包字节流的数据使用16进制字符串打印出来,就可以很容易比较
-
对于网关服务 GateServer,登录服务 LoginServer 这两种部署在外网的服务与客户端之间之间打交道的,它们的压力大部分来自网络IO而非业务计算,尤其是网关服务器 GateServer 几乎没什么业务逻辑处理,主要处理网络消息的转发(客户端发送到后端服务器,后端服务器发送到客户端)与过滤。从它负责的业务角度上可以看出一个好的 GateServer 其实就是一个高性能的网络服务。针对高性能网络服务的实现有很多方式
常见模型有:
多线程模型
one loop per thread 是一个高性能网络服务常用模型,loop是基于反应器Reactor的设计模式实现,每个IO线程里面都有且只有一个loop,一个socket/channel连接只会在一个线程里面处理,一个线程利IO复用可以处理多个连接。Java 的 Netty、Mina,C++的 Muduo 都是默认使用这种模型编写的高性能网络库-
协程
语言级别的golang,erlang,单独库形式的libco,skynet等我们游戏里面使用的方案是基于linux系统下的多线程模型,在我们实际运行过程对服务器硬件的一些参数进行优化设置后,单GateServer最高同时在线达到10w+问题不大,机器整体的负载也不高。
对于逻辑服务 LogicServer 游戏逻辑主要在这个服务上处理。常见的实现,IO线程 + 单独的业务线程。IO线程接收到的数据解码成具体的消息数据后通过消息队列传入到业务线程中,消息数据汇总到同一个线程中处理,也就是说游戏业务处理是单线程的,这种设计的优势在于避免业务开发中数据竞争。如果单线程实在不能满足性能要求,比如一些mmo游戏中,可以采用多进程的模式,也就是有多个LogicServer,管理和协调这些LogicServer 可能还需要一个逻辑管理服务。
对于管理服务 GMServer 一般来说除了需要处理与其他后端服务的tcp的连接之外,还需要启动一个http的服务来处理游戏的后台管理发来的请求。
对于DB服务 DBServer 数据库的代理层,负责与数据库交互,解耦业务层的数据处理,适应数据库的改变,业务逻辑也相对简单。DBServer通常会开一个线程池来处理,根据玩家id分配到不同的线程上处理玩家的数据,对数据库的操作根据不同的场景需求可以实时操作,也可以定时操作。它也可以合并到LogicServer里,但是独立开来,整体框架更加清晰
对于数据库,玩家游戏数据的存储 常见的游戏数据库包括: mysql,redis,mongodb等。对于mysql这种传统数据库设计表的时候一定要注意结构的合理性和索引的建立,否则很有可能导致数据库大瓶颈。
对于这些后端服务(除了网关服务和登录服务之外)来说连接数是固定切有限的,一般来说也就一两个其他后端服务与自己连接,另外自己也会发起与其他后端服务的连接。所以这些服务并不需要所谓的高性能网络IO的处理,一个Rector处理网络IO就够了,编写模式也比较固定,所有的网络数据接收解析后通过消息队列传到主业务线程+线程池处理。
游戏的业务主要是响应外部请求和处理内部状态改变,包括以下两种驱动方式:
-
消息驱动
针对各种网络消息做出响应。比如,玩家的点击反馈等。常见实现方式是通过命令模式,也就是消息封装成一个对象,不同的消息参数化。
-
时间驱动
一些业务需要基于时间而做出改变。比如,定时活动的开启,玩家数据定时的保存等。常见的实现方案是定时器,定时器采用的算法可以是时间轮,红黑树等。
实现这两种驱动之后,游戏服务器可以响应外部和内部的改变,基本上服务就可以正常的运转。游戏业务与其他所有后端业务都一样,本身就是数据结构的组织。
有疑问加站长微信联系(非本文作者)