转自:http://www.jb51.net/article/119274.htm 侵权删程序员
近年来,我身边的朋友有不少都从web转向了游戏开发。他们之前都没有作过游戏服务器开发,更谈不上什么经验,而从网上找的例子或游戏方面的知识,又是那么的少,那么的零散。当他们进入游戏公司时,显得一脸茫然。若是是大公司还好点,起码有人带带,能学点经验,可是有些人是直接进入了小公司,甚至这些小公司只有他一个后台。他们一肩扛起了公司的游戏后端的研发,也扛起了公司的成败。他们也很是尽力,他们也想把游戏的后端作好。但是就是由于没什么经验,刚开始时觉得作游戏服务器和作web差很少,可是通过一段时间以后,才发现代码太多,太乱了,一看代码都想重构,都是踩着坑往前走。这里我把一些游戏开发方面的东西整理一下,但愿能对那些想作游戏服务器开发的朋友有所帮助。web
首先,要明确一点,作游戏服务器开发和作传统的web开发有着本质的区别。游戏服务器开发,若是没有经验,一开始根本没有一个明确清析的目标,不像web那样,有些明确的MVC架构,每每就是为了尽快知足策划的需求,尽快的实现功能,尽快能让游戏跑起来。可是随着功能愈来愈多,在老代码上面修改的愈来愈频繁,游戏测试时暴露出来的一堆bug,更让人以为一筹莫展,这个时候咱们想到了重构,想到了架构的设计。算法
游戏的构架设计很是重要,好的构架代码清析,责任明确,扩展性强,易调试。这些会为咱们的开发省去很多时间。那要怎么样设计游戏的构架呢?可能每一个游戏都不同,可是本质上仍是差很少的。数据库
对于游戏服务器的构架设计,咱们首先要了解游戏的服务器构架都有什么组成的?一款游戏到上线,须要具有哪些功能?有些人可能会说,只要让游戏跑起来,访问服务器不出问题不就好了吗?答案是不行的,游戏构架自己表明的是一个体系,它包括:编程
1,系统初始化
2,游戏逻辑
3,数据库系统
4,缓存系统。
5,游戏日志
6,游戏管理工具
7,公共服务组件后端
这一系统的东西都是不可少的,它们共同服务于游戏的整个运营过程。咱们一点点来介绍各个系统的功能。设计模式
一,系统初始化api
系统初始化是在没有客户端链接的时候,服务器启动时所须要作的工做。基本上就是配置文件的读取,初始化系统参数。可是咱们必需要考虑的是,系统初始化须要的参数配置在哪儿,是配置在本地服务器,仍是配置在数据库,服务器启的时候去数据库取。配置的修改需不须要重启服务器等。数组
二,游戏逻辑缓存
游戏逻辑是游戏的核心功能实现,也是整个游戏的服务中心,它被开发的好坏,直接决定了游戏服务器在运行中的性能。那在游戏逻辑的开发中咱们要注意些什么呢?
(1)网络通讯
游戏是一种网络交互比较强的业务,好的底层通讯,能够最大化游戏的性能,增长单台服务器处理的同时在线人数,给游戏带来更好的体验,至少不容易出现由于网络层致使的数据交互卡顿的现象。在这里我推荐使用Netty,它是目前最流行的NIO框架,它的用法能够在我以前的文章中查看,这里再也不多说了。
有人疑问,代码也须要分层次?这个是固然了,不一样的代码,表明了不一样的功能实现。如今的开发语言都是面向对象的,若是咱们不加思考,不加整理的把功能代码乱堆一块儿,起始看起来是快速实现了功能,可是到后期,若是要修改需求,或在原来的代码上增长新的需求,那真是被本身战胜了。因此代码必定要分层,主要有如下几层:
a,协议层,也叫先后台交互层,它主要负责与前台交互协议的解析和返回数据。在这一层基本上没有什么业务逻辑实现。与前台交互的数据都在这一层开始,也在这一层终止。好比你使用了Netty框架,那么Netty的ChannelHandlerContext即Ctx只能出如今这一层,他不能出现到游戏业务逻辑代码的实现中,接收到客户端的请求,在这一层把须要的参数解析出来,再把参数传到业务逻辑方法中,业务逻辑方法处理完后,把要返回给客户端的数据再返回到这一层,在这一层组织数据,返回给客户端,这样就能够把业务逻辑和网络层分离,业务逻辑只关心业务实现,并且也方便对业务逻辑进行单元测试。
b,业务逻辑层,这里处理真正的游戏逻辑,该计算价格计算价格,该通关的通关,该计时的计时。该保存数据的保存数据。可是这一层不直接操做缓存或数据库,只是处理游戏逻辑计算。由于业务逻辑层是整个游戏事件的处理核心,因此他的处理是否正确直接决定游戏的正确性。因此这一层的代码要尽可能使用面向对角的方法去实现。不要出现重复代码或类似的功能进行复制粘贴,这样修改起来很是不方便,多是修改了某一处,而忘记了修改另外一样的代码。还要考虑每一个方法都是可测试的,一个方法的行数最好不要超过一百行。另外,能够多看看设计模式的书,它能够帮助咱们设计出灵活,整洁的代码。
三,数据库系统
数据库是存储数据库的核心,可是游戏数据在存储到数据库的时候会通过网络和磁盘的IO,它的访问速度相对于内存来讲是很慢的。通常来讲,每次访问数据库都要和数据库创建链接,访问完成以后,为了节省数据库的链接资源,要再把链接断开。这样无形中又为服务器增长了开销,在大量的数据访问时,可能会更慢,而游戏又是要求低延时的,这时该怎么办呢?咱们想到了数据库链接池,即把访问数据库的链接放到一个地方管理,用完我不断开,用的时候去那拿,用完再放回去。这样不用每次都创建新的链接了。可是若是要咱们本身去实现一套链接池管理组件的话,须要时间不说,对技术的把控也是一个考验,还要再通过测试等等,幸亏互联网开源的今天,有一些现成的可使用,这里推荐Mybatis,即实现了代码与SQL的分离,又有足够的SQL编写的灵活性,是一个不错的选择。
四,缓存系统
游戏中,客户端与服务器的交互是要求低延迟的,延迟越低,用户体验越好。像以前说过的同样,低延迟就是要求服务器处理业务尽可能的快,客户端一个请求过来,要在最短的时间内响应结果,最低不得超过500ms,由于加上来回的网络传输耗时,基本上就是600ms-到700ms了,再长玩家就会以为游戏卡了。若是直接从数据库中取数据,处理完以后再存回数据库的话,这个性能是跟不上的。在服务器,数据在内存中处理是最快的,因此咱们要把一部分经常使用的数据提早加载到内存中,好比说游戏数据配置表,常常登录的玩家数据等。这样在处理业务时,就不用走数据库了,直接从内存中取就能够了,速度更快。游戏中常见的缓存有两种,1,直接把数据存储在jvm或服务器内存中,2,使用第三方的缓存工具,这里推荐Redis,详细的用法能够本身去查询。
五,游戏日志
日志是个好东西呀,一个游戏中更不能少了日志,并且日志必定要记录的详细。它是玩家在整个游戏中的行为记录,有了这个记录,咱们就能够分析玩家的行为,查找游戏的不足,在处理玩家在游戏中的问题时,日志也是一个良好的凭证和快速处理方式。
在游戏中,日志分为:1,系统日志,主要记录游戏服务器的系统状况。好比:数据库可否正常链接,服务器是否正常启动,数据是否正常加载;2,玩家行为日志,好比玩家发送了什么请求,获得了什么物品,消费了多少货币等等;3,统计日志,这种日志是对游戏中全部玩家某种行为的一种统计,根据这个统计来分析大部分玩家的行为,得出一些共性或不一样之处,以方法运营作不一样的活动吸引用户消费。
在构架设计中,日志记录必定要作为一种强制行为,由于不强制的话,可能因为某种缘由某个功能忘记加日志了,那么当这个功能出问题了,或者运营跟咱们要这个功能的一些数据库,就傻眼了。又得加需求,改代码了。日志必定要设计一种良好的格式,日志记录的数据要容易读取,分解。日志行为能够用枚举描述,在功能最后的处理方法里面加上这个枚举作为参数,这样无论谁在调用这个方法时,都要去加参数描述。
俗话说,工欲善其事,必先利其器。游戏管理工具是对游戏运行中的一系列问题处理的一种工具。它不只是给开发人员用,大多数是给运营使用。游戏上线后,咱们须要针对线上的问题进行不一样的处理。不可能把全部问题都让程序员去处理吧,因而程序员们想到了一个办法,给大家作一个工具,大家爱谁处理谁处理去吧。
六, 游戏管理工具
游戏管理工具是一个不断增涨的系统,由于它不少时候是伴随着游戏中遇到的问题而实现的。可是根据经验,有一些功能是必须有的,好比:服务器管理,主要负责服务器的开启,关闭,服务器配置信息,玩家信息查询,玩家管理,好比踢人,封号;统计查询,玩家行为日志查询,统计查询,次留率查询,邮件服务,修改玩家数据等,根据游戏的不一样要求,凡是能够能过工具实现的,都作到游戏管理工具里面。它是针对全部服务器的管理。一个好的,全的游戏管理工具,能够提升游戏运营中遇到问题处理的效率,为玩家提供更好的服务。
七,公共组件
公共组件是为游戏运行中提供公共的服务,好比,充值服务器,咱们没必须一个服用一个充值,并且你也不能对外提供多个充值服务器地址,和第三方公司对接,他们绝对不干,这是要疯呀;还有运营搞活动时的礼包码,还有注册用户的管理,玩家一个注册帐号能够进不一样的区等。这些都是针对全部区服提供的服务,因此要单独作,与游戏逻辑分开,这样方便管理,部署和负载均衡。还有SDK的登录验证,如今手游比较多,与渠道对接里要进行验证,这每每是不少http请求,速度慢,因此这个也要拿出来单独作,不要在游戏逻辑中去验证,由于网络IO的访问时间是不可控制的,http是阻塞的请求。
因此,综上来看,一个游戏服务器起码有几个大的功能模块组成:a,游戏逻辑工程;b,日志处理工程;c,充值工程;d,游戏管理工具工程;e,用户登录工程;f,公共活动工程等,根据游戏的不一样须要,可能还有其它的。所在在构架的设计中,必定要考虑到系统的分布式部署,尽可能把公共的功能拆出来作,这样能够加强系统的可扩展性。
服务器端开发的一些建议
本文做为游戏服务器端开发的基本大纲,是游戏实践开发中的总结。第一部分专业基础,用于指导招聘和实习考核, 第二部分游戏入门,讲述游戏服务器端开发的基本要点,第三部分服务端架构,介绍架构设计中的一些基本原则。但愿能帮到你们
一 专业基础
1.1 网络
1.1.1 理解TCP/IP协议
网络传输模型
滑动窗口技术
创建链接的三次握手与断开链接的四次握手
链接创建与断开过程当中的各类状态
TCP/IP协议的传输效率
思考
1)请解释DOS攻击与DRDOS攻击的基本原理
2)一个100Byte数据包,精简到50Byte, 其传输效率提升了50%
3)TIMEWAIT状态怎么解释?
1.1.2 掌握经常使用的网络通讯模型
Select
Epoll,边缘触发与平台出发点区别与应用
Select与Epoll的区别及应用
1.2 存储
计算机系统存储体系
程序运行时的内存结构
计算机文件系统,页表结构
内存池与对象池的实现原理,应用场景与区别
关系数据库MySQL的使用
共享内存
1.3 程序
对C/C++语言有较深的理解
深入理解接口,封装与多态,而且有实践经验
深入理解经常使用的数据结构:数组,链表,二叉树,哈希表
熟悉经常使用的算法及相关复杂度:冒泡排序,快速排序
二 游戏开发入门
2.1防护式编程
不要相信客户端数据,必定要检验。做为服务器端你没法肯定你的客户端是谁,你也不能假定它是善意的,请作好自我保护。(这是判断一个服务器端程序员是否入门的基本标准)
务必对于函数的传人参数和返回值进行合法性判断,内部子系统,功能模块之间不要太过信任,要求低耦合,高内聚
插件式的模块设计,模块功能的健壮性应该是内建的,尽可能减小模块间耦合
2.2 设计模式
道法天然。不要迷信,迷恋设计模式,更不要生搬硬套
简化,简化,再简化,用最简单的办法解决问题
借大宝一句话:设计本天成,妙手偶得之
2.3 网络模型
自造轮子: Select, Epoll, Epoll必定比Select高效吗?
开源框架: Libevent, libev, ACE
2.4 数据持久化
自定义文件存储,如《梦幻西游》
关系数据库: MySQL
NO-SQL数据库: MongoDB
选择存储系统要考虑到因素:稳定性,性能,可扩展性
2.5 内存管理
使用内存池和对象池,禁止运行期间动态分配内存
对于输入输出的指针参数,严格检查,宁滥勿缺
写内存保护。使用带内存保护的函数(strncpy, memcpy, snprintf, vsnprintf等),严防数组下标越界
防止读内存溢出,确保字符串以'\0'结束
2.6 日志系统
简单高效,大量日志操做不该该影响程序性能
稳定,作到服务器崩溃是日志不丢失
完备,玩家关键操做必定要记日志,理想的状况是经过日志能重建任什么时候刻的玩家数据
开关,开发日志的要加级别开关控制
2.7 通讯协议
采用PDL(Protocol Design Language), 如Protobuf,能够同时生成先后端代码,减小先后端协议联调成本, 扩展性好
JSON,文本协议,简单,自解释,无联调成本,扩展性好,也很方便进行包过滤以及写日志
自定义二进制协议,精简,有高效的传输性能,彻底可控,几乎无扩展性
2.8 全局惟一Key(GUID)
为合服作准备
方便追踪道具,装备流向
每一个角色,装备,道具都应对应有全局惟一Key
2.9 多线程与同步
消息队列进行同步化处理
2.10 状态机
强化角色的状态
前置状态的检查校验
2.11 数据包操做
合并, 同一帧内的数据包进行合并,减小IO操做次数
单副本, 用一个包尽可能只保存一份,减小内存复制次数
AOI同步中减小中间过程无用数据包
2.12 状态监控
随时监控服务器内部状态
内存池,对象池使用状况
帧处理时间
网络IO
包处理性能
各类业务逻辑的处理次数
2.13 包频率控制
基于每一个玩家每条协议的包频率控制,瘫痪变速齿轮
2.14 开关控制
每一个模块都有开关,能够紧急关闭任何出问题的功能模块
2.15 反外挂反做弊
包频率控制能够消灭变速齿轮
包id自增校验,能够消灭WPE
包校验码能够消灭包拦截篡改
图形识别吗,能够踢掉99%非人的操做
魔高一尺,道高一丈
2.16 热更新
核心配置逻辑的热更新,如防沉迷系统,包频率控制,开关控制等
代码基本热更新,如Erlang,Lua等
2.17 防刷
关键系统资源(如元宝,精力值,道具,装备等)的产出记日志
资源的产出和消耗尽可能依赖两个或以上的独立条件的检测
严格检查各项操做的前置条件
校验参数合法性
2.18 防崩溃
系统底层与具体业务逻辑无关,能够用大量的机器人压力测试暴露各类bug,确保稳定
业务逻辑建议使用脚本
系统性的保证游戏不会崩溃
2.19 性能优化
IO操做异步化
IO操做合并缓写 (事务性的提交db操做,包合并,文件日志缓写)
Cache机制
减小竞态条件 (避免频繁进出切换,尽可能减小锁定使用,多线程不必定因为单线程) 多线程不必定比单线程快
减小内存复制
本身测试,用数听说话,别猜
2.20 运营支持
接口支持:实时查询,控制指令,数据监控,客服处理等
实现考虑提供Http接口
2.21 容灾与故障预案
略
三 服务器端架构
3.1 什么是好的架构?
知足业务要求
能迅速的实现策划需求,响应需求变动
系统级的稳定性保障
简化开发。将复杂性控制在架构底层,下降对开发人员的技术要求,逻辑开发不依赖于开发人员自己强大的技术实力,提升开发效率
完善的运营支撑体系
3.2 架构实践的思考
简单,知足需求的架构就是好架构
设计性能,抓住重要的20%, 不必从程序代码里面去抠性能
热更新是必须的
人不免会犯错,尽量的用一套机制去保障逻辑的健壮性
游戏服务器的设计是一项很有挑战性的工做,游戏服务器的发展也由之前的单服结构转变为多服机构,甚至出现了bigworld引擎的分布式解决方案,最近了解到Unreal的服务器解决方案atlas也是基于集群的方式。
负载均衡是一个很复杂的课题,这里暂不谈bigworld和atlas的这类服务器的设计,更多的是基于功能和场景划分服务器结构。
首先说一下思路,服务器划分基于如下原则:
分离游戏中占用系统资源(cpu,内存,IO等)较多的功能,独立成服务器。
在同一服务器架构下的不一样游戏,应尽量的复用某些服务器(进程级别的复用)。
以多线程并发的编程方式适应多核处理器。
宁肯在服务器之间多复制数据,也要保持清晰的数据流向。
主要按照场景划分进程,若需按功能划分,必须保持整个逻辑足够的简单,并知足以上1,2点。
服务器结构图:
各个服务器的简要说明:
Gateway 是应用网关,主要用于保持和client的链接,该服务器须要2种IO,对client采用高并发链接,低吞吐量的网络模型,如IOCP等,对服务器采用高吞吐量链接,如阻塞或异步IO。
网关主要有如下用途:
分担了网络IO资源
同时,也分担了网络消息包的加解密,压缩解压等cpu密集的操做。
隔离了client和内部服务器组,对client来讲,它只须要知道网关的相关信息便可(ip和port)。
client因为一直和网关保持常链接,因此切换场景服务器等操做对client来讲是透明的。
维护玩家登陆状态。
World Server 是一个控制中心,它负责把各类计算资源分布到各个服务器,它具备如下职责:
管理和维护多个Scene Server。
管理和维护多个功能服务器,主要是同步数据到功能服务器。
复杂转发其余服务器和Gateway之间的数据。
实现其余须要跨场景的功能,如组队,聊天,帮派等。
Phys 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 相似。额外多两条接入与离开游戏网络便可。