刚开始时以为做游戏服务器和做web差不多,但是经过一段时间之后,才发现代码太多,太乱了,一看代码都想重构,都是踩着坑往前走。这里我把一些游戏开发方面的东西整理一下,希望能对那些想做游戏服务器开发的朋友有所帮助 近年来,我身边的朋友有很多都从web转向了游戏开发。他们以前都没有做过游戏服务器开发,更谈不上什么经验,而从网上找的例子或游戏方面的知识,又是那么的少,那么的零散。当他们进入游戏公司时,显得一脸茫然。如果是大公司还好点,起码有人带带,能学点经验,但是有些人是直接进入了小公司,甚至这些小公司只有他一个后台。他们一肩扛起了公司的游戏后端的研发,也扛起了公司的成败。他们也非常尽力,他们也想把游戏的后端做好。可是就是因为没什么经验,刚开始时以为做游戏服务器和做web差不多,但是经过一段时间之后,才发现代码太多,太乱了,一看代码都想重构,都是踩着坑往前走。这里我把一些游戏开发方面的东西整理一下,希望能对那些想做游戏服务器开发的朋友有所帮助。 首先,要明确一点,做游戏服务器开发和做传统的web开发有着本质的区别。游戏服务器开发,如果没有经验,一开始根本没有一个明确清析的目标,不像web那样,有些明确的MVC架构,往往就是为了尽快满足策划的需求,尽快的实现功能,尽快能让游戏跑起来。但是随着功能越来越多,在老代码上面修改的越来越频繁,游戏测试时暴露出来的一堆bug,更让人觉得束手无策,这个时候我们想到了重构,想到了架构的设计。 游戏的构架设计非常重要,好的构架代码清析,责任明确,扩展性强,易调试。这些会为我们的开发省去不少时间。那要怎么样设计游戏的构架呢?可能每个游戏都不一样,但是本质上还是差不多的。 对于游戏服务器的构架设计,我们首先要了解游戏的服务器构架都有什么组成的?一款游戏到上线,需要具备哪些功能?有些人可能会说,只要让游戏跑起来,访问服务器不出问题不就行了吗?答案是不行的,游戏构架本身代表的是一个体系,它包括: 1,系统初始化 这一系统的东西都是不可少的,它们共同服务于游戏的整个运营过程。我们一点点来介绍各个系统的功能。 一,系统初始化 系统初始化是在没有客户端连接的时候,服务器启动时所需要做的工作。基本上就是配置文件的读取,初始化系统参数。但是我们必须要考虑的是,系统初始化需要的参数配置在哪儿,是配置在本地服务器,还是配置在数据库,服务器启的时候去数据库取。配置的修改需不需要重启服务器等。 二,游戏逻辑 游戏逻辑是游戏的核心功能实现,也是整个游戏的服务中心,它被开发的好坏,直接决定了游戏服务器在运行中的性能。那在游戏逻辑的开发中我们要注意些什么呢? (1)网络通信 游戏是一种网络交互比较强的业务,好的底层通信,可以最大化游戏的性能,增加单台服务器处理的同时在线人数,给游戏带来更好的体验,至少不容易出现因为网络层导致的数据交互卡顿的现象。在这里我推荐使用Netty,它是目前最流行的NIO框架,它的用法可以在我之前的文章中查看,这里不再多说了。 有人疑问,代码也需要分层次?这个是当然了,不同的代码,代表了不同的功能实现。现在的开发语言都是面向对象的,如果我们不加思考,不加整理的把功能代码乱堆一起,起始看起来是快速实现了功能,但是到后期,如果要修改需求,或在原来的代码上增加新的需求,那真是被自己打败了。所以代码一定要分层,主要有以下几层: a,协议层,也叫前后台交互层,它主要负责与前台交互协议的解析和返回数据。在这一层基本上没有什么业务逻辑实现。与前台交互的数据都在这一层开始,也在这一层终止。比如你使用了Netty框架,那么Netty的ChannelHandlerContext即Ctx只能出现在这一层,他不能出现到游戏业务逻辑代码的实现中,接收到客户端的请求,在这一层把需要的参数解析出来,再把参数传到业务逻辑方法中,业务逻辑方法处理完后,把要返回给客户端的数据再返回到这一层,在这一层组织数据,返回给客户端,这样就可以把业务逻辑和网络层分离,业务逻辑只关心业务实现,而且也方便对业务逻辑进行单元测试。 b,业务逻辑层,这里处理真正的游戏逻辑,该计算价格计算价格,该通关的通关,该计时的计时。该保存数据的保存数据。但是这一层不直接操作缓存或数据库,只是处理游戏逻辑计算。因为业务逻辑层是整个游戏事件的处理核心,所以他的处理是否正确直接决定游戏的正确性。所以这一层的代码要尽量使用面向对角的方法去实现。不要出现重复代码或相似的功能进行复制粘贴,这样修改起来非常不方便,可能是修改了某一处,而忘记了修改另外同样的代码。还要考虑每个方法都是可测试的,一个方法的行数最好不要超过一百行。另外,可以多看看设计模式的书,它可以帮助我们设计出灵活,整洁的代码。 三,数据库系统 数据库是存储数据库的核心,但是游戏数据在存储到数据库的时候会经过网络和磁盘的IO,它的访问速度相对于内存来说是很慢的。一般来说,每次访问数据库都要和数据库建立连接,访问完成之后,为了节省数据库的连接资源,要再把连接断开。这样无形中又为服务器增加了开销,在大量的数据访问时,可能会更慢,而游戏又是要求低延时的,这时该怎么办呢?我们想到了数据库连接池,即把访问数据库的连接放到一个地方管理,用完我不断开,用的时候去那拿,用完再放回去。这样不用每次都建立新的连接了。但是如果要我们自己去实现一套连接池管理组件的话,需要时间不说,对技术的把控也是一个考验,还要再经过测试等等,幸好互联网开源的今天,有一些现成的可以使用,这里推荐Mybatis,即实现了代码与SQL的分离,又有足够的SQL编写的灵活性,是一个不错的选择。 四,缓存系统 游戏中,客户端与服务器的交互是要求低延迟的,延迟越低,用户体验越好。像之前说过的一样,低延迟就是要求服务器处理业务尽量的快,客户端一个请求过来,要在最短的时间内响应结果,最低不得超过500ms,因为加上来回的网络传输耗时,基本上就是600ms-到700ms了,再长玩家就会觉得游戏卡了。如果直接从数据库中取数据,处理完之后再存回数据库的话,这个性能是跟不上的。在服务器,数据在内存中处理是最快的,所以我们要把一部分常用的数据提前加载到内存中,比如说游戏数据配置表,经常登陆的玩家数据等。这样在处理业务时,就不用走数据库了,直接从内存中取就可以了,速度更快。游戏中常见的缓存有两种,1,直接把数据存储在jvm或服务器内存中,2,使用第三方的缓存工具,这里推荐Redis,详细的用法可以自己去查询。 五,游戏日志 日志是个好东西呀,一个游戏中更不能少了日志,而且日志一定要记录的详细。它是玩家在整个游戏中的行为记录,有了这个记录,我们就可以分析玩家的行为,查找游戏的不足,在处理玩家在游戏中的问题时,日志也是一个良好的凭证和快速处理方式。 六, 游戏管理工具 游戏管理工具是一个不断增涨的系统,因为它很多时候是伴随着游戏中遇到的问题而实现的。但是根据经验,有一些功能是必须有的,比如:服务器管理,主要负责服务器的开启,关闭,服务器配置信息,玩家信息查询,玩家管理,比如踢人,封号;统计查询,玩家行为日志查询,统计查询,次留率查询,邮件服务,修改玩家数据等,根据游戏的不同要求,凡是可以能过工具实现的,都做到游戏管理工具里面。它是针对所有服务器的管理。一个好的,全的游戏管理工具,可以提高游戏运营中遇到问题处理的效率,为玩家提供更好的服务。 七,公共组件 公共组件是为游戏运行中提供公共的服务,比如,充值服务器,我们没必须一个服用一个充值,而且你也不能对外提供多个充值服务器地址,和第三方公司对接,他们绝对不干,这是要疯呀;还有运营搞活动时的礼包码,还有注册用户的管理,玩家一个注册账号可以进不同的区等。这些都是针对所有区服提供的服务,所以要单独做,与游戏逻辑分开,这样方便管理,部署和负载均衡。还有SDK的登陆验证,现在手游比较多,与渠道对接里要进行验证,这往往是很多http请求,速度慢,所以这个也要拿出来单独做,不要在游戏逻辑中去验证,因为网络IO的访问时间是不可控制的,http是阻塞的请求。 服务器端开发的一些建议 本文作为游戏服务器端开发的基本大纲,是游戏实践开发中的总结。第一部分专业基础,用于指导招聘和实习考核, 第二部分游戏入门,讲述游戏服务器端开发的基本要点,第三部分服务端架构,介绍架构设计中的一些基本原则。希望能帮到大家 一 专业基础 1.1 网络 1.1.1 理解TCP/IP协议 2.1防御式编程 3.1 什么是好的架构? 负载均衡是一个很复杂的课题,这里暂不谈bigworld和atlas的这类服务器的设计,更多的是基于功能和场景划分服务器结构。 首先说一下思路,服务器划分基于以下原则: 分离游戏中占用系统资源(cpu,内存,IO等)较多的功能,独立成服务器。
各个服务器的简要说明: Gateway 是应用网关,主要用于保持和client的连接,该服务器需要2种IO,对client采用高并发连接,低吞吐量的网络模型,如IOCP等,对服务器采用高吞吐量连接,如阻塞或异步IO。 网关主要有以下用途: 分担了网络IO资源 管理和维护多个Scene Server。 所有玩家的移动类操作都在该服务器上做检查,所以该服务器本身具备所有地图的地形等相关信息。具体检查过程是这样的:首先,Worldserver收到一个移动信息,WorldServer收到后向Phys Server请求检查,Phys Server检查成功后再返回给world Server,然后world server传递给相应的Scene Server。 Scene Server 场景服务器,按场景划分,每个服务器负责的场景应该是可以配置的。理想情况下是可以动态调节的。 ItemMgr Server 物品管理服务器,负责所有物品的生产过程。在该服务器上存储一个物品掉落数据库,服务器初始化的时候载入到内存。任何需要产生物品的服务器均与该服务器直接通信。 AIServer 又一个功能服务器,负责管理所有NPC的AI。AI服务器通常有2个输入,一个是Scene Server发送过来的玩家相关操作信息,另一个时钟Timer驱动,在这个设计中,对其他服务器来说,AIServer就是一个拥有很多个NPC的客户端。AIserver需要同步所有与AI相关的数据,包括很多玩家数据。由于AIServer的Timer驱动特性,可在很大程度上使用TBB程序库来发挥多核的性能。 把网络游戏服务器分拆成多个进程,分开部署。这种设计的好处是模块自然分离,可以单独设计。分担负荷,可以提高整个系统的承载能力。 缺点在于,网络环境并不那么可靠。跨进程通讯有一定的不可预知性。服务器间通讯往往难以架设调试环境,并很容易把事情搅成一团糨糊。而且正确高效的管理多连接,对程序员来说也是一项挑战。 前些年,我也曾写过好几篇与之相关的设计。这几天在思考一个问题:如果我们要做一个底层通用模块,让后续开发更为方便。到底要解决怎样的需求。这个需求应该是单一且基础的,每个应用都需要的。 正如 TCP 协议解决了互联网上稳定可靠的点对点数据流通讯一样。游戏世界实际需要的是一个稳定可靠的在游戏系统内的点对点通讯需要。 我们可以在一条 TCP 连接之上做到这一点。一旦实现,可以给游戏服务的开发带来极大的方便。 可以把游戏系统内的各项服务,包括并不限于登陆,拍卖,战斗场景,数据服务,等等独立服务看成网络上的若干终端。每个玩家也可以是一个独立终端。它们一起构成一个网络。在这个网络之上,终端之间可以进行可靠的连接和通讯。 实现可以是这样的:每个虚拟终端都在游戏虚拟网络(Game Network)上有一个唯一地址 (Game Network Address , GNA) 。这个地址可以预先设定,也可以动态分配。每个终端都可以通过游戏网络的若干接入点 ( GNAP ) 通过唯一一条 TCP 连接接入网络。接入过程需要通过鉴权。 鉴权过程依赖内部的安全机制,可以包括密码证书,或是特别的接入点区分。(例如,玩家接入网络就需要特定的接入点,这个接入点接入的终端都一定是玩家) 鉴权通过后,网络为终端分配一个固定的游戏域名。例如,玩家进入会分配到 player.12345 这样的域名,数据库接入可能分配到 database 。 游戏网络默认提供一个域名查询服务(这个服务可以通过鉴权的过程注册到网络中),让每个终端都能通过域名查询到对应的地址。 然后,游戏网络里所有合法接入的终端都可以通过其地址相互发起连接并通讯了。整个协议建立在 TCP 协议之上,工作于唯一的这个 TCP 连接上。和直接使用 TCP 连接不同。游戏网络中每个终端之间相互发起连接都是可靠的。不仅玩家可以向某个服务发起连接,反过来也是可以的。玩家之间的直接连接也是可行的(是否允许这样,取决于具体设计)。 由于每个虚拟连接都是建立在单一的 TCP 连接之上。所以减少了互连网上发起 TCP 连接的各种不可靠性。鉴权过程也是一次性唯一的。并且我们提供域名反查服务,我们的游戏服务可以清楚且安全的知道连接过来的是谁。 系统可以设计为,游戏网络上每个终端离网,域名服务将广播这条消息,通知所有人。这种广播服务在互联网上难以做到,但无论是广播还是组播,在这个虚拟游戏网络中都是可行的。 在这种设计上。在逻辑层面,我们可以让玩家直接把聊天信息从玩家客互端发送到聊天服务器,而不需要建立多余的 TCP 连接,也不需要对转发处理聊天消息做多余的处理。聊天服务器可以独立的存在于游戏网络。也可以让广播服务主动向玩家推送消息,由服务器向玩家发起连接,而不是所有连接请求都是由玩家客互端发起。 虚拟游戏网络的构成是一个独立的层次,完全可以撇开具体游戏逻辑来实现,并能够单独去按承载量考虑具体设计方案。非常利于剥离出具体游戏项目来开发并优化。 最终,我们或许需要的一套 C 库,用于游戏网络内的通讯。api 可以和 socket api 类似。额外多两条接入与离开游戏网络即可。 |
|Archiver|手机版|小黑屋|软件开发编程门户 ( 陇ICP备2024013992号-1|甘公网安备62090002000130号 )
GMT+8, 2024-11-21 14:31 , Processed in 0.039251 second(s), 16 queries .
Powered by Discuz! X3.5
© 2001-2024 Discuz! Team.