我不得不承认Q我的能力不以写出一?00%不会宕机的游戏服务器E序Q这也不能全怪我的能力太弱,谁让咱国内网游玩家数量庞大,哪个游戏刚上U时没有挤的爆满q?q有些或是猎奇,或是谋私的个人和l织Q在刉着千奇百怪,匪夷所思的数据包及操作程来试探你的服务器。这些都曾是我在服务器宕机后向老板开q理由?/p>
当WOWl于来到中国Ӟ我一Ҏ喜着l于可以在艾泽拉斯的大陆上自q,一边却咒骂着9C的破服务器,动不动就宕机。当Ӟwؓ游戏E序设计师的我明知道Q这大部分的错误都不应归|于代理?CQ但是,谁让blizzard是我心目中的,谁又让WOW成ؓ我游戏制作的教科书呢。好吧,我知道上面这D|力追捧blizzard跟WOW的话可能早已让你恶心q连Q不堪入目了Q对不vQ忘了这一节,让我们l?/p>
服务器宕机后都发生了些什么?
昄的,宕机后玩家会骂,像我在玩WOW旉P骂游戏公司,骂老板Q骂GM。非常抱歉,我们可爱的玩家们gq不清楚Q这个时候最该骂的其实是我们q些E序员们。长久的遗忘被我们当成了包容Q以至于游戏E序员在公司里都L了趾高气扬,不可一世的坏毛病:看吧Q策划们Q你们做的太烂了Q数gqQ玩法没新意Q只会照抄WOW跟大菠萝Q能怪玩安你们吗?q营不得力,买服务器的钱不知道去了哪里,游戏里卡的要死,偶尔办个zdq没半点吸引力,能不被玩安你是无良q营商吗QGM们能不天天被骂家指着骂吗Q?#8230;…呃,又扯q了?/p>
赶紧先把服务器重启吧。老板正站在你的n后,一脸愁容,虽然暂时q没有发作,但看得出来:老板很生气,后果很严重!
玩家们很快又回来了,不得不ؓ玩家们的毅力和执着_而感动,更ؓ自己的错误而愧疚,凌晨时分Q服务器启了又宕Q宕了又启,如此反复Q可热情的玩家们依然陪着我在折腾。哦Q当q安其拉开门的时候,我也曾这h腾过?/p>
q个时候不是你一个h在战斗。GM们在忙碌地处理着玩家不断打来的投诉电话:刚买的装备在宕机后消׃Q花光了w上所有材料合成的武器回档了,但材料却没有q给?#8230;…数据库维护组的同事们也在紧张的恢复着数据Q尽可能的将玩家的损失减到最?/p>
真是一件o人沮丧的事?/p>
真的该试着做点什么了吧!
既然我们非常不愿意看到宕机的情况发生Q但又无?00%保证写出来的服务器程序一定不会出错,那我们就在当机发生后的抢救措施上q功夫Q让玩家的损׃至于太大Q也让我们的l护人员些压力吧?/p>
一个最单也最有效的做法是为每一台服务器都配备物理冗余,同步更新冗余服务器上的状态,当宕机发生时Q立卛_处理切换到后备服务器上。只是,物理冗余的代价太大,从成本方面考虑Q老板可能不大愿意点头?/p>
既然不能做硬冗余Q那再来考虑软的吧?/p>
如果只是单的启动冗余q程Q其实是换汤不换药的做法。原来能?000人的服务器,׃同时q行了两个相同的q程Q得CPU和内存开销都翻了倍,l果是只能跑500Z。还是要加服务器?/p>
看来只能更深一层,从架构设计上来动手了?/p>
假设我们的游戏世界是由多个独立场景构成的Q那么在实现上我们可以让q些场景在进E上也独立,q样做的好处是可以得一个场景的宕机不会影响到其他场景的正常q行。如果我们的游戏世界物理上没有分隔,是一个无~的大世界,我们也可以h为的其分成多个独立区域Q所需要做的额外工作是处理好那些站在区域边界附q的对象。事实上Q现在的无缝大世界也都是q样实现的?/p>
有了q样一个前提,我们再来看这个已宕掉的场景该如何处理?/p>
q是老办法,赶紧先把它拉h吧。一个具体可行的Ҏ是,由场景管理器Q或者你也有可能叫它世界服务器,来监视各个场景进E的q行状态,当某个场景异常失去联pLQ由理器来其重新启动。这里需要再q心思的是,如何让玩家数据正常地发送到新启动的场景q程中,而且q个q程对于客户端来说是透明的?/p>
q个Ҏ听v来似乎不错,只是Q如果宕掉的是场景管理器q程Q那该怎么办呢Q?/p>
按照前面的描qͼ场景理器可以看作是整个游戏世界的中心,它以一个指挥者的w䆾l护着游戏世界的有序运行,所以它的宕机对整个游戏世界的媄响也会是巨大的?/p>
有没有什么办法能够得场景管理器q程再次启动后能够恢复先前的状态呢Q?/p>
我们可以为管理器和场景进E定义一套协议,使得理器不仅能够创建ƈ恢复一个已有场景,而且场景理器还能通过现有的场景进E数据恢复出自己?/p>
一个理Z可行的方案是Q场景管理器与场景进E间保持TCP长连接,q以一定频率进行心跌p,L一方发现联pM断或长时间未收到心蟩包后都会立即做出处理?/p>
如果是管理器发现场景q程失去联系Q那启动新的场景,如前面所描述的那栗如果是场景q程发现理器失去联p,那就立即启动重连q程Q直接再ơ连接上理器,然后立即自己当前的状态和负责的场景ID报告l管理器。管理器通过q些上报的数据就能恢复出游戏世界内的场景对应关系表,也就是恢复出了自己原来的状态?/p>
q程是恢复出来了Q可我们忘了最重要的内容:数据。当场景q程宕机后,上面保存的玩家属性数据也随之丢失了,虽然我们能够再次这个场景创建出来,q把原来在这个场景内的客L数据重新定向q来Q但q些客户端对应的玩家对象的数据却没有了,游戏仍然无法l箋?/p>
也许我们可以再做一点修改,把场景内的玩家数据分d来,保存C个独立的q程上,比如Q我们可以把q个q程叫做数据服务器,或者数据中心之cȝ。一个隐含的要求是,数据服务器的逻辑实现非常单,单到你可以认为它是绝对安全的Q不会宕机。所以,保存在这里的玩家数据也就是绝对安全的?/p>
让我们在q个问题上稍微再深入一炏V?/p>
场景q程上每ơ执行玩家的游戏逻辑旉要异步地到数据服务器上来存取数据Q这个开销可能太大Q而且会得一些游戏逻辑的实现变的很复杂Q那么,把一些会频繁使用到的数据直接保存在场景进E中Q当数据发生改变时同步更新到数据服务器上Q这样可能会比较Ҏ接受?/p>
老板全都满意了吗Q?/strong>
从理Z来说Q我们已l解决了场景q程宕机和管理器宕机后的状态恢复问题,q且在场景恢复后也不会因Z׃玩家数据而无法l进行游戏,而且Q只要处理得当,q个q程对客L来说可以是完全透明的,也就是玩家根本不知道服务器上有个q程意外l束Q我们做了这么多的工作来它恢复了?/p>
事实上,q个q程的透明也是必须的,我们q不需要嚷L告诉我们的用P也就是玩Ӟ我们做了多少多少事情来让你玩的更畅Q又׃多少多少_֊来解军_为服务器宕机而引LȝQ对于最l的用户来说Q他只需要n受最好的服务。闲话少_让我们l?/p>
真的已经完全解决了所有问题吗Q?/strong>
惌q样一个场景:我带着几个刚刚降到艾泽拉斯大陆的伙伴冲向了艾文林Q去开荒霍|正在霍格只剩下一丝血的时候,服务器稍E卡了一下,{我~过来Q面前的霍格骤然消失Q地上也不见怽。找了一圈,它正在出生点摇头晃脑Q也在四处张望,但头上的血条分明是Q满血Q?/p>
怎么回事Q?/p>
处理q张地图的场景进E意外结束了Q服务器的宕机处理机制很快地恢复了这个场景进E,q且把我的客L数据重新定向C新场景。只是,事情q不是一切都完美。因个场景是完全重新创徏的,q意味着所有的怪物也是重新创徏q被摆放C初始位置Q所以,只剩下一丝血的霍格碰上了好运?#8230;…
cM的还有,正在护送NPCq回营地Q在E微停顿了一会儿之后QNPC又重新回C原来的地方,{等?/p>
虽然q比h初的“客户端被q断开q接Q服务器端数据丢?#8221;要进步了许多Q但会给我工资的老板仍然可能不太满意Q他希望Q霍格应该还在我的面前,而且只有一丝血Q那个跟着我的NPC也应该还在我旁边……
我要是不能说服老板Q这?#8220;Ҏ不可能完成的dQ?#8221;Q那也就只能坐下来再试一试?/p>
也许Q可以考虑所有对象的数据都保存到数据服务器上Q当Ӟq要求每个怪物都跟玩家一P有一个唯一IDQ这一点实现v来可能会有些ȝ?/p>
再不Ӟ为对象提供一个从已有的内存数据构造的ҎQ这样便可以使用׃n内存来保存现场数据,再从׃n内存中恢复出原来的对象。理Z来说Q这个方法是可行的,只是Q这三十多个字的文字描述要用C++来实C可能会是一Ҏ大的挑战Q所以,q也仅只是可供参考的一个尝试方案?/p>
我想Q我们走的够q了
让我们先暂停一会儿Q回q头来看一看最初的目的。其实我们想要的只是可能的让服务器q程不要宕机Q如果实在是没有办法Q就可能的让宕机后的玩家损失比较小Q不需要我们做大量的工作去做善后处理?/p>
很简单的需求,g我们U缠的有些过头了?/p>
写出能够E_q行的程序是对程序员的最基本要求Q如果一个程序连E_性都不具备,那根本都不用再去考虑功能啊、扩展啊{其他标准了。但是,正如我最开始所说的Q没有一个h能够100%保证他写出来的服务器E序是绝对不会崩溃的。我们所能要求的只是可能的仔细Q尽可能的多一些必要的试Q离安全可能的更近一步?/p>
剩下的就是在宕机后如何降低损q问题了?/p>
对于一般的MMOG来说Q玩家在q入游戏时会从数据库中将该玩家的所有相x据读到内存,以便快速的q行游戏逻辑的处理,而在玩家下线时再数据的改动存回数据库?/p>
昄的,当服务器q程出现意外宕机Ӟ内存中所有的数据都丢׃Q这也就造成了玩家数据的回档Q而且玩家在游戏中呆的旉长Q回档的损失p大。所以,一个被q泛采用的做法是为玩家数据实CU定时存盘的机制Q就像现在大多数的单机游戏一PAutoSave。比如,?分钟自动为玩家存一ơ盘Q这样就可以使得回档的最大损失控制在5分钟以内?/p>
另外Q对于一些重要数据的变动Q比如玩家花大量游戏货币购买了一件贵重的武器装备Q这时可玩家数据立卛_一ơ存盘操作,q也有效的减少玩家的重大损失?/p>
听v来这是一不错的技术,在意外宕机的时候最多只回档5分钟Q而且q没有贵重物品的损失Q玩家应该是可以接受的吧?/p>
我已l听C数据库维护员的咆?/strong>
“数据库已l快要崩溃了Q你׃能让每秒需要执行的SQL语句一点吗Q?#8221;
“?#8230;……”
我一直以为我们的数据库非常强大,可以处理M的数据,唯一的缺点就是查询速度比直接内存读取要慢很多。所以我使用了异步数据存取的ҎQƈ且开启了多个数据库操作线E来q行的执行我的请求,q行的效果看hq不错?/p>
也许Q我应该来算一,每秒U究竟丢了多条操作hl数据库?/p>
请允许我再自U一回,我已l很久没有提到WOW?#8230;…
大概可信的数字是QWOW一l服务器的玩家数量在3000?000之间Q去掉最大的敎ͼ再去掉最的敎ͼ最后的q_值是Q?000吧,q4000?/p>
4000人在U,假设也是?分钟定时存盘一ơ,再假设所有玩家的存盘旉是^均分布的。这L下来Q每U种׃?7个玩家向数据库发出存盘请求操作?/p>
?7个,数据库维护组的同事就跟我说不堪重负了Q笑话,q数据库服务器是谁买的?
先别急,67是玩家数Q但是每个玩家的存盘h不会只有一条SQL语句?/p>
虽然每个游戏的内定w各有差别Q但是一ƾMMOG需要存入数据库的数据少不了会有技能、物品、Q务、宠物、好友、公会这些东ѝ取决于游戏的类型差异,每个游戏都会有自q存盘方式Q比如我可能会把所有的技能ID作ؓ一条数据来存储Q但是我也有可能把每个技能作Z条单独的记录来存储,q样可以方便Ҏ能附加数据的扩展Q等{?/p>
但是Q游戏中的物品存储大概都是相同的Q只能是一?l?物品作ؓ一条记录来存储?/p>
而且Q可以说游戏中存储量最大的是物品数据。算一你的角色背包有多大Q?0| 100|q是200|不要忘了银行、摆摊位、装备拦、宠物背包和邮箱q些地方也能攄品。ƈ且,在游戏进行过E中Q玩家背包中物品数据的变动也是相当的频繁Q不断的有药品被用掉Q不断地又有些小玩意儿被捡v来,不久后,它们又被卖给了NPC?/p>
虽然你可以用一些y妙的比较法来过滤掉那些实际上没有发生变动的物品更新Q另外也不是所有的玩家物品数据变动都很频繁Q但在实际运营中Q尤其是当玩家的背包格数都很多的时候,物品数据的存盘的会成ؓ一个很大的问题?/p>
除了物品Q还有玩家的基本属性存盘,C会关系存盘{等Q再加上全局公共数据的存盘,如公会数据,拍卖行物品数据,如果老板也要我在游戏中开上一家拍卖行的话?/p>
q么一下来,g是有些多了?/p>
再一ơ的挑战
具体的数字将取决于游戏的cd和设计的数据表结构?/p>
而数据库服务器能承担的每U查询数则取决于数据库服务器的Yg配置情况?/p>
但是一般来_数据库维护h员可能会告诉我,当每U执行的SQL语句数达?000条时Q数据库服务器将会感受到明显的压力,我可能也会看到数据库执行队列中的hC直在增长Q也可能会看到数据库服务器间歇性地拒绝响应Q等{?/p>
看v来我们又一ơ的面对C巨大的打h?/p>
q个问题的v因是什么?我们不希望服务器q程宕机时回档太久,所以我们增加了一个玩家数据定时存盘的机制Q结果却D了数据库h的骤然增多?/p>
那再退回到q个L处,定时存盘的旉间隔廉点,比如10分钟才存一ơ?数据库的压力会有~解Q但最初的问题却又会有所暴露。真是个两难的问题?/p>
既想要玩家数据存盘间隔时间短一点,又不想给数据库造成的压力太大?/p>
同样的需求似乎出现过很多回了Q在中间加一层代理做~冲。我们姑且称q一层代理ؓ数据库代理服务器Q它所要完成的工作是从场景q程攉玩家的定时存盘请求数据,再以一个低一点的频率写入到数据库?/p>
听v来这又像是一个换汤不换药的做法,写入数据库的旉间隔q是变长了。但实际上在前面我们曾l描q过Q如果服务器q程不会出现意外的宕机,玩家数据只需要在他上U时dQ在他下U时写入卛_Q中间添加的q些定时存盘q程完全只是Z防范宕机回档所造成的巨大损失?/p>
因ؓq个中间代理层的加入Q我们把场景q程宕机的隐患与数据丢失的后果隔d来了Q现在即使场景进E宕机,数据q在数据库代理服务器上,当然q里又隐含了一个条Ӟ数据库代理服务器需要够稳定,不会在场景进E之前先宕掉。事实上Q因个代理进E的工作是,我们完全有理q信,q个q程是非常稳定的Q那L话,多久旉才把~存的数据真正写入数据库Q就看你自己的喜好了?/p>
该结束了?/strong>
是否有些似曾相识的感觉?
没错Q前面我们曾l描q过一个数据服务器Q也是这栯的?/p>
所以,数据服务器,数据库代理服务器可以合ƈCP来共同保证数据的安全?/p>
再加上场景进E与理器进E的恢复协议Q让服务器的重启对玩家保持透明?/p>
看v来这个晚上可以睡个安E?/p>

]]>