GameRes游戏开发资源网 http://www.gameres.com
谈谈MMOG的项目测试期相关技术
经过一番奋斗之后,你的新项目已经有了宏大的世界观、无缝链接的超大场景、精美的人物和场景、爽快的战斗和技能,还有——为媒体宣传所打动的成千上万的玩家们在激动的等待着她的面世……所有事情看起来都很美妙,但是接下来你要面对的才是真正的挑战——你要证明你的网游真正可以玩,不会总是黑屏死机加回档,你要以实际行动告诉玩家:“哦,那些Bug我们很快可以搞定”——对于热心支持国产游戏的玩家们来说,这听起来相当不错,但是你需要有一些技术和准备来做到这个,我们接下来就谈谈怎么干。
Author:王志勇
Email & MSN: [email protected]
Date: 2006年5月
Copyright: 王志勇2006. All rights reserved
如果全面的讨论MMOG项目测试期的相关的技术,恐怕可以写一本不算薄的书籍(并且会超出我现有的知识范围),这本书将包括程序调试技巧、代码编写技巧、如何重构代码等等、甚至是沟通技巧——在项目测试开始后的这段时间,对于一直埋头研发工作的你来说,需要更多的与别人沟通,包括运营部门、市场部门、网站部门等等,当然——还有你的老板。他这时候会经常走过来和你说“听说昨天又当机了”,恐怕需要一定的沟通技巧去让他相信你和你的那些人能搞定这个事情,不必对未来过分担心。
所有我不打算在这里罗嗦很多东西,但是我会提供给你一些链接,以便你能够得到让自己满意的答案。现在我们主要来关注Profiling和Postmortem方法。目前国内MMOG开发大多基于Windows平台,包括客户端和服务器端,从开发工具和辅助工具的完善性、总体拥有成本和系统维护性上来说,Windows都相当不错。所以以下提到的相关工具和技术也都基于Windows平台。
在项目测试期开始之后,我们要做的第一件事情就是测试系统的承载能力是否能够达到预期的目标,也就是做所谓的服务器压力测试。主要有以下几个目的:
l 验证系统架构设计的合理性;
在影响系统性能的各种因素上,架构合理性应该是最重要的一个因素;糟糕的架构设计几乎无法通过代码优化或者其他方式得以根本性的改进。所以我们要先证明现在的系统架构是否合理。
l 找到系统的性能瓶颈以便改进;
几乎任何系统都有性能上的改进余地,尤其是一个规模庞大的新系统。我们不可能有时间去优化所有已经写好的模块和代码,根据80/20理论,找到系统的‘瓶颈’然后集中进行优化才是有效提高系统性能的办法。
l 验证采用技术的合理性;
在一个设计合理的系统内,可以通过改进某个模块的实现方式来明显地改进系统性能。在测试期开始的时候发现某个模块采用的技术不合理,还不算晚。
l 为日后开始测试和真正运营提供一份详细的硬件规范;
相关部门需要根据这份文档去采购硬件,并最终确定每区/组服务器的采购成本。还可以得出这个游戏的单位数量用户的硬件成本;或者是服务器组的最佳承载人数——它可以用来设置‘登陆排队系统’的阀值。
我们主要在压力测试期,进行系统的Profiling工作。压力测试一般划分两个阶段:封闭的压力测试和向玩家公开的压力测试。封闭的压力测试一般发生在公司内部,好处是你可以随时启动和中止测试,并且在这个过程中可以应用各种软件以辅助测试、收集数据。这些性能分析软件通常会占用大量的系统资源,让服务器响应变得很慢,在客户端的反映就是操作变得很‘卡’。另外我们要通过测试找到系统承载能力的极限,通过一些技术手段,可以较为容易的让系统承载能力达到极限,下面将会提到这些办法。在系统达到极限承载能力的情况下,服务器也一定会很‘卡’。但是就算在压力测试期,玩家们也不会喜欢一个很‘卡’的游戏,所以这些测试最好是在公司内部进行。向玩家公开的压力测试,则可以得到实际的服务器承载能力水平、直观的服务质量表现以及IDC的网络状况。
我们主要谈谈内部的压力测试工作。在开始前准备工作包括:
n 一组服务器:一般的网络游戏,对于每台服务器来说,双Intel Xeon 3.0 GHz CPU加上1G内存的服务器已经足够用了;除了数据库服务器和备份服务器,一般来说对硬盘没什么要求。
n 测试人员:你需要一些配合测试的人员,分别控制系统的不同部分;例如客户端、游戏服务器、数据库服务器等等;
下面我们就如何实施Profiling,分部分进行详细的描述。
我们需要一些(例如20台)PC上运行大量‘机器人’程序来模拟大量玩家的基本行为,例如行走、聊天、攻击动作等等,基本上可以认为它就是一个简单的‘脱机外挂’程序。机器人应该具有以下基本功能:
1, 连接、登陆服务器;
2, 能够读取游戏的基本场景信息;
3, 做基本的游戏消息处理;
为了提高开发效率机器人程序应该使用MFC或其他能够辅助快速开发的工具来实现,另对于网络模块、协议定义、基本数据结构等应该使用(重用)与客户端程序同样的模块和配置。大致流程如下图所示,不过某些情况下,你应当做适当的处理,让你的机器人不至于完全挤在一起然后他们什么都不做。
Tips:一般每个“机器人”进程用来模拟一个玩家,然后在每个PC上运行几十甚至几百个这样的“机器人”程序,开启这些机器人可以用一个简单的“管理程序”来进行。管理程序在启动每个“机器人”进程的时候同时可以传入帐户、密码、服务器IP等等基本信息。在关闭这些服务器程序的时候,最简单的办法是使用Windows的“关闭组”命令。当然,前提是你在WindowsXP/2003系统上运行这些“机器人”程序。 |
在大多数时候,Windows自己的Performance Monitor可以用来分析几乎所有系统资源的使用情况,例如Processor、缓存、内存、网络、磁盘、进程等等。
运行Perfmon.exe,可以打开系统的性能检测工具。系统提供了大量可以用于采集分析的性能对象,在每种性能对象下面,可以选择你感兴趣的计数器添加为采集的数据源。
举例来说,如果一个进程的Memory/Pages/sec数据超出了建议的阀值:20(或者你自己的经验数值),那么很有可能此进程的内存页面交换活动过于频繁,或者你使用系统的内存偏少。这会成为一个性能的瓶颈。
Windows系统的帮助文档:《性能日志和警报》,详细的描述了所有性能对象和计数器的作用,包括在进行性能分析时如何选择计数器进行数据分析。我们可以在服务器系统上添加计数器日志,并设定保存为指定格式的Log文件以便分析。通常文本格式的Log文件(.tsv/.csv)因为比较容易使用Excel打开并制作相关图表,比较常用。
在MMOG系统内,数据库相关系统的表现对性能也有较大影响。对数据库本身的性能优化超出了本文的范围,可以参考一下相关的书籍。
对于使用Microsoft SQL Server的MMOG系统来说,在数据库的应用方面,我们可以借助SQL Server自带的事件探察器,主要对游戏服务器进行DB操作的频度和类型进行采样统计。
Compuware公司的DevPartner Studio(以下简称DPS)是一套面向Windows开发者的强大的开发辅助工具,对于提高软件系统的性能和质量有着很大的帮助。DPS与Visual Studio系列工具的集成相当出色,并且功能包括了Code Review、Error Detection、Coverage Analysis、Memory Analysis等等,所以在整个MMOG项目的开发、测试中都有用武之地。在此我们主要关注DPS在性能分析方面的特性。
Instrumentation并执行你的项目之后,DPS会生详细的统计数据,这些数据是都很直观。例如对于Coverage Analysis来说,你可以直接看到某些代码行运行了多少次,包括哪些函数运行次数多少,所占执行时间多少等等。总的来说,DPS的工作方式对于程序员来说是直观而且有极大帮助的。可能唯一的问题就在于开启DPS之后的运行速度——真的很慢,不过大多数时候我们没必要在这上面追求完美。
现在DPS的最新版本已经是8.0了。关于DPS几乎全部的信息,参考:http://www.compuware.com/products/devpartner/studio.htm
Intel 的VTune,也可以作为DPS的替代品,并且有一些她独到的特点。
请参考:http://www.intel.com/cd/software/products/asmo-na/eng/vtune/vpa/219898.htm
没有什么通用的性能分析工具能够为你统计出:‘玩家平均每秒发出多少个骂人指令’、‘系统的周贸易差额’,甚至是‘玩家的周上线率’这样的数据。很多时候这些数据对于系统性能提高以及玩家的行为分析都是很有用的,例如你可以借助这些数据大致分析出货币贬值的趋势。而且实现这些统计功能你只能自己开发。
不管你需要采集哪些数据,值得注意的是:不要让数据采集模块,把本来是多线程并行处理的服务器程序变成了单线程串行处理的,或者因为这个模块大大影响并行处理的能力。
比较好的解决办法是创造一个数据统计模块专用的‘统计线程’,然后在这个线程内WaitForMultipleObjects (),等待各种需要统计事件的Event通知并记录保存下来。
不过即使这样,也不要在开放测试期去统计会发生得非常频繁的事情,例如每分钟全体玩家移动了多少次,通常这种数据也是没有多大用处的。
对于MMOG服务器的Profiling工作来说,我们主要关注的有以下一些关键参数:CPU占用率(user time/privilege time)、内存占用变化情况、单位时间的Client请求处理量、页面失效率、单位时间的DB请求量、在线Client数等等,所有数据的采集都应包括时间变量。
以上各种数据,一般需要通过不同的采集方法收集,收集到的数据尽量都以Excel可以处理的格式进行保存,以便制作统计结果表格和制作更为直观的图表。
例如以下这样的表格:
1, 在线人数与CPU占用、内存占用、IO处理量的关系图;
2, 在线人数与DB请求处理量的关系图;
3, 在线人数与网络(包括WAN、LAN两个部分)带宽使用关系图;
通过对以上这些图表的分析,我们可以对服务器的承载能力得出直观的量化的结论。作为后续工作的指导数据。有了这些数据,你就不会在对系统性能做评价的时候总是说:大概、估计、差不多、或许、接近……这些词汇了——这会让你看上去更专业、可靠J。
我们的MMOG服务器程序来一般都是多线程的应用程序,这样才能充分发挥多CPU服务器、超线程CPU以及多核心CPU的优势。所以在进行Profiling的时候,各个CPU的‘负载均衡度’也是值得关注的。我们可以从Windows的‘任务管理器’内部的‘性能’页面,直观(当然,由于‘采样间隔’的原因,这只能给你个直观印象)的看出CPU的工作情况,我们应该尽量让各个CPU的工作负载均衡。
另外,我们也要避免‘游戏循环线程’或其他类似线程的周期性繁忙——例如忙碌2秒后闲置2秒,表现在‘性能’曲线上就是存在多个接近100%CPU占用的高峰。应该尽量让CPU的工作曲线随时间轴均匀分布,这一般可以通过调整一些模块的工作参数来达到。
在我们的MMOG程序中,合理的应用定长对象内存池、线程池、异步方式处理请求、在设计数据结构的时候尽量考虑避免运行时的缓存失效、在查找的时候应用Hash表,并且在所有可能的时候选用最简单、(对计算机来说)最直观的数据结构,都是对系统的提高性能很有帮助的。
但是最关键的提高系统性能的方法还是先找到性能的瓶颈,然后针对性能瓶颈处进行优化。
你的项目现在已经到了Open Beta测试的时候了,就要面对玩家了,不知道你是否已经为即将到来的一切而准备好了?你的客户端程序将会面对非常复杂的运行环境,IDC那里可能有让你琢磨不透的网络状况,还有——你的程序已经充分地测试过了吗?你知道,在开始开放测试之后,其实只需要一个微小的、毫不起眼的小Bug,就会导致你的服务器当机、数据回档,以及玩家震天的骂声。所以在这个时候你需要有坚强的神经和解决问题的自信,更重要的是你需要有充分的技术准备以应付接下来可能出现的任何局面。
你将要面对以下一些困难:
首先,是你无法像过去那样,在开发工具的调试环境下运行你的Server程序了。作为一个发行版本(Release Version),主要基于Runtime效率的考虑,编译器不再去为你做那些你本应该自己考虑到的事情,最常见的是变量以及内存初始化的工作,所以最常见的Release版本的错误提示就是“Access violation”,然后你的进程就不见了,玩家被断线,数据被回档。
别担心,测试的目的就是发现问题,当然还包括解决问题J。这个过程大概是这个样子。
我们可以看到,这副图里面最关键的一步在于收集的用来修正Bug的信息。你可能会在自己的程序内使用C++异常处理(EH)或者结构化异常处理(SEH)来捕获这些异常,并且分析产生原因。
一般MMOG的开发团队都会有至少4、5名程序员在一起做同一个程序,因为每个人的工作习惯不同,所以强制每个人使用C++异常处理或者SEH有时候是比较麻烦的事情,更糟糕的是有可能会出现在同一个程序中有人用C++异常处理同时有人用SEH异常处理的情况,微软在MSDN里面建议不要这么做。
我们下面来看一个更简单的方法——在代码内不到处使用C++异常处理或者SEH,只使用SetUnhandledExceptionFilter()来收集异常发生时候的信息并保存,然后进行“事后处理”。(函数参考:http://msdn.microsoft.com/library/default.asp?url=/library/en-us/debug/base/setunhandledexceptionfilter.asp)。
首先,在程序启动时调用:
::SetUnhandledExceptionFilter( TopLevelFilter );
然后在TopLevelFilter函数内部,保存Dump信息用于查找Bug,参考以下代码。
LONG TopLevelFilter( struct _EXCEPTION_POINTERS *pExceptionInfo ) { LONG ret = EXCEPTION_CONTINUE_SEARCH;
//先加载DbgHelp.DLL,HANDLE存于hDll(省略)
MINIDUMPWRITEDUMP pDump = (MINIDUMPWRITEDUMP)::GetProcAddress( hDll, "MiniDumpWriteDump" ); HANDLE hFile = ::CreateFile( szDumpPath, GENERIC_WRITE, FILE_SHARE_WRITE, NULL, CREATE_ALWAYS,FILE_ATTRIBUTE_NORMAL, NULL );
if (hFile!=INVALID_HANDLE_VALUE) { MINIDUMP_EXCEPTION_INFORMATION ExInfo;
ExInfo.ThreadId = ::GetCurrentThreadId(); ExInfo.ExceptionPointers = pExceptionInfo; ExInfo.ClientPointers = NULL;
// write the dump BOOL bOK = pDump( GetCurrentProcess(), GetCurrentProcessId(), hFile, MiniDumpNormal, &ExInfo, NULL, NULL );
if (bOK) { rel = EXCEPTION_EXECUTE_HANDLER; } else { // 错误处理 } ::CloseHandle(hFile); } return ret; } |
有了这个Dump文件(例如GameServer_03-15_12-13-24.dmp这样的一个文件,表示这是什么时候哪个程序生成的Dump)以及编译此EXE程序时生成的EXE、PDB文件之后,可以使用VC直接打开此dump文件,就能够得到以下基本信息:调用栈、进程的模块情况、线程的基本情况、寄存器情况等等,可以用于了解程序发生异常时候的基本状态,进而找到程序的Bug位置——当然,这需要一些通过实践得到的分析经验。
对于MMOG服务器程序来说,上面的TopLevelFilter函数最好再扩展一下,在成功的保存了Dump文件之后,再通过调用另外一个简单的程序(例如AutoReboot.exe)把你的服务器在发生异常之后自动重新启动。尽管你的服务器应该会一直有人看管,但是自动重启毕竟比手动来得要快,而且你难免会碰上一个懒惰的管理员。另外,在测试期也应该尽量保证游戏服务的可用性。
你可以在AutoReboot.exe这个程序内实现一些‘额外的’功能,例如立刻发送一封email(或者短信通知,听起来很Cool不是吗?)给某些人,告诉他们服务器在某个时间出了问题,基本的情况是怎么样的,等等。
相信大家都见过Windows的“自动错误报告发送程序”,我们也可以实现一个类似的程序,在游戏客户端发生错误的时候,通过这个程序来收集错误信息。
你可以安排一个“错误报告收集服务器”,来收集客户端的Dump信息。通过FTP或者HTTP协议,客户端那里的“自动错误报告发送程序”在经过玩家同意之后,向此服务器发送一个Dump文件。这对于完善“质量改进循环”是大有帮助的。
不是我想打击你,但是实际情况就是不管是从玩家那里还是从服务器那里,Dump文件都并非100%可靠的。主要是因为在某些情况下,Dump文件内部保存的信息可能是已经被破坏了的。当你发现自己通过Dump文件显示的调用栈关系“非常混乱”,或者发现“这不可能符合逻辑”的时候,就有必要怀疑Dump所反映事实的真是性了——根据我的经验,这多半是由于内存(写)操作的错误引起的。
在这种情况下,辅以游戏关键事件/错误事件的Log信息,应该可以让你逐步定位Bug的所在,当然这要看你对自己的程序以及程序执行机制、汇编语言的熟悉程度了。
服务器5分钟前当机了,你正在分析它又‘挂掉’的原因,脑子里头都是汇编符号。这时候电话响了,客服部门的一个MM用甜甜的语调告诉你说她很着急,因为有玩家因为‘穿不上那个漂亮的翅膀装备’在论坛上大骂客服,你的MSN、QQ上面的好友都在狂闪,他们说“天哪,我又掉线了!”……
你的大脑一片混乱……
在这个时候,保持清醒的头脑相当重要。所以最好形成一些规则——从任何部门、人员那里反馈来的错误信息、系统改进建议等等,都通过统一的(也是唯一的)渠道进行记录、分类、处理和检查。并且开发团队内部应当有专门的1、2个人负责对这些信息进行鉴别、分类、转发和答复。
这就是BugTrack系统的作用。别担心,你并不需要自己开发一套,现有的BugTrack系统足够你使用的,并且其中很多都是开源的工具、基于Web页面进行提交和管理,有着非常好的易用性。他们通常叫作‘错误跟踪系统、缺陷跟踪系统、错误管理工具’等等(我相信你回去google一下这些词的)。目前有很多错误管理工具,商业的如ClearQuest, PVCS Tracker,免费的如Bugzilla, Mantis, BugFree等,这些系统的用法也大同小异。我相信使用一个跟踪软件对你来说应该很简单,所以剩下的还是那句话,你去google一下吧。
在你搞定以上这些东西之后,相信你可以从容的面对各种故障了,不至于累得满头大汗却一无所获,也不至于回答你的老板的时候感到心跳加速。但是如何避免以后出那么多问题,其实是在开发初期时候就应该注意的,包括架构设计的合理性和技术选择的合理性,还有系统的‘可维护性’。不过别忘了,现在是测试期,很多玩家在那儿看着呢,我相信你不会去和他们说“哦,我们需要改进一下我们的做法,十二个月之后你们再来吧”。
除了以上提到的这些,在项目测试期还会有很多其他事情需要处理,例如你需要专门人员去测试硬件兼容性,包括各种操作系统、CPU、显卡、甚至是各种网吧的无盘工作站下的运行情况等等。
所以我才在这里提醒你——你应该为测试期的到来做好充分准备J。
GameRes游戏开发资源网 http://www.gameres.com
Author:王志勇