无缝世界网游服务器架构的设计(二)(转)
从一组服务器的角度来看,一般来说,我们的服务器组(Cluster)内都会有登陆验证服务器(Login Server)、持久性数据服务器(DB及DB Proxy)、连接代理服务器(Gate Server、FEP Server、Client Proxy等)以及Auto Patch Server、还有用于集中管理及控制组的服务器等等,由于这些服务器基本上什么样的架构设计都会用到,所以——现在不考虑以上这些服务器,只考虑具体处理游戏逻辑、游戏规则的各个服务器。以此为前提来分析一下 Services-based Architecture 和 Cells-based Architecture 的优缺点。
对Services-based Architecture 的分析
基于服务的架构,顾名思义这种架构的实现(程序)会是和服务的具体内容(策划)相关的,这是因为——各种【服务】内容的确定是建立于项目的【需求分析】基础上的,【需求分析】的前提是基本确定了【策划设计】,至少是项目的概要设计。
我想多数做过游戏项目的人都应该对需求变更有很深的感触,每个人都说“开始想做的那个和最后实际做出来的那个不一样”。特别是在项目的早期阶段,团队的不同成员对项目做完之后的样子有相当不同的看法(很可能大家互相都不知道对方怎么看的),这很容易理解,谁也不可能从几页纸几张图就确切地知道这个游戏做完了什么样子,即使不考虑需求变更。涉及到项目开发方法方面的东西这里就不多说了,总之我的看法就是——尽管我们不大可能设计出一个架构能够适应任何的游戏设计,但是不同开发任务间的耦合度显然还是越低越好,基于服务的架构适应需求变更的能力较差。
关于服务耦合不管如何划分service,不同 service之间都一定存在不同程度的耦合(coupling)关系,不同的 service 之间会有相互依赖关系。而你们的策划设计可能会让这种关系复杂到程序在运行时的状态很难以琢磨的程度。
![](https://www.cnblogs.com/jack-feng/admin/images/smilies/default/smile.gif)
而且他们在写文档时候的表达还多半不如我上面写的清楚,另外,他们还会把这些规则分到很多不同的文档里面去写。好吧,你肯定会想“把这两个服务合二为一好了”,实际上不管你想把哪两个(或多个)服务合并为一个服务的时候,都应该先考虑一下当时是为什么把他们独立为不同服务的?
实际上很多这样“看上去不算过分”的规则都会导致service间的频繁交互,所以每个service最好都是stateless service,这样的话情况会好很多,但是对于游戏来说这很难做到。
请求处理的时序问题服务耦合的问题在不考虑开发复杂度比较高的情况下,还是可以被搞定的,只要脑袋够清醒,愿意花够多的时间,那么还有更难以搞定的么?我看确实还有,如果你对将要面对的问题,了解得足够多的话![](https://www.cnblogs.com/jack-feng/admin/images/smilies/default/smile.gif)
上面两个序列图描述的是某个玩家做了连续做了两次同样的操作但是很可能得到了不同的结果,当然这些请求都是异步地被处理。问题的关键在于——尽管两次玩家执行的命令一样、顺序一样,甚至时间间隔都一样,但是结果却很不同——因为图(1)里面C2CS::Request_to_attack请求被处理的时候,C2IS::Request_equip_item 这个请求还没有被处理完,但是图(2)显示的情况就不一样了。因为C2IS::Request_equip_item这个操作很可能会改变游戏人物的属性,这个属性又很可能影响attack的结果。这两幅图实际上省略了 Combat Server 与 Item Server 之间的交互过程。但是已经足以说明问题了,每个Service处理每个Request时具体会消耗的时间,是无法在设计时确定的!
谁喜欢这类结果上的不确定性?举个例子:玩家很可能已经装备上了“只能使用1次的魔兽必杀刀”然后攻击了一下魔兽,但是它却没死!这会导致什么样的结果?请自行想象。另外,这种不确定性还会表现为“在项目开发期和运营期的行为差异”,或者“出现某些偶然的奇怪现象”。
那还有解决方案么?有的,其实只要序列化玩家请求的处理,使处理有序进行就可以了。但是又一次的,这会带来新的复杂度——在某个范围(整个服务器组?一个行会?一个队伍?)内,以每个玩家为单位,序列化他(们)的(可能是所有)操作,但是也显而易见,这在某种程度上降低了请求处理的并发性,尽管它对并发性的影响可能只局限于不大(最少是一个玩家)的范围。
对Cells-based Architecture 的分析
基于Cell的架构有个明显的优势就是Cell如何划分和你的策划没有关系J这是真的。而且Cell间如何交互可以被放到系统的底层,具体有多底层、多隐蔽(实际上可以隐蔽到对开发上层游戏逻辑的程序员都不可见的程度)要看你的实现如何了。如果做到了某个系统的程序设计与游戏设计完全无关的话,显然,这个系统受到游戏设计变更(需求变更)的影响就会很小很小,甚至会到完全不受影响的程度,当然这是理想情况。
关于跨边界对象交互在基于Cell的服务器架构里面,实现无缝世界(Seamless World)的主要难点在于实现跨边界对象的交互时会出现的一些问题,因为这些对象在不同的Cell进程里面,这些Cell一般来说是在不同的物理服务器上运行。
无缝世界的特点自然就是无缝,并且因为无缝给玩家带来更好的游戏体验,所以显然我们希望“跨边界对象交互”问题不把事情搞砸,那么这种交互的表现就必须满足稳定、高效的前提。一般来说,高于300ms的延迟对玩家操作来说就属于“明显可见”的程度了,不能让玩家骑着500块RMB买来的虚拟马在一片大草原上面畅快的奔跑的时候,在某个地方突然就被“看不见的墙”给“挡”了一下,因为这“墙”根本看不见,所以会很影响“上帝”的游戏心情。
关于组成整个虚拟世界的Cell之间的关系,下面来分析两种情况:
<!--[if !supportLists]-->一,
<!--[endif]-->Cell 承载的场景不重叠
![](http://canremember.com/wp-content/uploads/2008/02/cell_1.gif)
如图(1),一个连续的虚拟世界场景被分成左右两块,分别在不同的Cell Server上面运行。A、B、C分别是3个不同的游戏角色。在这种情况下B与C的交互并不存在任何障碍,因为B和C只不过是同一个物理服务器上同一个进程内的两块不同的内存数据而已。但是A与B/C的交互就不那么直接了,尽管他们所在的场景看上去是“连续的、一体的”但是事情不会像表面上那么简单。A与B发生交互时候会发生什么事情?例如A攻击了B、A与B交易物品等等,因为在这种结构下做数据同步会带来很多问题,例如对象状态不确定性、开发复杂度等等、相对来说两个Cell Server之间做网络通讯而带来的延迟可能反而是最小的问题,这些问题不需要很复杂的分析就可以得出结论,在此不再多说了。
<!--[if !supportLists]-->二,
Cell 承载的场景(部分地)重叠
![](http://canremember.com/wp-content/uploads/2008/02/cell_2.gif)
如图(2),一个连续的虚拟世界场景被分成左右两块,分别在不用的Cell Server上面运行。A、B、C、D分别是4个不同的游戏角色。这个情况下,中间的区域为2个Cell所共同维护,中间区域的对象同属于2个Cell所‘拥有’。这有什么好处?现在,任意两个对象之间,除了A与C之间的交互,都变得更‘直接’了。变得直接肯定是一件好事儿,那么A与C之间呢?他们之间其实也没有任何问题J
因为双方都已经超出了对方的Area of Interest(AoI)区域,游戏规则可以限制他们不能直接交互。
上面提到的第二种方案算不上什么魔法,但是肯定是比第一种方案更有效。接下来怎么办?假设B是个玩家,他站在中间这块区域上面时,并不会产生“我到底是在哪里”这样的疑问J
问题的关键在于对于Cell Server来说,怎么样同步那些处于重叠区域对象的状态。游戏世界内的对象可能同时处于1个、2个、3个或者4个不同的Cell Server。如果你的Cell分隔方法不限于水平线和垂直线、或者有人故意捣乱的话,还可能会更多。需要被同步的对象也不只是玩家本身,还包括怪物、NPC、一颗会走的树、某玩家在地上吐的痰等等。
由于我们的基于无缝世界的游戏规则不大会直接去限制游戏世界某处玩家的行为,也就是说玩家如果能相互交易物品的话,他们肯定希望在任何地方都能交易,“为什么其他地方都行,但是在某个墙角做交易就会导致物品丢失?”所以比较可靠的方法是建立一套的用于同步的底层机制,来同步这些跨边界对象。
怎么实现?这个话题很大,恐怕再写几篇Blog我也讲不完,但是有一些东西可以作为参考,例如:DCOM和CORBA规范,Java的RMI,基于Python的 PYRO,TAO(The ACE ORB)等等。好在分布式处理的问题不止是网络游戏会涉及到,可以借鉴的东西还是很多的。
总结很显然,这篇文章在两种架构的评价上面存在某些倾向性,但是倾向性本身只是副产品。另外一个副产品就是关于一些技术分析方法。
在考虑采用何种技术的时候,我们往往很容易地就会忽略对程序之外那些事情的影响。上面我提到的关于Services-based架构实现的时候,提到划分service及数据设计对程序设计能力的挑战、对策划设计的制约,对适应需求变更能力的影响,都不会只是空谈。这些问题也不是只在实现这种架构的时候才出现。
不要高估自己的智商,Keep It Simple and Stupid
应该可以让我们离成功更近一点儿。
对Services-based Architecture 的分析
基于服务的架构,顾名思义这种架构的实现(程序)会是和服务的具体内容(策划)相关的,这是因为——各种【服务】内容的确定是建立于项目的【需求分析】基础上的,【需求分析】的前提是基本确定了【策划设计】,至少是项目的概要设计。
我想多数做过游戏项目的人都应该对需求变更有很深的感触,每个人都说“开始想做的那个和最后实际做出来的那个不一样”。特别是在项目的早期阶段,团队的不同成员对项目做完之后的样子有相当不同的看法(很可能大家互相都不知道对方怎么看的),这很容易理解,谁也不可能从几页纸几张图就确切地知道这个游戏做完了什么样子,即使不考虑需求变更。涉及到项目开发方法方面的东西这里就不多说了,总之我的看法就是——尽管我们不大可能设计出一个架构能够适应任何的游戏设计,但是不同开发任务间的耦合度显然还是越低越好,基于服务的架构适应需求变更的能力较差。
关于服务耦合不管如何划分service,不同 service之间都一定存在不同程度的耦合(coupling)关系,不同的 service 之间会有相互依赖关系。而你们的策划设计可能会让这种关系复杂到程序在运行时的状态很难以琢磨的程度。
假设:我想我还能想出很多很多“看上去不算过分”的规则来让这个事情变得复杂无比,很可能你们的策划也在无意中,已经拥有我这种能力
服务器组内的战斗处理和物品处理分别由两个不同的服务(器)提供
游戏规则:
人物被攻击后自己携带的物品可能掉落到地上
某些物品掉落后会爆炸
物品在地上爆炸可能伤及周围(半径10米内)人物
人物之间的‘仇恨度’影响战斗数值计算
被攻击时掉落的物品爆炸后伤及的人物,会增加对‘被攻击人’的‘仇恨度’
![](https://www.cnblogs.com/jack-feng/admin/images/smilies/default/smile.gif)
而且他们在写文档时候的表达还多半不如我上面写的清楚,另外,他们还会把这些规则分到很多不同的文档里面去写。好吧,你肯定会想“把这两个服务合二为一好了”,实际上不管你想把哪两个(或多个)服务合并为一个服务的时候,都应该先考虑一下当时是为什么把他们独立为不同服务的?
实际上很多这样“看上去不算过分”的规则都会导致service间的频繁交互,所以每个service最好都是stateless service,这样的话情况会好很多,但是对于游戏来说这很难做到。
请求处理的时序问题服务耦合的问题在不考虑开发复杂度比较高的情况下,还是可以被搞定的,只要脑袋够清醒,愿意花够多的时间,那么还有更难以搞定的么?我看确实还有,如果你对将要面对的问题,了解得足够多的话
![](https://www.cnblogs.com/jack-feng/admin/images/smilies/default/smile.gif)
![](http://canremember.com/wp-content/uploads/2008/02/problem_of_services-based_arch.gif)
上面两个序列图描述的是某个玩家做了连续做了两次同样的操作但是很可能得到了不同的结果,当然这些请求都是异步地被处理。问题的关键在于——尽管两次玩家执行的命令一样、顺序一样,甚至时间间隔都一样,但是结果却很不同——因为图(1)里面C2CS::Request_to_attack请求被处理的时候,C2IS::Request_equip_item 这个请求还没有被处理完,但是图(2)显示的情况就不一样了。因为C2IS::Request_equip_item这个操作很可能会改变游戏人物的属性,这个属性又很可能影响attack的结果。这两幅图实际上省略了 Combat Server 与 Item Server 之间的交互过程。但是已经足以说明问题了,每个Service处理每个Request时具体会消耗的时间,是无法在设计时确定的!
谁喜欢这类结果上的不确定性?举个例子:玩家很可能已经装备上了“只能使用1次的魔兽必杀刀”然后攻击了一下魔兽,但是它却没死!这会导致什么样的结果?请自行想象。另外,这种不确定性还会表现为“在项目开发期和运营期的行为差异”,或者“出现某些偶然的奇怪现象”。
那还有解决方案么?有的,其实只要序列化玩家请求的处理,使处理有序进行就可以了。但是又一次的,这会带来新的复杂度——在某个范围(整个服务器组?一个行会?一个队伍?)内,以每个玩家为单位,序列化他(们)的(可能是所有)操作,但是也显而易见,这在某种程度上降低了请求处理的并发性,尽管它对并发性的影响可能只局限于不大(最少是一个玩家)的范围。
对Cells-based Architecture 的分析
基于Cell的架构有个明显的优势就是Cell如何划分和你的策划没有关系J这是真的。而且Cell间如何交互可以被放到系统的底层,具体有多底层、多隐蔽(实际上可以隐蔽到对开发上层游戏逻辑的程序员都不可见的程度)要看你的实现如何了。如果做到了某个系统的程序设计与游戏设计完全无关的话,显然,这个系统受到游戏设计变更(需求变更)的影响就会很小很小,甚至会到完全不受影响的程度,当然这是理想情况。
关于跨边界对象交互在基于Cell的服务器架构里面,实现无缝世界(Seamless World)的主要难点在于实现跨边界对象的交互时会出现的一些问题,因为这些对象在不同的Cell进程里面,这些Cell一般来说是在不同的物理服务器上运行。
无缝世界的特点自然就是无缝,并且因为无缝给玩家带来更好的游戏体验,所以显然我们希望“跨边界对象交互”问题不把事情搞砸,那么这种交互的表现就必须满足稳定、高效的前提。一般来说,高于300ms的延迟对玩家操作来说就属于“明显可见”的程度了,不能让玩家骑着500块RMB买来的虚拟马在一片大草原上面畅快的奔跑的时候,在某个地方突然就被“看不见的墙”给“挡”了一下,因为这“墙”根本看不见,所以会很影响“上帝”的游戏心情。
关于组成整个虚拟世界的Cell之间的关系,下面来分析两种情况:
<!--[if !supportLists]-->一,
<!--[endif]-->Cell 承载的场景不重叠
![](http://canremember.com/wp-content/uploads/2008/02/cell_1.gif)
如图(1),一个连续的虚拟世界场景被分成左右两块,分别在不同的Cell Server上面运行。A、B、C分别是3个不同的游戏角色。在这种情况下B与C的交互并不存在任何障碍,因为B和C只不过是同一个物理服务器上同一个进程内的两块不同的内存数据而已。但是A与B/C的交互就不那么直接了,尽管他们所在的场景看上去是“连续的、一体的”但是事情不会像表面上那么简单。A与B发生交互时候会发生什么事情?例如A攻击了B、A与B交易物品等等,因为在这种结构下做数据同步会带来很多问题,例如对象状态不确定性、开发复杂度等等、相对来说两个Cell Server之间做网络通讯而带来的延迟可能反而是最小的问题,这些问题不需要很复杂的分析就可以得出结论,在此不再多说了。
<!--[if !supportLists]-->二,
Cell 承载的场景(部分地)重叠
![](http://canremember.com/wp-content/uploads/2008/02/cell_2.gif)
如图(2),一个连续的虚拟世界场景被分成左右两块,分别在不用的Cell Server上面运行。A、B、C、D分别是4个不同的游戏角色。这个情况下,中间的区域为2个Cell所共同维护,中间区域的对象同属于2个Cell所‘拥有’。这有什么好处?现在,任意两个对象之间,除了A与C之间的交互,都变得更‘直接’了。变得直接肯定是一件好事儿,那么A与C之间呢?他们之间其实也没有任何问题J
因为双方都已经超出了对方的Area of Interest(AoI)区域,游戏规则可以限制他们不能直接交互。
上面提到的第二种方案算不上什么魔法,但是肯定是比第一种方案更有效。接下来怎么办?假设B是个玩家,他站在中间这块区域上面时,并不会产生“我到底是在哪里”这样的疑问J
问题的关键在于对于Cell Server来说,怎么样同步那些处于重叠区域对象的状态。游戏世界内的对象可能同时处于1个、2个、3个或者4个不同的Cell Server。如果你的Cell分隔方法不限于水平线和垂直线、或者有人故意捣乱的话,还可能会更多。需要被同步的对象也不只是玩家本身,还包括怪物、NPC、一颗会走的树、某玩家在地上吐的痰等等。
由于我们的基于无缝世界的游戏规则不大会直接去限制游戏世界某处玩家的行为,也就是说玩家如果能相互交易物品的话,他们肯定希望在任何地方都能交易,“为什么其他地方都行,但是在某个墙角做交易就会导致物品丢失?”所以比较可靠的方法是建立一套的用于同步的底层机制,来同步这些跨边界对象。
怎么实现?这个话题很大,恐怕再写几篇Blog我也讲不完,但是有一些东西可以作为参考,例如:DCOM和CORBA规范,Java的RMI,基于Python的 PYRO,TAO(The ACE ORB)等等。好在分布式处理的问题不止是网络游戏会涉及到,可以借鉴的东西还是很多的。
总结很显然,这篇文章在两种架构的评价上面存在某些倾向性,但是倾向性本身只是副产品。另外一个副产品就是关于一些技术分析方法。
在考虑采用何种技术的时候,我们往往很容易地就会忽略对程序之外那些事情的影响。上面我提到的关于Services-based架构实现的时候,提到划分service及数据设计对程序设计能力的挑战、对策划设计的制约,对适应需求变更能力的影响,都不会只是空谈。这些问题也不是只在实现这种架构的时候才出现。
不要高估自己的智商,Keep It Simple and Stupid
![](https://www.cnblogs.com/jack-feng/admin/images/smilies/default/smile.gif)
应该可以让我们离成功更近一点儿。