轻松实现可伸缩性,容错性,和负载平衡的大规模多人在线系统

上传人:飞*** 文档编号:52773101 上传时间:2018-08-25 格式:PDF 页数:9 大小:14.20KB
返回 下载 相关 举报
轻松实现可伸缩性,容错性,和负载平衡的大规模多人在线系统_第1页
第1页 / 共9页
轻松实现可伸缩性,容错性,和负载平衡的大规模多人在线系统_第2页
第2页 / 共9页
轻松实现可伸缩性,容错性,和负载平衡的大规模多人在线系统_第3页
第3页 / 共9页
轻松实现可伸缩性,容错性,和负载平衡的大规模多人在线系统_第4页
第4页 / 共9页
轻松实现可伸缩性,容错性,和负载平衡的大规模多人在线系统_第5页
第5页 / 共9页
点击查看更多>>
资源描述

《轻松实现可伸缩性,容错性,和负载平衡的大规模多人在线系统》由会员分享,可在线阅读,更多相关《轻松实现可伸缩性,容错性,和负载平衡的大规模多人在线系统(9页珍藏版)》请在金锄头文库上搜索。

1、轻松实现可伸缩性, 容错性,和负载平衡的大规模多人在线系统简介:本文以我的 OpenPoker项目为例介绍另一种构建大规模多人在线系统的方案。 OpenPoker是一个大型多人扑克网游,内建支持了容错能力,负载平衡和无限 制的规模大小。 OpenPoker的源代码遵循 GPL协议可以从我的网站下载,大约包 含一万行代码,有三分之一是用来测试的。在 Openpoker最终版出台之前,我花了很大精力设计参考,尝试过Delphi, Python, C# ,C/C+ 还有 Scheme 。我甚至还用 Common Lisp完成了一个可运行的 Poker 引擎。虽然我花了9 个多月研究设计,最终代码编写

2、却只用了6 个星期, 这最后的高效率要归功于选择了Erlang 作为 编写平台。根据比较,老版本的OpenPoker需要 45 个人的小组 9 个月时间完成。原班人 马还另外完成了一个Windows版的客户端,就算把这个开发时间的一半(1 个半 月)算进去,也比预期的18 个月少得多,就当今游戏开发的客观环境,如此可 观的时间节省不可小看!什么是 Erlang 我建议你先读一下Erlang FAQ,不过我在这里尽量概括一下:Erlang 是一个结构化,动态类型编程语言,内建并行计算支持。最初是由爱立 信专门为通信应用设计的, 比如控制交换机或者变换协议等,因此非常适合于构 建分布式,实时软并行

3、计算系统。使用 Erlang 编写出的应用运行时通常由成千上万个轻量级进程组成,并通过消 息传递相互通讯。进程间上下文切换对于Erlang 来说仅仅只是一两个环节,比 起 C程序的线程切换要高效得多得多了。使用 Erlang 来编写分布式应用要简单的多,因为它的分布式机制是透明的:对 于程序来说并不知道自己是在分布式运行。Erlang 运行时环境是一个虚拟机,有点像Java 虚拟机,这样代码一经编译,同 样可以随处运行。 它的运行时系统甚至允许代码在不被中断的情况下更新。另外 如果你需要更高效的话,字节代码也可以编译成本地代码运行。请参考 Erlang 网站上的教程、文档、范例等精彩资源。为什

4、么选择 Erlang 内建的并行计算模型使得Erlang 非常适合编写多人在线服务器。具有良好伸缩性的大型多人后台系统用Erlang 是这样构建的:由很多被分配不 同任务的“节点 (Node)”组成的“集群 (Cluster)”。一个 Erlang 节点就是一 个 Erlang 虚拟机的实例, 你可以在一台机器 (服务器,台式机或者笔记本电脑 ) 上运行多个节点。我推荐一块CPU 一个 节点。Erlang 节点自动跟踪所有连接着的其他节点。要添加一个节点你仅仅需要把它 指向任何一个已建节点就可以了。只要这两个节点建立了连接, 所有其他的节点 马上就会感应到新加入的节点。Erlang 进程使用进

5、程 ID 向其他进程传递报文,进程ID 包含着运行此进程的节 点的信息。因此进程不需要理会正在与其交流的其他进程实际在何处运行。一组 相互连接的 Erlang 节点可以看作是一个网格计算体或者一台超级计算机。将大型多人在线游戏里的玩家,NPC以及其他个体抽象为很多并行运行的进程是 最理想的,但是通常并行运算的实现让人十分头疼。Erlang 天生就是简化并行 计算的实现。Erlang 语法里的比特操作让二进制操作变得异常简单,极大发挥了Perl 和 Python 的打包/ 解包结构。使得Erlang 非常适合操作二进制网络通讯协议。OpenPoker的体系结构OpenPoker里的一切的一切都是

6、进程。玩家,机器人,游戏,抬面等等等等,都 是一个个进程。每一个连接到 OpenPoker的客户端都有一个扮演“代理”角色的 玩家进程用来处理网络消息。 取决于玩家是否登陆, 某些消息被忽略而有些被传 递到处理游戏逻辑的进程。纸牌游戏进程是一个状态机宿主:由多种游戏状态的各种状态机模块组成。这样 我的纸牌游戏进程可以向乐高积木一样随意添砖加瓦只要加入状态机就可 以添加新的纸牌游戏。此方案可以参考我写的初始函数(在 cardgame.erl里) 纸牌游戏状态机根据目前游戏的状态接受不同的消息。而且我用一个独立的进城 来处理通用信息,比如跟踪玩家状态,抬面情况,各种限制等等。在我的笔记本 电脑上模

7、拟 27,000 个扑克游戏,会产生136,000 个玩家和大约 800,000 个进 程。这说明了为什么我极力专注于使用OpenPoker为例讨论 Erlang 如何轻松的实现 可伸缩性,容错性,和负载平衡。此方案不仅仅局限于扑克纸牌游戏。相同的机 制完全可以胜任其他类型大规模可伸缩多人在线后台系统,便宜简单一点儿也不 郁闷!可伸缩性我使用多层体系实现高伸缩性和负载平衡。第一层是网关节点。 第二层是游戏服 务端节点,最后一层是Mnesia 主节点。Mnesia 是 Erlang 的实时分布式数据库系统。Mnesia FAQ解释得很详细,它是一 个高速,重复性,内存驻留的数据库。Erlang

8、本身没有对象支持但是Mnesia 可 以被看作是面向对象的因为它存贮所有Erlang 数据。有两种 Mnesia 节点:访问磁盘的和不访问磁盘的。 无论怎样, 所有 Mnesia 节点 在内存中存储数据。 OpenPoker里的 Mnesia 节点是用来访问磁盘的。网关和游 戏服务层只操作内存,启动后从Mnesia 访问数据库。Erlang 虚拟机有一套很方便的命令行参数来通知Mnesia 主数据库存在哪里。 任 何新的 Mnesia 节点只要和主节点建立了连接, 新的节点马上成为集群的一部分。假设主节点位于 apple 和 orange 两台主机上,那么添加新的网关节点,游戏服 务节点等等到

9、你的OpenPoker集群仅仅需要如下命令行启动:erl -mnesia extra_db_nodes dbapple,dborange -s mnesia start -s mnesia start 也可以用 Erlang 控制台启动 Mnesia: erl -mnesia extra_db_nodes dbapple,dborange Erlang (BEAM) emulator version 5.4.8 source hipe threads:0 Eshell V5.4.8 (abort with G) 1 mnesia:start(). ok OpenPoker在 Mnesia 数据

10、表里保存配置信息, 此信息在 Mnesia 启动的时候自动 被新节点下载。完全零配置!容错性添置几个便宜的 Linux 系统到我的服务器组, OpenPoker可以要多大规模有多大 规模。组合一打 1U服务器系统可以轻松胜任五十万甚至一百万玩家同时在线。 当然不仅仅是纸牌游戏,对于其他多人RPG 网游(MMORPG)也是一样的。我可以指派几个服务器做网关节点, 另外几个做数据库节点访问存储介质上的数 据,然后剩下的一些做游戏服务器。 我还可以限制单台服务器最高接纳五千万家 同时在线,所以任何一台当机,最多5 千个玩家受影响。另外要指出的是任何一台游戏服务器当机都不会有数据损毁因为所有Mnesi

11、a 的 数据访问操作都是由多个游戏,Mnesia 节点实时备份的。考虑到某些潜在错误, 游戏客户端需要做一些辅助工作让玩家顺滑的重新连接到 OpenPoker服务器集群。每当客户端发现网络错误,就会尝试连接网关节点,通 过接力网络包得到一个新的游戏服务节点地址然后重新连接。这里需要点技巧因 为不同的情况要不同对待:OpenPoker划分如下需要重新连接的情况:1. 游戏服务器当机 2. 客户端当机或者网络延迟超时 3. 玩家换另外一个网络连接在线 4. 玩家在游戏中切换另一个网络连接最常见的就是客户端因为网络错误而断开连接。最不常见但是还是有可能的是同 一个客户端在游戏中的时候从另一个电脑尝试

12、连接。每个 OpenPoker游戏缓存发送给玩家的数据包, 每次客户端重新连接都会收到自 游戏开始的所有数据包然后再开始正常接受。OpenPoker使用 TCP连接所以不用 考虑数据包的发送顺序所有数据包保证是按顺序收到的。每个客户端连接由两个OpenPoker进程组成:套接字进程还有玩家进程。 还有一 个受限制的访客进程被使用直至玩家成功登陆,访客不能加入游戏。 套接字进程 虽网络中断而停止,但是玩家进程仍然保持活动。玩家进程发送游戏数据包的时候可以侦测到已经中断的套接字进程,此时会进入 自动运行状态或者暂停状态。 登陆代码会在重新连接的时候同时参考套接字进程 和玩家进程。用来侦测的代码如下

13、:login(atomic, Player, _Nick, Pass|_ = Args) when is_record(Player, player) - Player1 = Player#player socket = fix_pid(Player#player.socket), pid = fix_pid(Player#player.pid) , Condition = check_player(Player1, Pass, fun is_account_disabled/2, fun is_bad_password/2, fun is_player_busy/2, fun is_play

14、er_online/2, fun is_client_down/2, fun is_offline/2 ), ,其中的各个条件是这么写的:is_player_busy(Player, _) - Online, _ = is_player_online(Player, ), Playing = Player#player.game /= none, Online and Playing, player_busy. is_player_online(Player, _) - SocketAlive = Player#player.socket /= none, PlayerAlive = Play

15、er#player.pid /= none, SocketAlive and PlayerAlive, player_online. is_client_down(Player, _) - SocketDown = Player#player.socket = none, PlayerAlive = Player#player.pid /= none, SocketDown and PlayerAlive, client_down. is_offline(Player, _) - SocketDown = Player#player.socket = none, PlayerDown = Pl

16、ayer#player.pid = none, SocketDown and PlayerDown, player_offline. 要注意 login函数首先要做的是修复已失败的进程ID。这样简化了处理过程, 代码如下:fix_pid(Pid) when is_pid(Pid) - case util:is_process_alive(Pid) of true - Pid; _ - none end; fix_pid(Pid) - Pid. 和: -module(util). -export(is_process_alive/1). is_process_alive(Pid) when is_pid(Pid) - rpc:call(node(Pid), erlang, is_process_alive, Pid). Erlang 里的进程 ID 包含运行进程的节点的Id. is_pid(Pid)返回参数是否为一 个进程 Id 但是无法知道进程是否已中断。

展开阅读全文
相关资源
正为您匹配相似的精品文档
相关搜索

最新文档


当前位置:首页 > 行业资料 > 其它行业文档

电脑版 |金锄头文库版权所有
经营许可证:蜀ICP备13022795号 | 川公网安备 51140202000112号