原文件Pdf下载: Next_Generation_MMO_Architecture.zip
官网下载:http://www.next-gen.cc/index.php?option=com_content&view=article&id=2&Itemid=3
nextgen架构官网:http://www.next-gen.cc
翻译:赵月明
摘要
本文阐述了一种MMO架构的实现,架构充分利用了微线程和多核处理器,使服务器能够承载更多的玩家。本方案使用erlang实现服务器集群,并将游戏世界实现分区。
I 介绍
MMOG(大型多人在线游戏)让数千计甚至数以百万计的人玩同一个游戏。新一代MMO 将提供一个高动态的游戏设计。让游戏设计者的设计充分自由将提升了技术门槛。
Achterbosch,皮尔斯,西蒙斯(2008),对下一代MMO期望是什么这儿问题调查了122人。
当问及到什么样的特性是令人满意的,受访者提出了一些想法:
作为6.2.1指出的,宁愿玩家影响世界,许多受访者转而提议:
世界靠其自身的自然环境事件发生改变。这些例子包括毁灭森林的火灾,淹没陆地的洪水,生长的植被,衰减的人类建筑和道路等等。这要创造一个身临其境的世界,只是增加了昼夜循环和过去天气的改变。燃烧的森林可能最终再生,洪水可能平息,还有npc(非游戏角色)可能维护衰退的地区,以至于世界没有发生戏剧性的改变。
另一个有趣但视为不可行的建议:许多受访者相信他们的角色应该能够产生影响,在游戏世界中,会永远完全的改变它。一个任务中的部分生物和NPC,每一个玩家完成任务之后都要重置,以便下一个玩家完成同样的任务。这通常被称为重生。
当一个被杀的怪在世界里完全健康的重置,被称为重生。一个参与调查者提出:开发者永远改变世界,使怪或者任务不能重生。
问题是:早晚没有任务留下,没有怪兽留下去被杀死。因为玩家已经完成任务,并杀死了所有的怪兽。开发者必须赶在玩家完成之前,快速的创造出新的任务。 这是不可能的。
游戏开发者不断地创造新的内容视乎是不可行的,但是动态设计游戏能够自动的搜索适当的时机建立任务。
动态NPC也是一项受访者期望并有技术挑战的特色。受访者表示,NPC过于静态,因此在提升游戏真实体验方面,NPC应该进行一些日常的活动。如同在单人游戏里一样被遗忘。
大多数情况下,友好的NPC昼夜闲站着等待玩家与他们沟通,在这种情况下他们只有一个目的,快速执行简单的一句话,商店购物,或者给玩家一个任务。敌人的NPC趋向于站着等死。
一个受访者建议,NPC可以被雇佣,做一些搜集资源保护建筑物的任务。
据一些受访者所说,NPC除了站立还会有一个目的用途。
另一个受访者建议:玩家应该有NPC敌人的选择权,用很小的时间间隔来增大预测难度。就像龙与地下城中的地下城主的情况。
下一代的MMO特性要求架构先进, 允许动态游戏设计、分发和缩放。在大多数编程语言中,这是一个难以处理的任务。要开发满足这些需求的服务,需要新工具和编程语言提供更好的支持。
如果这一需求能够实现,玩家会有丰富的体验,让他们感觉到在自己的影响下,世界发生持续改变。
II 背景
基本上有两种不同类型的MMOG架构:服务器-客户端模式和点对点。
A客户/服务器结构
通过(Cai, Xavier, Turner, and Lee, 2002) 和(Gil, Tavares, and Roque, 2005)的讨论,集中服务器-客户端实现是现在最普遍的MMO’S架构方案。每个玩家通过客户端连接游戏服务。服务器可以验证客户端发送的所有信息和防作弊。,让服务器处理所有的事件,可以非常容易的保持世界的一致性状态。
问题是在于,当玩家连接足够多时,服务器将耗尽硬件资源,然后拒绝更多的连接。解决这个问题最常见的方案是:分区,游戏复制到许多分区服务器上,这些服务器上存储了数以千计的玩家,并维持他们在世界中的状态。
负面效应是,玩家基地被孤立的分布在这些服务器上。意味着如果两个朋友想在一起玩,他们必须在同一台服务器上。另一个问题是平衡人口,如果服务器有很多玩家,人们不得不等待一个长队来登录。如果一个服务器玩家很少,人们可能很难有机会和其他玩家一起玩,这样就降低了游戏的玩法。
B 点对点架构
分散的点对点实现是通过by (Hampel, Bopp, and Hinn, 2007) 和
(el Rhalibi and Merabti, 2005)提出的,他让每个客户端成为一个地理有限区域的服务器。这避免了使用集中服务的伸缩性问题,但很大程度上提升了作弊的风险,增大了所有玩家状态连贯性维护的复杂性。
C Erlang
Erlang 是一个内置并发性的语言,erlang为每一个对象或任务创建一个轻型进程,代替一个连续运行的主循环.每个进程都有其自己的主循环。依赖于硬件,一个普通的桌面计算机可以建立数以万计的进程。Erlang附带了一个称为OTP(开放通信平台)的平台。为构建服务,OTP提供了通用的设计模式:状态机、容错系统、实行热代码升级。Erlang也有自己的分布式数据库系统Mneisa.如果架构用c++或者java实现,这些特色必须从头开发或者
借助于第三方库。 erlang 代码被组织在被称为模块的文件里,模块里是被写好的函数。据(Nystr?m, 2004)说,选择erlang为实现语言,通过使用其轻量级的进程就能使服务器的性能得到显著提升。
D 理论研究
在近期流行的这些游戏中,有许多对提升MMO的研究。与本架构最相关的是对世界分区的研究。此研究就是在本章节中对讨论和批评。
有两个商业项目Eve Online (CCP) 和 Second Life (Linden Lab)使用了将世界分成子区域的技术。
研究论文如(Chen, Wu, Knutsson, Lu, and Amza, 2005), (Yamamoto, Murat, Yasumoto, and Ito, 2005) 和 (Bossche, Verdickt, and Vleeschauwer, 2006)提到以允许架构平衡的方式将游戏划分成一些子区域。当一个子区域变得有负载的时候,建议移植到新的服务器。使用这样一个伸缩性的限制,一个子地区的玩家数量不可能超过计算机负载人数上限。By assigning the computers to sub areas instead removes this limitation. (联系上下文,没翻译出来)。所需要的是子区域状态必须分布在指定的计算机上。
E 商业项目
EvE在线是一个发生在太空的游戏。架构将世界划分为若干子地区(星系)。集群包括刀片服务器,也称为节点。每个星系驻留在一个节点上。若干星系可以共享一个节点。玩家在节点间无缝转移。当一个玩家“跳”到另一个恒星系统,一个加载屏幕出现而转移发生。
据称,约60名玩家一起参加战斗足以搞垮一个节点。如果节点移动到一台专业的服务器上,大约能够负载大约1000次战斗或者交换玩家。利用机器所有的CPU内核资源是一个令开发者感到棘手问题。在集群中,有大约五成的内核处于闲置状态。?insertref? 是 EvE 服务器实现所用的命令式设计语言 python的东西。 Python开发人员称之为“全局解释器锁”。在其内部主要部分没有重新设计的情况下,他有效的阻止Python使用多核。
第二人生(游戏名称)也有类似的地区服务器架构。当无缝转移玩家时,他们穿过了世界。没有前端集群服务器为客户端进行通讯。客户端保持四个连接,连接最近的四个地区服务器。
地区服务器将所有数据流实时传给玩家,没有内容保存硬盘,这样使客户端会很小。
III 推荐建构
如图1所示:架构由四个类型的服务器通过一个计算机集群组成。服务器由:链接服务器,账户服务器,角色服务器,区域服务器。这些服务器运行在一个计算机网络(局域网)的不同服的计算机上。然而这不是必需的,所有的服务器也可以配置部署在同一台计算机上。服务器软件用Erlang实施,因为他具有并发、容错和分布式特性。客户端通过互联网链接到链接服务器链接服务器操纵客户端和其他服务器之间的通讯。该帐户服务器包含用于验证登录的账户信息。所有的玩家角色存储在角色服务器上,角色是玩家在游戏中的替身或者实体。地区服务器应该运行的像一个地理网格连接地区。如图2所示, 他们在一起,是一个玩家相互作用的世界。每个服务器运行在一个由名称、@和主机名称组成的节点上。例如:完整的节点名称是start_area@myserver。 start_area是节点名,myserver是主机名。节点名作用于运行在不同计算机上的服务器之间的通讯。
A 客户端
客户端是运行在玩家计算机上的程序。通过互联网与链接服务器建立链接。客户端是系统中非常重要的部分,但不属于架构的内容。只要是能够建立TCP链接,能够与服务器发送和接受数据的语言就能实现。客户端的目的是通过图像或者文本接口为玩家展示世界。
B 链接服务器
链接服务器等待客户端的链接,服务于集群的前端。客户端从不会直接与其他服务器直接通讯,所有与客户端的通讯必须通过链接服务器。为每个客户端创建一个链接进程。链接进程是一个在四种状态间变化的简单有限状态机。
*链接状态:客户端已经建立一个链接,但是没有发送任何进一步的指令请求。
*大厅状态:客户端已经登录账号,浏览或者在游戏中建立了实体。
*玩:客户端已经用一个实体联机,目前正在游戏中玩。
*链接丢失状态:客户端已经失去链接。链接服务器保持玩家有效,给他一个再次登录的机会继续玩游戏。当断开时间超时,链接服务器断开session并终止。
C 账户服务器
如图3所示:账户服务器在一个Mneisa数据库中存储了所有的用户账号信息。如图3所示:账户服务器在一个Mneisa数据库中存储了所有的用户账号信息。通过账号和密码验证玩家的合法性。
D 角色服务器
如图4所示:角色服务器存储了所有的角色数据。每个账户能创建能够登陆游戏世界的新角色。服务器提供了保存和创建新角色的功能。
E 地区服务器
地区服务器是集群中最复杂的服务器。他管理一个世界中的一个地区。为在地区服务器加载完成的每一个角色或对象,创建一个保存其状态的进程。图5阐述此过程。在游戏世界里,每个进程有一个通过erlang生成的自定义id和一个作为唯一标示id。唯一标示id和进程id存储在一个用来查询对象的数据库中。
F 动态特性
如图6所示,地区服务器有一个模块化的构建。它核心是仅仅提供了一个简单事件注册和启动与选择函数库的能力。这些库依次为地区服务器提供所有的功能。这些函数库在运行时,可以编译、重新编译、启动和停止。他们为开发和测试提供了新类库,例如一个战斗系统无需重新启动服务器。
架构提供了三个默认库:
* libstd:这个库提供了基本的功能,如注册/检索对象、对象记录和热代码升级。
* libchar:这个库允许角色在地区服务器登录。
*libdist: 这个库允许一个地区服务在集群中跨多个计算机分发。
注意不是直接启动库,而是启动他们的监督者。依次启动库以及库运行所需要的其他任何进程。这样,模块libstd_sup提供了一个开始函数代替libstd。在erlang中,监督者是一个监督其他进程的的进程,如果一个进程崩溃,监督者进程会重启它。下面是一个命令启动一个库展示:
- lib_sup:start(libstd_sup).
- =PROGRESS REPORT==== 7-Jul-2009::12:34:09
- supervisor: {local,libstd_sup}
- started: [{pid,<0.270.0>},
- {name,’LibStd’},
- {mfa,{libstd,start_link,[std_funs]}},
- {restart_type,permanent},
- {shutdown,2000},
- {child_type,worker}]
- =PROGRESS REPORT==== 7-Jul-2009::12:34:09
- supervisor: {local,libstd_sup}
- started: [{pid,<0.271.0>},
- {name,’ObjectSupervisor’},
- {mfa,{obj_sup,start_link,[]}},
- {restart_type,permanent},
- {shutdown,2000},
- {child_type,supervisor}]
- =PROGRESS REPORT==== 7-Jul-2009::12:34:09
- supervisor: {local,lib_sup}
- started: [{pid,<0.269.0>},
- {name,libstd_sup},
- {mfa,{libstd_sup,start_link,[]}},
- {restart_type,transient},
- {shutdown,infinity},
- {child_type,supervisor}]
- {ok,<0.269.0>}
用下面的命令关闭一个库:
- lib_sup:stop(libstd_sup).
- Ok
另一个动态特性被称为热代码交换。这使得Erlang在运行的同时升级代码。为此,一个名为upgrade的函数被添加到类库libstd。接下来一个例子,对模块libchar做了修改之后,新的代码成功编译,通过以下命令完成升级:
- libstd:upgrade(libchar).
- {module, libchar}
Erlang允许两个版本的代码同时运行。当升级函数被调用的模块作为参数时,erlang让当前运行的代码为旧的,让已升级的作为现在的。升级之后,当前代码(新)被使用,原来执行旧代码的所有进程,在下个函数调用时将被升级。
G 分布
数台电脑可以管理同一个地区服务器。如图7中,两台分别名为server1和server2的计算机运行各自名为start_area节点。运行下面的命令,从节点start_area@server2将关联位于start_area@server1的分布式对象记录。
- libstd:join(start_area@server1).
- {ok, [start_area@server1]}
分布式对象记录是一个Mnesia数据库,存储的信息和本地对象记录一样,不同之处是分布式对象记录不只是包含本地进程,还有所有地区服务器中的管理同一个地区的所有进程。(不知道翻译的对不对)
如果start_area@server2往分布式对象记录中写数据,erlang也同样将修改同步到start_area@server1。erlang进程id在本地是透明的,这就意味着如果进程运行在同一个本地机器或者同一个网络中的另一台计算机,他们的通讯是没有问题的。分布式注册对象表将start_area@server1和start_area@server2链接在一起,这就使他们如同在同一个地区服务器上操作一样。
G1 负载均衡
libdist库提供动态负载分布的特征
当管理同一片区域的所有服务器开启时更新分布式Mnsia表。分布式库通过检查本地区域节点负载 与 表中的最少负载的节点进行对比。如果负载差值等于或大于1时,则节点开始将一半的进程移动到负载最小的地区服务器上去。
G.2 迁移
分布式的增加,在系统运行时,使客户端没有任何察觉的情况下,角色在从一台服务器迁徙到另一台服务器成为可能。下面解释如何迁移:
如图8所示,一个对象接收到一个将要迁徙到另外一个节点的消息。这个消息通常是由分布式库 libdist 发送,但也可以通过以下命令从节点手动发送。
- ProcessId ! {migrate, start_area@server1}.
如图9所示:在迁徙目标节点,当收到一个迁徙消息时,当前对象进程开始一个带有相等id和状态的新进程。新进程id被返回到位于start_area@server2上旧的进程。当一个新进程被创建,他覆盖了当前在全局注册表中拥有自己进程id的数据条目,因此所有的消息应该发给他自己。
图10所描述,这时,即使所有消息仍然发送给旧的进程,旧的进程会转发给新的进程。
图11所示, 如果旧进程一秒钟之内无法接受新的消息,将杀死自己,我们认为迁移是成功的。
IV 效果
用于测试的服务器处理器Phenom(tm) II X4 940,8G内存。地区的大小为10000公尺,在一个625公尺的四边形中,四叉树将世界拆分成256个四边形。在测试中,地区里生成了一些机器人对象。机器人大约每7.5秒(5+5秒内随即)前后移动改变方向。
每个动作更新都要发送给在同一区块中的其他机器人对象,这就意味着与系统中机器人数量相比,消息数量是呈指数增长的。
A CPU负载
图12展示了在系统中机器人增长同时所有CPU核心的负载情况。CPU负载呈指数增长,当24000个并发机器人运行时,所有的核心负载都接近了100%。
当机器人达到24000时,在一个四边形区块中每秒钟处理1178个消息,在整个地区服务器中,每秒钟处理了301568个消息。
B 内存负载
在图13中显示的是链接服务器,角色服务器、账户服务器和地区服务器的内存负载情况。
当24000个并发机器人运行时,地区服务器使用内存619M的时候。其他服务器内存负载基本保持不变,仍然在40-43M之间。
C 视觉效果
当一个客户端登陆,大约60-80个机器人被做了统计。视觉定时被用来检查所有明显视觉延迟。根据不同的负载水平,随即挑选一些机器人,他们的运动变化在iphone上面同步。6000机器人在地区服务器上时,平均运动变化时间是7.1秒。(我应该估量一下12000和24000个机器人时的情况,但在机器人中做了一个日志功能来代替手动计时。)
V讨论
测试数以千计的客户端在现实的环境是一个问题,因为很难获得如此数量的计算机。从同一台计算机上运行数以千计的客户端可能会造成瓶颈,不是每个客户端都有他们自己的计算机。所有的测试计算机都通过同一个局域网内连接,真实的生产环境或许不会是这样的。当从世界各地通过互联网连接时,响应时间可能会增加。(我还未测试这些)。在这种情况下,服务器集群的地理位置也会影响到客户端的响应时间。
当游戏的特性数据向进程状态增加时,地区服务器的内存消耗可能会增加。在测试时,地区服务器的进程中有一个极小的状态,可以充分的管理角色登陆和迁徙过程。
在没有检查探测已经登陆的账户或者角色,又或者角色是否属于账号的登陆之前,账号和角色登陆序列是不安全的。(感觉没翻译清楚。)
将世界划分在地区服务器中可能并不是最好的方案。一个更好的方式是做出一个模型:对统一服务器做一个继承,分支出包含区域/太阳系系统的银河服务器,同样区域/太阳系系统包含星球服务器,星球服务器又被划分成区域服务器。该方法将可以使它通过自上而下的方式更加容易的分发消息。这将使星际旅行甚至从星球表面到它周围太空的无缝运动变得如此简单。
VI 结论和未来工作
以后再翻译。
原文件Pdf下载: Next_Generation_MMO_Architecture.zip
nextgen架构官网:http://www.next-gen.cc
由于本人英文水平太烂,本文完全是为学习之用的业余翻译。希望对大家有用~~ 文章难免多处错误,欢迎批评指正。
若转载,请附上原文链接:http://blog.chinaunix.net/uid-429659-id-3396370.html