经典游戏服务器端架构实例分析

发布时间:2022-01-06 14:38:51 作者:iii
来源:亿速云 阅读:136

本篇内容主要讲解“经典游戏服务器端架构实例分析”,感兴趣的朋友不妨来看看。本文介绍的方法操作简单快捷,实用性强。下面就让小编来带大家学习“经典游戏服务器端架构实例分析”吧!

架构的分析模型

一. 讨论的背景

现代电子游戏,基本上都会使用一定的网络功能。从验证正版,到多人交互等等,都需要架设一些专用的服务器,以及编写在服务器上的程序。因此,游戏服务器端软件的架构,本质上也是游戏服务器这个特定领域的软件架构。

软件架构的分析,可以通过不同的层面入手。比较经典的软件架构描述,包含了以下几种架构:

二. 游戏服务器架构的要素

服务器端软件的本质,是一个会长期运行的程序,并且它还要服务于多个不定时,不定地点的网络请求。所以这类软件的特点是要非常关注稳定性和性能。这类程序如果需要多个协作来提高承载能力,则还要关注部署和扩容的便利性;同时,还需要考虑如何实现某种程度容灾需求。由于多进程协同工作,也带来了开发的复杂度,这也是需要关注的问题。

功能约束,是架构设计决定性因素。一个万能的架构,必定是无能的架构。一个优秀的架构,则是正好把握了对应业务领域的核心功能产生的。游戏领域的功能特征,于服务器端系统来说,非常明显的表现为几个功能的需求:

针对以上的需求特征,在服务器端软件开发上,我们往往会关注软件对电脑内存和CPU的使用,以求在特定业务代码下,能尽量满足承载量和响应延迟的需求。最基本的做法就是“时空转换”,用各种缓存的方式来开发程序,以求在CPU时间和内存空间上取得合适的平衡。在CPU和内存之上,是另外一个约束因素:网卡。网络带宽直接限制了服务器的处理能力,所以游戏服务器架构也必定要考虑这个因素。

对于游戏服务器架构设计来说,最重要的是利用游戏产品的需求约束,从而优化出对此特定功能最合适的“时-空”架构。并且最小化对网络带宽的占用。

经典游戏服务器端架构实例分析
[图-游戏服务器的分析模型]

三. 核心的三个架构

基于上述的分析模型,对于游戏服务端架构,最重要的三个部分就是,如何使用CPU、内存、网卡的设计:

本文的讨论,也主要是集中于对以上三个架构的分析。

四. 游戏服务器模型的进化历程

最早的游戏服务器是比较简单的,如UO《网络创世纪》的服务端一张3.5寸软盘就能存下。基本上只是一个广播和存储文件的服务器程序。后来由于国内的外挂、盗版流行,各游戏厂商开始以MUD为模型,建立主要运行逻辑在服务器端的架构。这种架构在MMORPG类产品的不断更新中发扬光大,从而出现了以地图、视野等分布要素设计的分布式游戏服务器。而在另外一个领域,休闲游戏,天然的需要集中超高的在线用户,所以全区型架构开始出现。现代的游戏服务器架构,基本上都希望能结合承载量和扩展性的有点来设计,从而形成了更加丰富多样的形态。
本文的讨论主要是选取这些比较典型的游戏服务器模型,分析其底层各种选择的优点和缺点,希望能探讨出更具广泛性,更高开发效率的服务器模型。

分服模型

一. 模型描述

分服模型是游戏服务器中最典型,也是历久最悠久的模型。其特征是游戏服务器是一个个单独的世界。每个服务器的帐号是独立的,而且只用同一服务器的帐号才能产生线上交互。在早期服务器的承载量达到上限的时候,游戏开发者就通过架设更多的服务器来解决。这样提供了很多个游戏的“平行世界”,让游戏中的人人之间的比较,产生了更多的空间。所以后来以服务器的开放、合并形成了一套成熟的运营手段。一个技术上的选择最后导致了游戏运营方式的模式,是一个非常有趣的现象。
经典游戏服务器端架构实例分析
[图-单进程调度模型]

  • 多进程游戏服务器
    多进程的游戏服务器系统,最早起源于对于性能问题需求。由于单进程架构下,总会存在承载量的极限,越是复杂的游戏,其单进程承载量就越低,因此开发者们一定要突破进程的限制,才能支撑更复杂的游戏。
    一旦走上多进程之路,开发者们还发现了多进程系统的其他一些好处:能够利用上多核CPU能力;利用操作系统的工具能更仔细的监控到运行状态、更容易进行容灾处理。多进程系统比较经典的模型是“三层架构”:
    在多进程架构下,开发者一般倾向于把每个模块的功能,都单独开发成一个进程,然后以使用进程间通信来协调处理完整的逻辑。这种思想是典型的“管道与过滤器”架构模式思想——把每个进程看成是一个过滤器,用户发来的数据包,流经多个过滤器衔接而成的管道,最后被完整的处理完。由于使用了多进程,所以首选使用单进程单线程来构造其中的每个进程。这样对于程序开发来说,结构清晰简单很多,也能获得更高的性能。
    经典游戏服务器端架构实例分析
    [图-对象树架构]

    在Objective C语言中,有所谓autorealse的特性,这种特性实际上是一种引用计数的技术。由于能配合在某个调度模型下,所以使用起来会比较简单。同样的思想,有些开发者会使用一些智能指针,配合自己写的框架,在完整的业务逻辑调用后一次性清理相关内存。
    经典游戏服务器端架构实例分析
    [图-预分配内存池]

    不过这样做,同样有一些缺点:首先是不太好部署,比如你想在某个资源较小的虚拟机上部署一套用来测试,可能一位内没改内存池的大小,导致启动不成功。每次更换环境都需要修改这个配置。其次,是所有的用到的类对象,都要在根节点对象那里有个指针或者引用,否则就可能泄漏内存。由于对于非基本类型的对象,我们一般不喜欢用拷贝的方式来作为函数的参数和返回值,而指针和应用所指向的内存,如果不能new的话,只能是现成的某个对象的成员属性。这回导致程序越复杂,这类的成员属性就越多,这些属性在代码维护是一个不小的负担。

    要解决以上的缺点,可以修改内存池的实现,为动态增长,但是具备上限的模型,每次从内存池中“获取”对象的时候才new。这样就能避免在小内存机器上启动不了的问题。对于对象属性复杂的问题,一般上需要好好的按面向对象的原则规划代码,做到尽量少用仅仅表示函数参数和返回值的属性,而是主要是记录对象的“业务状态”属性为主,多花点功夫在构建游戏的数据模型上。

  • 四. 进程间通讯手段

    在多进程的系统中,进程间如何通讯是一个至关重要的问题,其性能和使用便利性,直接决定了多进程系统的技术效能。

    1. Socket通讯
      TCP/IP协议是一种通用的、跨语言、跨操作系统、跨机器的通讯方案。这也是开发者首先想到的一种手段。在使用上,有使用TCP和UDP两个选择。一般我们倾向在游戏系统中使用TCP,因为游戏数据的逻辑相关性比较强,UDP由于可能存在的丢包和重发处理,在游戏逻辑上的处理一般比较复杂。由于多进程系统的进程间网络一般情况较好,UDP的性能优势不会特别明显。

      要使用TCP做跨进程通讯,首先就是要写一个TCP Server,做端口监听和连接管理;其次需要对可能用到的通信内容做协议定制;最后是要编写编解码和业务逻辑转发的逻辑。这些都完成了之后,才能真正的开始用来作为进程间通信手段。

      使用Socket编程的好处是通用性广,你可以用来实现任何的功能,和任何的进程进行协作。但是其缺点也异常明显,就是开发量很大。虽然现在有一些开源组件,可以帮你简化Socket Server的编写工作,简化连接管理和消息分发的处理,但是选择目标建立连接、定制协议编解码这两个工作往往还是要自己去做。游戏的特点是业务逻辑变化很多,导致协议修改的工作量非常大。因此我们除了直接使用TCP/IP socket以外,还有很多其他的方案可以尝试。
      经典游戏服务器端架构实例分析
      [图-消息队列]

      需要注意的是,有些开发者缺乏经验,使用了数据库,如MySQL,或者是NFS这类运行效率比较低的媒介作为队列的存储者。这在功能上虽然可以行得通,但是操作一频繁,就难以发挥作用了。如以前有一些手机短信应用系统,就用MySQL来存储“待发送”的短信。

      消息队列虽然非常好用,但是我们还是要自己对消息进行编解码,并且分发给所需要的处理程序。在消息到处理程序之间,存在着一个转换和对应的工作。由于游戏逻辑的繁多,这种对应工作完全靠手工编码,是比较容易出错的。所以这里还有进一步的改进空间。

    2. 远程调用
      有一些开发者会希望,在编码的时候完全屏蔽是否跨进程在进行调用,完全可以好像调用本地函数或者本地对象的方法一样。于是诞生了很多远程调用的方案,最经典的有Corba方案,它试图实现能在不同语言的代码直接,实现远程调用。JAVA虚拟机自带了RMI方案的支持,在JAVA进程之间远程调用是比较方便的。在互联网的环境下,还有各种Web Service方案,以HTTP协议作为承载,WSDL作为接口描述。

      使用远程调用的方案,最大好处是开发的便捷,你只需要写一个函数,就能在任何一个其他进程上对此函数进行调用。这对游戏开发来说,就解决了多进程方案最大的一个开发效率问题。但是这种便捷是有成本的:一般来说,远程调用的性能会稍微差一点,因为需要用一套统一的编解码方案。如果你使用的是C/C++这类静态语言,还需要使用一种IDL语言来先描述这种远程函数的接口。但是这些困难带来的好处,在游戏开发领域还是非常值得的。

      经典游戏服务器端架构实例分析
      [图-服务器状态管理]

      尽管用简单的目录服务器可以实现大部分容灾和扩容的需求,但是如果被访问进程的内存中有数据存在,那么问题就比较复杂了。对于容灾来说,新的进程必须要有办法重建那个“失效”了的进程内存中的数据,才可能完成容灾功能;对于扩容功能来说,新加入的进程,也必须能把需要的数据载入到自己的内存中才行,而这些数据,可能已经存在于其他平行的进程中,如何把这部分数据转移过来,是一个比较耗费性能和需要编写相当多代码的工作。——所以一般我们喜欢对“无状态”的进程来做扩容和容灾。

      到此,相信大家对“经典游戏服务器端架构实例分析”有了更深的了解,不妨来实际操作一番吧!这里是亿速云网站,更多相关内容可以进入相关频道进行查询,关注我们,继续学习!

    推荐阅读:
    1. 什么是架构?Untiy开发游戏使用什么架构合适?
    2. 游戏引擎架构

    免责声明:本站发布的内容(图片、视频和文字)以原创、转载和分享为主,文章观点不代表本网站立场,如果涉及侵权请联系站长邮箱:is@yisu.com进行举报,并提供相关证据,一经查实,将立刻删除涉嫌侵权内容。

    服务器

    上一篇:如何分析C语言关键字中的void、const和return

    下一篇:怎么快速部署国人开源的Java博客系统Tale

    相关阅读

    您好,登录后才能下订单哦!

    密码登录
    登录注册
    其他方式登录
    点击 登录注册 即表示同意《亿速云用户服务条款》