MMO游戏服务端架构
游戏服务端架构介绍
端游、⼿游服务端常⽤的架构是什么样的?
根据知乎问答⽂章整理⽽成。
作者:韦易笑
谢邀,⼿游页游和端游的服务端本质上没区别,区别的是游戏类型。
类型1:卡牌、跑酷等弱交互服务端
卡牌跑酷类因为交互弱,玩家和玩家之间不需要实时⾯对⾯PK,打⼀下对⽅的离线数据,计算下排⾏榜,买卖下道具即可,所以实现往往使⽤简单的 HTTP服务器:
登录时可以使⽤⾮对称加密(RSA, DH),服务器根据客户端uid,当前时间戳还有服务端私钥,计算哈希得到的加密 key 并发送给客户端。之后双⽅都⽤ HTTP通信,并⽤那个key进⾏RC4加密。客户端收到key和时间戳后保存在内存,⽤于之后通信,服务端不需要保存key,因为每次都可以根据客户端传上来
的 uid 和时间戳以及服务端⾃⼰的私钥计算得到。⽤模仿 TLS的⾏为,来保证多次 HTTP请求间的客户端⾝份,并通过时间戳保证同⼀⼈两次登录密钥不同。
每局开始时,访问⼀下,请求⼀下关卡数据,玩完了⼜提交⼀下,验算⼀下是否合法,获得什么奖励,数据库⽤单台 MySQL或者MongoDB即可,后端的 Redis做缓存(可选)。如果要实现通知,那么让客户端定时15秒轮询⼀下服务器,如果有消息就取下来,如果没消息可以逐步放长轮询时间,⽐如30秒;如果有消息,就缩短轮询时间到10秒,5秒,即便两⼈聊天,延迟也能⾃适应。
此类服务器⽤来实现⼀款三国类策略或者卡牌及酷跑的游戏已经绰绰有余,这类游戏因为逻辑简单,玩家之间交互不强,使⽤ HTTP来开发的话,开发速度快,调试只需要⼀个浏览器就可以把逻辑调试清楚了。
类型2:第⼀代游戏服务器 1978
1978年,英国著名的财经学校University of Essex的学⽣ Roy Trubshaw编写了世界上第⼀个MUD程序《MUD1》,在University of Essex于1980年接⼊ ARPANET之后加⼊了不少外部的玩家,甚⾄包括国外的玩家。《MUD1》程序的源代码在 ARPANET共享之后出现了众多的改编版本,⾄此MUD才在全世界⼴泛流⾏起来。不断完善的 MUD1的基础上产⽣了开源的 MudOS(1991),成为众多⽹游的⿐祖:MUDOS采⽤ C语⾔开发,因为玩家和玩家之间有⽐较强的交互(聊天,交易,PK),MUDOS
使⽤单线程⽆阻塞套接字来服务所有玩家,所有玩家的请求都发到同⼀个线程去处理,主线程每隔1秒钟更新⼀次所有对象(⽹络收发,更新对象状态机,处理超时,刷新地图,刷新NPC)。
游戏世界采⽤房间的形式组织起来,每个房间有东南西北四个⽅向可以移动到下⼀个房间,由于欧美最早的⽹游都是地牢迷宫形式的,因此场景的基本单位被成为 “房间”。MUDOS使⽤⼀门称为LPC的脚本语⾔来描述整个世界(包括房间拓扑,配置,NPC,以及各种剧情)。游戏⾥⾯的⾼级玩家(巫师),可以不断的通过修改脚本来为游戏添加房间以及增加剧情。早年 MUD1上线时只有17个房间,Roy Trubshaw 毕业以后交给他的师弟 Richard Battle,在 Richard Battle⼿上,不断的添加各种玩法到⼀百多个房间,终于让 MUD发扬光⼤。
⽤户使⽤ Telnet之类的客户端⽤ Tcp协议连接到 MUDOS上,使⽤纯⽂字进⾏游戏,每条指令⽤回车进⾏分割。⽐如 1995年国内第⼀款MUD游戏《侠客⾏》,你敲⼊:"go east",游戏就会提⽰你:“后花园 - 这⾥是归云庄的后花园,种满了花草,⼏个庄丁正在浇花。此地乃是含羞草⽣长之地。这⾥唯⼀的出⼝是 north。这⾥有:花待阿牧(A mu),还有⼆位庄丁(Zhuang Ding)”,然后你继续⽤⽂字操作,查看阿牧的信息:“look a mu”,系统提⽰:“花待阿牧(A mu)他是陆乘风的弟⼦,受命在此看管含羞草。他看起来三⼗多岁,⽣得眉清⽬秀,端正⼤⽅,⼀表⼈才。他的武艺看上去【不是很⾼】,出⼿似乎【极轻】”。然后你可以选择击败他获得含羞草,但是你吃了含羞草却⼜可能会中毒死亡。在早期⽹上资源贫乏的时候,这样的游戏有很强的代⼊感。
⽤户数据保存在⽂件中,每个⽤户登录时,从⽂本⽂件⾥把⽤户的数据全部加载进来,操作全部在内存⾥⾯进⾏,⽆需马上刷回磁盘。⽤户退出了,或者每隔5分钟检查到数据改动了,都会保存会磁盘。这样的系统在当时每台服务器承载个4000⼈同时游戏,不是特别⼤的问题。从1991年的 MUDOS发布后,全球各地都在为他改进,扩充,退出新版本,随着 Windows图形机能的增强。1997游戏《UO》在 MUDOS 的基础上为⾓⾊增加的x,y坐标,为每个房间增加了地图,并且为每个⾓⾊增加了动画,形成了第⼀代的图形⽹络游戏。
因为游戏内容基本可以通过 LPC脚本进⾏定制,所以MUDOS也成为名副其实的第⼀款服务端引擎,引擎⼀次性开发出来,然后制作不同游戏内容。后续国内的《万王之王》等游戏,很多都是跟《UO》⼀样,直接在 MUDOS上进⾏⼆次开发,加⼊房间的地图还有⾓⾊的坐标等要素,该架构⼀直为国内的第⼀代 MMORPG提供了稳固的⽀持,直到 2003年,还有游戏基于 MUDOS开发。
虽然后⾯图形化增加了很多东西,但是这些MMORPG后端的本质还是 MUDOS。随着游戏内容的越来越复杂,架构变得越来越吃不消了,各种负载问题慢慢浮上⽔⾯,于是有了我们的第⼆代游戏服务器。
类型3:第⼆代游戏服务器 2003
2000年后,⽹游已经脱离最初的⽂字MUD,进⼊全⾯图形化年代。最先承受不住的其实是很多⼩⽂件,
⽤户上下线,频繁的读取写⼊⽤户数据,导致负载越来越⼤。随着在线⼈数的增加和游戏数据的增加,服务器变得不抗重负。同时早期 EXT磁盘分区⽐较脆弱,稍微停电,容易发⽣⼤⾯积数据丢失。因此第⼀步就是拆分⽂件存储到数据库去。
此时游戏服务端已经脱离陈旧的 MUDOS体系,各个公司在参考 MUDOS结构的情况下,开始⾃⼰⽤ C在重新开发⾃⼰的游戏服务端。并且脚本也抛弃了 LPC,采⽤扩展性更好的 Python或者 Lua来代替。由于主逻辑使⽤单线程模型,随着游戏内容的增加,传统单服务器的结构进⼀步成为瓶颈。于是有⼈开始拆分游戏世界,变为下⾯的模型:
游戏服务器压⼒拆分后得意缓解,但是两台游戏服务器同时访问数据库,⼤量重复访问,⼤量数据交换,使得数据库成为下⼀个瓶颈。于是形成了数据库前端代理(DB Proxy),游戏服务器不直接访问数据库⽽是访问代理,再有代理访问数据库,同时提供内存级别的cache。早年 MySQL4之前没有提供存储过程,这个前端代理⼀般和 MySQL跑在同⼀台上,它转化游戏服务器发过来的⾼级数据操作指令,拆分成具体的数据库操作,⼀定程度上代替了存储过程:
但是这样的结构并没有持续太长时间,因为玩家切换场景经常要切换连接,中间的状态容易错乱。⽽且游戏服务器多了以后,相互之间数据交互⼜会变得⽐较⿇烦,于是⼈们拆分了⽹络功能,独⽴出⼀个⽹关服务 Gate(有的地⽅叫 Session,有的地⽅叫 LinkSvr之类的,名字不
同⽽已):
把⽹络功能单独提取出来,让⽤户统⼀去连接⼀个⽹关服务器,再有⽹关服务器转发数据到后端游戏服务器。⽽游戏服务器之间数据交换也统⼀连接到⽹管进⾏交换。这样类型的服务器基本能稳定的为玩家提供游戏服务,⼀台⽹关服务1-2万⼈,后⾯的游戏服务器每台服务5k-
1w,依游戏类型和复杂度不同⽽已,图中隐藏了很多不重要的服务器,如登录和管理。这是⽬前应⽤最⼴的⼀个模型,到今天任然很多新项⽬会才⽤这样的结构来搭建。
⼈都是有惯性的,按照先前的经验,似乎把 MUDOS拆分的越开性能越好。于是⼤家继续想,⽹关可以拆分呀,基础服务如聊天交易,可以拆分呀,还可以提供web接⼝,数据库可以拆分呀,于是有了下⾯的模型:
这样的模型好⽤么?确实有成功游戏使⽤类似这样的架构,并且发挥了它的性能优势,⽐如⼀些⼤型 MMORPG。但是有两个挑战:每增加⼀级服务器,状态机复杂度可能会翻倍,导致研发和bug的成本上升;并且对开发组挑战⽐较⼤,⼀旦项⽬时间吃紧,开发⼈员经验不⾜,很容易弄挂。
⽐如我见过某上海⼀线游戏公司的⼀个 RPG上来就要上这样的架构,我看了下他们团队成员的经验,问了下他们的上线⽇期,劝他们⽤前⾯稍微简单⼀点的模型。⼈家⾃信得很,认为有成功项⽬是这么
做的,他们也要这么做,⾃⼰很想实现⼀套。于是他们义⽆反顾的开始编码,项⽬做了⼀年多,然后,就没有然后了。
现今在游戏成功率不⾼的情况下,⼀开始上⼀套⽐较复杂的架构需要考虑投资回报率,⽐如你的游戏上线半年内 PCU会去到多少?如果⼀个 APRG游戏,每组服务器5千⼈都到不了的话,那么选择⼀套更为贴近实际情况的结构更为经济。即使后⾯你的项⽬真的超过5千⼈朝着1万⼈⽬标奔的话,相信那个时候你的项⽬已经挣⼤钱了,你数着钱加着班去逐步迭代,⼀次次拆分它,相信⼼⾥也是乐开花的。
上⾯这些类型基本都是从拆分 MUDOS开始,将 MUDOS中的各个部件从单机⼀步步拆成分布式。虽然今天任然很多新项⽬在⽤上⾯某⼀种类似的结构,或者⾃⼰⼜做了其他热点模块的拆分。因为他们本质上都是对 MUDOS的分解,故将他们归纳为第⼆代游戏服务器。
源代码剧情概括类型4:第三代游戏服务器 2007
从魔兽世界开始⽆缝世界地图已经深⼊⼈⼼,⽐较以往游戏玩家⾛个⼏步还需要切换场景,每次切换就要等待 LOADING个⼏⼗秒是⼀件⼗分破坏游戏体验的事情。于是对于 2005年以后的⼤型 MMORPG来说,⽆缝地图已成为⼀个标准配置。⽐较以往按照地图来切割游戏⽽⾔,⽆缝世界并不存在⼀块地图上⾯的⼈有且只由⼀台服务器处理了:
每台 Node服务器⽤来管理⼀块地图区域,由 NodeMaster(NM)来为他们提供总体管理。更⾼层次的 World则提供⼤陆级别的管理服务。这⾥省略若⼲细节服务器,⽐如传统数据库前端,登录服务器,⽇志和监控等,统统⽤ ADMIN概括。在这样的结构下,玩家从⼀块区域⾛向另外⼀块区域需要简单处理⼀下:
玩家1完全由节点A控制,玩家3完全由节点B控制。⽽处在两个节点边缘的2号玩家,则同时由A和B提供服务。玩家2从A移动到B的过程中,会同时向A请求左边的情况,并向B请求右边的情况。但是此时玩家2还是属于A管理。直到玩家2彻底离开AB边界很远,才彻底交由B管理。按照这样的逻辑将世界地图分割为⼀块⼀块的区域,交由不同的 Node去管理。
对于⼀个 Node所负责的区域,地理上没必要连接在⼀起,⽐如⼤陆的四周边缘部分和⾼⼭部分的区块⼈⽐较少,可以统⼀交给⼀个Node去管理,⽽这些区块在地理上并没有联系在⼀起的必要性。⼀个 Node到底管理哪些区块,可以根据游戏实时运⾏的负载情况,定时维护的时候进⾏更改 NodeMaster 上⾯的配置。
于是碰到第⼀个问题是很多 Node服务器需要和玩家进⾏通信,需要问管理服务器特定UID为多少的玩家到底在哪台 Gate上,以前按场景切割的服务器这个问题不⼤,问了⼀次以后就可以缓存起来了,但是现在服务器种类增加不少,玩家⼜会飘来飘去,按UID查玩家⽐较⿇烦;另外⼀⽅⾯ GATE需要动
态根据坐标计算和哪些 Node通信,导致逻辑越来越厚,于是把:“⽤户对象”从负责连接管理的 GATE中切割出来势在必⾏于是有了下⾯的模型:
⽹关服务器再次退回到精简的⽹络转发功能,⽽⽤户逻辑则由按照 UID划分的 OBJ服务器来承担,GATE是按照⽹络接⼊时的负载来分布,⽽ OBJ则是按照资源的编号(UID)来分布,这样和⼀个⽤户通信直接根据 UID计算出 OBJ服务器编号发送数据即可。⽽新独⽴出来的OBJ则提供了更多⾼层次的服务:
· 对象移动:管理具体玩家在不同的 Node所管辖的区域之间的移动,并同需要的 Node进⾏沟通。
· 数据⼴播:Node可以给每个⽤户设置若⼲ TAG,然后通知 Object Master 按照TAG⼴播。
· 对象消息:通⽤消息推送,给某个⽤户发送数据,直接告诉 OBJ,不需要直接和 GATE打交道。
· 好友聊天:⾓⾊之间聊天直接⾛ OBJ/OBJ MASTER。
整个服务器主体分为三层以后,NODE专注场景,OBJ专注玩家对象,GATE专注⽹络。这样的模型在⽆缝场景服务器中得到⼴泛的应⽤。但是随着时间的推移,负载问题也越来越明显,做个活动,远来不活跃的区域变得⼗分活跃,靠每周维护来调整还是⽐较笨重的,于是有了动态负载均衡。
动态负载均衡有两种⽅法,第⼀种是按照负载,由 Node Master 定时动态移动修改⼀下各个 Node的边界,⽽不同的玩家对象按照先前的⽅法从⼀台 Node上迁移到另外⼀台 Node上:
图11 动态负载均衡
这样 Node Master定时查地图上的热点区域,计算新的场景切割⽅式,然后告诉其他服务器开始调整,具体处理⽅式还是和上⾯对象跨越边界移动的⽅法⼀样。
但是上⾯这种⽅式实现相对复杂⼀些,于是⼈们设计出了更为简单直接的⼀种新⽅法:
图12 基于⽹格的动态负载均衡
还是将地图按照标准尺⼨均匀切割成静态的⽹格,每个格⼦由⼀个具体的Node负责,但是根据负载情况,能够实时的迁移到其他 Node上。在迁移分为三个阶段:准备,切换,完成。三个状态由Node Master负责维护。准备阶段新的 Node开始同步⽼ Node上⾯该⽹格的数据,完成后告诉NM;NM确认OK后同时通知新旧 Node完成切换。完成切换后,如果 Obj服务器还在和⽼的 Node进⾏通信,⽼的 Node将会对它进⾏纠正,得到纠正的 OBJ将修正⾃⼰的状态,和新的 Node进⾏通信。
很多⽆缝动态负载均衡的服务端宣称⾃⼰⽀持⽆限的⼈数,但不意味着 MMORPG游戏的⼈数上限真的可以⽆限扩充,因为这样的体系会受制于⽹络带宽和客户端性能。带宽决定了同⼀个区域最⼤⼴播
上限,⽽客户端性能决定了同⼀个屏幕到底可以绘制多少个⾓⾊。
从⽆缝地图引⼊了分布式对象模型开始,已经完全脱离 MUDOS体系,成为⼀种新的服务端模型。⼜由于动态负载均衡的引⼊,让⽆缝服务
器如虎添翼,容纳着超过上⼀代游戏服务器数倍的⼈数上限,并提供了更好的游戏体验,我们称其为第三代游戏服务端架构。⽹游以⼤型多⼈⾓⾊扮演为开端,RPG⽹游在相当长的时间⾥⼀度占据90%以上,使得基于 MMORPG的服务端架构得到了蓬勃的发展,然⽽随着玩家对RPG的疲惫,各种⾮MMORPG游戏如⾬后春笋般的出现在⼈们眼前,受到市场的欢迎。
类型5:战⽹游戏服务器
经典战⽹服务端和 RPG游戏有两个区别:RPG是分区分服的,北京区的⽤户和⼴州区的⽤户⽼死不相往来。⽽战⽹,虽然每局游戏⼀般都是 8⼈以内,但全国只有⼀套服务器,所有的玩家都可以在⼀起游戏,⽽玩家和玩家之使⽤ P2P的⽅式连接在⼀起,组成⼀局游戏:
玩家通过 Match Making 服务器使⽤:创建、加⼊、⾃动匹配、邀请等⽅式组成⼀局游戏。服务器会选择⼀个⼈做 Host,其他⼈ P2P连接到做主的玩家上来。STUN是帮助玩家之间建⽴ P2P的牵引服务器,⽽由于 P2P联通情况⼤概只有 75%,实在联不通的玩家会通过Forward进⾏转发。
⼤量的连接对战,体育竞技游戏采⽤类似的结构。P2P有⽹状模型(所有玩家互相连接),和星状模型(所有玩家连接⼀个主玩家)。复杂的游戏状态在⽹状模型下难以形成⼀致,因此星状P2P模型经受住了历史的考验。除去游戏数据,⽀持语⾳的战⽹系统也会将所有⼈的语⾳数据发送到做主的那个玩家机器上,通过混⾳去重再编码的⽅式返回给所有⽤户。
战⽹类游戏,以竞技、体育、动作等类型的游戏为主,较慢节奏的 RPG(包括ARPG)有本质上的区别,⽽激烈的游戏过程必然带来到较RPG复杂的多的同步策略,这样的同步机制往往带来的是很多游戏结果由客户端直接计算得出,那在到处都是破解的今天,如何保证游戏结果的公正呢?
主要⽅法就是投票法,所有客户端都会独⽴计算,然后传递给服务器。如果结果相同就更新记录,如果结果不⼀致,会采取类似投票的⽅式确定最终结果。同时记录本剧游戏的所有输⼊,在可能的情况下,另外闲散的游戏客户端验算整局游戏是否为该结果。并且记录经常有作弊嫌疑的⽤户,供运营⼈员封号时参考。
类型7:休闲游戏服务器
休闲游戏同战⽹服务器类似,都是全区架构,不同的是有房间服务器,还有具体的游戏服务器,游戏主体不再以玩家 P2P进⾏,⽽是连接到专门的游戏服务器处理:
和战⽹⼀样的全区架构,⽤户数据不能象分区的 RPG那样⼀次性load到内存,然后在内存⾥⾯直接修改。全区架构下,为了应对⼀个⽤户同时玩⼏个游戏,⽤户数据需要区分基本数据和不同的游戏数据,⽽游戏数据⼜需要区分积分数据、和⽂档数据。胜平负之类的积分可以直接提交增量修改,⽽更为普遍的⽂档类数据则需要提供读写令牌,写令牌只有⼀块,读令牌有很多块。同帐号同⼀个游戏同时在两台电脑上玩时,最先开始的那个游戏获得写令牌,可以操作任意的⽤户数据。⽽后开始的那个游戏除了可以提交胜平负积分的增量改变外,对⽤户数据采⽤只读的⽅式,保证游戏能运⾏下去,但是会提⽰⽤户,游戏数据锁定。
类型8:现代动作类⽹游
从早期的韩国动作游戏开始,传统的战⽹动作类游戏和 RPG游戏开始尝试融合。单纯的动作游戏玩家容易疲倦,留存也没有 RPG那么⾼;⽽单纯 RPG战⽃却⼜慢节奏的乏味,⽆法满⾜很多玩家激烈对抗的期望,于是⼆者开始融合成为新⼀代的:动作 + 城镇模式。玩家在城镇中聚集,然后以开副本的⽅式⼏个⼈出去以动作游戏的玩法来完成各种 RPG任务。本质就是⼀套 RPG服务端+副本服务端。由于每次副本时⼈物可以控制在8⼈以内,因此可以获得更为实时的游戏体验,让玩家玩的更加爽快。
说了那么多的游戏服务器类型,其实也差不多了,剩下的类型⼤家拼凑⼀下其实也就是这个样⼦⽽已。游戏服务端经历了那么多结构上的变迁,内部开发模式是否依然不变?究竟是继续延续传统的开
发⽅式?还是有了更多突破性的⽅法?经历那么多次架构变迁,后⾯是否有共通的逻辑?未来的发展还会存在哪些困难?游戏服务端开发如何达到最终的彼岸?请看下节:技术的演进。
技术的演进
(待续)
【感受
从⽬前流⾏的开源游戏服务端框架来分析:
⽹易pomelo 属于第⼆代游戏服务端五型的架构,即图7的架构。
skynet因为是⼀个服务端框架,官⽅只是提供了login server 和 gate server的参考实现,其他的需要⾃⼰来实现,编程的⾃由度变⼤了,架构完全取决于程序员⾃⼰的选择,程序员可以⾃⼰尝试去实现第⼆代的架构,也可以实现第三代的架构。注意: skynet仅仅是框架,其他属于完整解决⽅案。
Kbengine属于第三代服务端框架,可能类似于图10。(这个理解不确定)
Kbengine引擎应该是对图10中的Gate服务器和NODE和OBJ进⾏了细分。在功能上⼤体划分为与位置
有关(在Kbengine中称为Cellapp)和与位置⽆关(在Kbengine中称为Baseapp)。类似于下⾯的⽰图架构。

版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系QQ:729038198,我们将在24小时内删除。