下载安卓APP箭头
箭头给我发消息

客服QQ:3315713922

什么写网络游戏服务器构架设计

作者:课课家教育     来源: http://www.kokojia.com点击数:1679发布时间: 2018-12-27 09:59:43

标签: springbootspringcloud微服务

   今天我们说说服务器划分原则,我们知道在现有的网络游戏服务器端架构中。多是以功能和场景来划分服务器结构的,负载均衡和集群暂且不在本文中讨论(bigworld、atlas)。

     同时服务器划分可以基于以下原则:

  1,分离游戏中占用系统资源(cpu,内存,IO等)较多的功能,独立成服务器。

  2,以多线程或多进程的编程方式适应多核处理器。

  3,在同一个服务器架构下,应尽可能的复用某些服务器(进程级别的复用,比如3DMAX建筑设计场景服务器)。

  4,运行时玩家数据的保存、修改及数据流向应该是设计的焦点,它同时也决定了服务器应该如何划分。

  5,服务器的划分应该适度,在保证清晰的数据流向的前提下,根据游戏的类型和规模尽量减少服务器或服务器进程的个数,以减少服务器之间过多的复制数据、锁冲突(使用共享内存进行通讯时)。

  6,主要按照场景划分进程,若需按功能划分,必须保持整个逻辑足够简单,并满足以上1、3两点。

    二、运行时的玩家数据,

  网络游戏服务器程序一项重要的工作就是根据client发过来的数据包,在服务器端模拟玩家的行为操作并把这些行为广播出去。那么服务器程序在运行时就需要一些实体来保存玩家的数据,这些实体可以是一个类,也可以是一个线程,设计思想不同采用的实体差别也会很大。这里涉及服务器端设计的一个核心问题:运行时玩家数据的保存、修改及数据流向。

  云风通过抽象实体agent来处理单个client的服务请求,agent和client是1:1的关系(见图1)。agent是在gate程序后端,负责翻译、转发以及回应客户端发过来的请求。agent的主要工作内容见云风的《开发笔记(1)》。值得补充的是设计agent的主要优势是:

  把对单个client服务的代码集中写在agent服务中。由agent再和内部其它服务沟通。数据加载使用共享内存的方案,由agent向持久化模块发出信号,做加载或纯盘处理,通过共享内存得到结构化数据块。

  agent相当于client在服务器上对应的实体,玩家的属性和数据只能由agent来修改,别的服务只有读权限。通过attach操作获得数据(attach可能是通过服务器通讯框架skynet,也有可能直接mmap到共享内存sharedb上以获得数据)。

  agent的设计使得整个系统对玩家数据的修改只有一个输入点,数据流十分的明确,易于维护。虽然这种设计可能会照成数据的多次复制,但是带来的代码维护和查错上的便利是十分可观的。

  如果把所有的agent放在同一个进程里,在编程该程序时还应该考虑到容错问题,比如说(1)使用C++编写这个程序,agent以类的形式存在,使用threadpool来处理收到的数据包,实际操作时thread的数量是会远远小于agent的数量的,数据包到达后会在队列里等待thread调用agent的逻辑来处理。这是一种比较常见的设计方法,但要注意的是由于agent都放在一个进程里,程序的健壮性要求很高,一个进程core则会导致全服玩家掉线。而使用C++编写也增加了宕进程的可能性……..你懂的。(2)使用java编写,对于这种“中心节点”式架构来说可能是更好的选择,起码不是因为一个玩家的误操作(可能使用外挂)导致全服玩家掉线。(3)云风似乎是使用luacoroutine来实现agent的相互隔离和协同工作的,这样可以减少单一agent失败对其他agent的影响(动态语言的好处)。

  sharedb在系统中的地位看上去像是database前端的cache,但就本人的理解sharedb的作用远不止是一个数据缓存。

  和天龙八部的ShareMemory类似,sharedb也采用了定长的结构化数据(见《开发笔记(6)》),通过共享内存来实现进程间的数据共享。sharedb的存在使得游戏逻辑处理和数据保存逻辑得到很好的隔离,游戏逻辑不用关心后端的数据是如何保存的,只要sharedb挂上定期存盘的服务,在接口定义明确的情况下,后端到底采用什么样的数据库变得不是那么重要,从而降低了系统的耦合度。

            三、服务器底层框架skynet

  skynet的设计思想见《Skynet设计综述》:

  我希望我们的游戏服务器(但skynet不仅限于用于游戏服务器)能够充分利用多核优势,将不同的业务放在独立的执行环境中处理,协同工作。这个执行环境,最早的时候,我期望是利用OS的进程,后来发现,如果我们必定采用嵌入式语言,比如Lua的话,独立OS进程的意义不太大。LuaState已经提供了良好的沙盒,隔离不同执行环境。而多线程模式,可以使得状态共享、数据交换更加高效。而多线程模型的诸多弊端,比如复杂的线程锁、线程调度问题等,都可以通过减小底层的规模,精简设计,最终把危害限制在很小的范围内。这一点,Skynet最终花了不到3000行C代码来实现核心层的代码,一个稍有经验的C程序员,都可以在短时间理解,做维护工作。

  做为核心功能,Skynet仅解决一个问题:

  把一个符合规范的C模块,从动态库(so文件)中启动起来,绑定一个永不重复(即使模块退出)的数字id做为其handle。模块被称为服务(Service),服务间可以自由发送消息。每个模块可以向Skynet框架注册一个callback函数,用来接收发给它的消息。每个服务都是被一个个消息包驱动,当没有包到来的时候,它们就会处于挂起状态,对CPU资源零消耗。如果需要自主逻辑,则可以利用Skynet系统提供的timeout消息,定期触发。

  Skynet提供了名字服务,还可以给特定的服务起一个易读的名字,而不是用id来指代它。id和运行时态相关,无法保证每次启动服务,都有一致的id,但名字可以。

  本人感觉skynet像一个发布订阅的消息中间件(还没看源码,可能有误),这种基于服务的即插即用式的框架给服务器端带来很大的可扩展性,同时也使得各模块之间独立清晰,具有良好的可维护性。但是这里有个疑问,服务都以so的形式挂在skynet上,那么这些服务从哪里获取玩家、怪物、NPC等object的数据?是从skynet中获得还是直接从sharedb中获得,出于性能的考虑是不是要把skynet和sharedb部署在同一台物理主机上?这样一来就会增加设计和具体逻辑的耦合度。看了《Skynet集群及RPC》,感觉skynet上的服务是要通过skynet来获得玩家的数据,这样操作会不会导致数据被复制很多次,不知道最终的效率是否受到影响?

        四、gate

  满足服务器划分原则里的第一点:分离游戏中占用系统资源(cpu,内存,IO等)较多的功能,独立成服务器。

  gate的主要工作可以参见本系列blog的第二篇《网络游戏服务器构架设计(二):刀剑Online-连接负载服务器CLS》

        五、场景管理器

  主要用于管理静态场景和动态副本,比如agent登录时查询自己所在场景对应的服务器地址。

         六、场景服务器

  场景服务器的内容我没有从《开发笔记》中得到太多的信息(可能level太低),更多的是以功能模块的形式写,比如springboot,AOI。不过其中有一点比较新颖的是云风认为player的位置、动作状态,战斗数值状态等都是场景的一部份,应该保存在场景中而不是agent中。本节有所更正见(八)补充。

  据我的猜测,场景服务器应该会负责:

  怪物行走控制,player移动更新及位置同步

  怪物AI策略

  区域性广播,场景广播

  战斗逻辑

  AOI服务(AreaOfInterest)

  碰撞检测

  自动寻径

  需要注意的是场景服务器修改的一些数据应该以什么样的频率通知agent呢?比如player的位置信息,该信息是完全保存在场景数据里还是说agent里也有一份?

  

赞(16)
踩(0)
分享到:
华为认证网络工程师 HCIE直播课视频教程