简单来说,一套游戏服务端开发框架应该具备两种能力:
- 定义客户端到服务端、服务端到客户端、服务端到服务端之间的消息管道
- 描述游戏世界状态的维护方式
消息管道
场景同步
游戏服务端的需求源于
- 单机游戏联网版
实现为主客机模式,主机部分可看作是服务端。 - MMO的雏形MUD
跟WebServer比较类似,一个主机服务多个客户端,表现为C/S架构。
两种需求起源最终都导向了同一种业务需求,传统的MMO架构,一个进程中维护多个场景,每个场景中有多个玩家,额外的中心进程负责帮助玩家从一个场景(场景)切换到另一个场景(进程)。抽象一下问题,谈到游戏服务端首先想到就是多玩家对同一场景的视图同步也就是场景同步。
如何实现场景同步呢?简单来说使用socket
,那么如何使用socket
实现场景同步呢?
场景同步有两个需求:低延迟、重交互
实现低延迟最理想的情况是由程序员把控消息流的整套管道,换句话说就是不借助第三方消息库或连接库。重交互就需要保持场景同步逻辑的简化,换言之场景逻辑最好是单线程的,并且跟IO无关。其核心入口就是一个主循环,并依次更新场景中的所有实体、刷新状态并通知客户端。
正是由于这两个需求的存在,网络库的概念出现了,网络库由于易于实现,概念简单。并且笼罩着底层的光环,所以应该是程序员造过最多的轮子之一。
网络库解决了什么问题呢?
网络库首先解决的是将传输层的协议转换为应用层消息协议,对于业务层来说,接收到流和包的处理模型是完全不同的。其次是封装具体的连接细节,由于Socket是全双工的,因此在CS架构中的IO模型对于任意一侧都是实用的。连接细节不同体现在客户端,它的核心需求是建立连接外围需求是断线重连。对于服务端而言,核心是接收连接外围需求是主动断开连接。等到两端连接建立完毕后,就可以基于这个链接构建同样的IO模型了。
网络库实现的功能
- 一个连接好的socket对应一个connector
- connector负责向上提供IO模型抽象,同时借助connector buffer实现流转包。
- 客户端部分主要组件是ClientNetwork,维护一条connector,维护连接及重连。
- 服务端主要组件是ServerNetwork,维护多条connector,并且接收连接与主动断开连接。
- 网络层协议
有了网络库可以解决联网,但随着玩家数量增加,单进程是扛不住的,那么就需要多进程,每个进程服务一定数量的玩家。但是任意两个玩家之间,总是可能有交互的需求。对于交互的需求,比较直观的解决方式是让两个玩家在各自的进程中跨进程交互,但这样就成了一个分布式一致性的问题,简单来说,就是如何在两个进程中的玩家之间保持状态的一致性。简单的解决方案是如果是场景交互的话,就限定两个玩家必须在同一场景(进程)中,其他交互则借助于第三方的协调者来协助,如公会系统通常会走一个全局服务器等。
这样,服务端就由之前的单场景变为了多场景进程+协调进程,那么新的问题出现了:玩家需要与服务端保持多少条连接呢?
如果是保持O(n)条连接的话,既不环保又不易于扩展。如果是保持O(1)条连接,如何确定玩家与哪台服务端进程通信呢?
网关
需求整理
- 玩家在服务端的实体可以在不同的进程中,也可以在移动到同一个进程中。
- 玩家只需要与服务端建立有限条连接,有访问到服务端进程的可能性。同时,这个连接数量不会随着服务端进程数量的增长而线性增长。
要解决这些问题需要引入反向代理的中间件,反向代理是服务端开发中常见的基础设施的抽象,简单来说就是内网进程不是借助反向代理访问外部,而是被动地挂在代理上,等待外部通过反向代理访问内部。Linux在刚支持epoll时为解决C10K问题,其核心就是借助于反向代理中间件。在游戏服务器开发中,这种组件的名字比较通用叫做网关(Gate)。
网关解决了什么问题呢?
首先,网关作为服务器可以接收客户端的连接,同时可以接收服务端进程(后端)的连接并保持通信。其次,网关能够将客户端的消息转发到对应的后端,与此对应的后端可以向网管订阅自己关注的客户端消息。对于场景服务器来说,这里可增加一个约束条件,就是限制客户端的上行消息不会被dup,而只会导入到一个后端上。
具体的做法是客户端给消息添加head,其中标记可以供网关识别,然后将消息路由到对应的后端。
站在比需求更高的层次来看网关的意义会发现,现在客户端不需要关注后端的细节,后端也不需要关注客户端的细节,网关成为这一管道中唯一的静态部分。
https://www.cnblogs.com/fingerpass/p/game-server-programming-paradigm.html