二月七日,凌晨一点。深圳南山,创新大厦五楼东侧的玻璃房里,空气凝固得像一块冰。十六台高配电脑屏幕亮著,十六双眼睛盯著屏幕,但没人说话。只有伺服器风扇全速运转的轰鸣声,像某种巨大野兽垂死前的喘息,在密闭空间里迴荡。
屏幕上是“山海”压力测试的监控面板。左侧是实时数据:在线人数 8,427,还在缓慢增长;帧率 28 fps,勉强可玩;网络延迟 112 ms,偏高但可接受。右侧是资源占用:cpu 97%,內存 89%,资料库连接池占用 100%。所有指標都在危险边缘跳舞。
阿坤坐在监控台前,背挺得笔直,但手在抖。他眼镜片上反射著屏幕的光,那些跳动的数字像心电图,显示著“山海”这个新生世界的脉搏——虚弱,但还在跳。他已经四十八小时没合眼,下巴的胡茬青黑一片,眼里的血丝像蛛网。
“一万了。”王磊的声音从身后传来,很轻,但在一片死寂中像炸雷。
在线人数突破一万。这是“山海”偽3d引擎的第一次万人同图测试,目標是在简陋硬体(模擬2003年主流配置:赛扬1.7ghz,256mb內存,英特尔845g集显)上,跑出流畅的体验。测试用户是浩宇从《血战天下》老玩家里招募的志愿者,两千人,加上八千个模擬机器人。伺服器是刚採购的戴尔poweredge 2650,四路至强,16gb內存,在2004年算是顶配。
但现在,顶配伺服器在呻吟。
“资料库响应时间突破500毫秒了。”张一鸣盯著另一个监控界面,声音平静,但眉头紧锁,“索引命中率在下降,有全表扫描的跡象。我们的查询优化器可能有问题。”
“关掉非核心服务。”阿坤说,声音嘶哑,“邮件系统、排行榜、聊天频道,全部停掉。集中资源保核心玩法。”
“已经在做了。”赵永在另一台机器上快速操作,“但资料库连接池满了,新请求在排队。等待队列已经积压了三千多个查询。”
“清连接。把閒置超过三十秒的连接踢掉。”
“踢了,但新连接建立需要时间,玩家会感觉卡。”
“顾不上了,先保住伺服器不崩。”
玻璃房里键盘声密集起来,像暴雨敲打铁皮屋顶。每个人都在自己的终端上操作,关服务,调参数,杀进程。但屏幕上的数字还在恶化:cpu 99%,內存 92%,资料库响应时间突破800毫秒。
凌晨一点四十七分,第一波玩家掉线。世界聊天频道(还没关掉的部分)开始刷屏:
“卡死了!”
“动不了!”
“技能放不出来!”
“伺服器炸了?”
阿坤的手指在键盘上飞舞,调出资料库慢查询日誌。日誌在疯狂滚动,每秒新增几百条。他快速扫描,锁定一个查询:
select * from player_items where player_id = ? and item_type in (?,?,?,?,?,?,?,?,?,?) order by created_at desc limit 50
“这个查询,”他指著屏幕,“player_items表有千万级数据,item_type有十个可能值,索引是(player_id, item_type),但in查询让索引失效了。全表扫描,每秒执行几千次,资料库不崩才怪。”
“谁写的?”王磊问。
“我。”陈默的声音从角落传来,很小,“物品系统我负责的。我想一次查出玩家所有类型的物品,减少查询次数……”
“想法没错,但实现错了。”阿坤快速改代码,“拆成十个查询,用union,或者用临时表。但现在来不及了,先加force index强制走索引。”
他改完,部署。三分钟后,资料库压力稍降,但cpu还是99%。
凌晨两点零八分,在线人数突破一万二。伺服器开始出现周期性卡顿:每三十秒,帧率掉到5以下,持续五秒,然后恢復。玩家骂得更凶了。
“是gc(垃圾回收)。”张一鸣调出jvm监控,“老年代內存满了,每次full gc要五秒。我们的对象创建太频繁,特別是技能特效和伤害数字,每个都是独立对象,用完就丟。”
“用对象池。”阿坤说,“但现在改不了,先加大堆內存。”
“已经最大了,16g全给了java。物理內存不够了。”
“那就砍特效。同屏技能特效超过十个,后面的不显示。”
“在做了。”
凌晨两点三十三分,在线人数一万三千五百。伺服器cpu曲线出现锯齿状波动,从99%掉到60%,又飆回99%。这是死锁的典型特徵——某个资源被竞爭,线程在等待中休眠,然后被唤醒,继续竞爭。
“资料库死锁。”赵永调出死锁日誌,屏幕上一片红,“事务a锁了玩家表行1,等待物品表行2;事务b锁了物品表行2,等待玩家表行1。循环等待,超时后自动回滚,但新请求又来,继续死锁。”
“事务隔离级別是rr(可重复读)?”阿坤问。
“是。我们之前为了数据一致性,设的rr。”
“改成rc(读已提交)。先活下来,再谈一致性。”
“改了,需要重启资料库。”
“重启。”
资料库重启花了三分钟。这三分钟,所有玩家掉线。世界频道最后一条消息是:“浩宇牛逼,把伺服器测炸了!”后面跟了一串笑哭的表情。
凌晨两点四十一分,资料库重启完成。玩家重新连接,在线人数慢慢爬回八千。但所有人都知道,测试已经失败了。万人同图的目標,在现实面前碎了一地。
玻璃房里死寂。只有伺服器风扇还在响,但声音听起来像嘲讽。
阿坤摘下眼镜,用力揉著眉心。手指在抖。他想起三个月前,林浩在白板上画“山海”的架构图,眼神坚定地说“我们要做中国人自己的3d mmo”。想起团队加班加点,写了几十万行代码,调了几千个参数。想起今天测试前,大家眼里那种期待的光。
现在,光灭了。
“是我的问题。”阿坤开口,声音很乾,“资料库中间件是我写的,事务隔离级別我设的,对象创建策略我定的。崩了,我负责。”
“现在不是追责的时候。”王磊说,“问题是,接下来怎么办?测试失败了,消息传出去,玩家会失望,投资方会质疑,盛大会看笑话。”
“重写。”另一个声音从门口传来。
所有人回头。林浩站在玻璃房门口,穿著简单的灰色卫衣,头髮乱糟糟的,看起来也一夜没睡。他手里拿著一个保温杯,走到阿坤身边,看著监控屏幕。
“资料库中间件的线程调度模块,要重写。”林浩说,声音很平静,“现在的调度算法是简单的轮询,高並发下会有飢饿现象。要改成带优先级的抢占式调度,高优先级事务(比如战斗、交易)优先执行,低优先级(比如邮件、成就)排队。死锁检测要从被动等超时,改成主动检测,发现死锁立即拆,而不是等十分钟。”
阿坤看著他:“现在重写?来得及吗?”
“来得及。”林浩把保温杯放在桌上,拉过一把椅子坐下,“你们去睡,我写。天亮前,新模块上线,重新测试。”
“你一个人?”
“嗯。”林浩已经打开了代码编辑器,调出资料库中间件的源码,“这个模块我熟。当初设计的时候,就留了后手——调度算法是插件化的,换掉核心文件就行。你们去休息,养足精神,早上八点,第二轮测试。”
没人动。所有人都看著他,这个十八岁的创始人,在伺服器崩了、测试失败、团队士气跌到谷底的时候,没有骂人,没有抱怨,只是平静地说“我重写”。
“我陪你。”阿坤重新戴上眼镜。
“我也在。”赵永说。
“加我一个。”张一鸣坐回座位。
王磊笑了,是那种疲惫但释然的笑:“行,那都別睡了。林浩写调度,阿坤调资料库,一鸣优化gc,我盯著伺服器。陈默,你带美术组去睡,明天还要改特效。”
陈默点头,带著美术组的人离开了。玻璃房里剩下九个技术核心,九台电脑,九双盯著屏幕的眼睛。
林浩已经开始写了。手指在键盘上快速敲击,几乎不用思考。他写的是一个全新的线程调度器,基於时间片轮转加优先级抢占。高优先级事务(战斗、移动、技能)分配大时间片,低优先级事务分配小时间片。死锁检测模块独立成一个守护线程,每100毫秒扫描一次事务图,发现环就强制回滚优先级最低的事务。
代码像流水一样从他指尖淌出。变量命名规范,注释清晰,逻辑严谨。阿坤在旁边看著,心里越来越惊。这种代码,不像现场写的,像在脑子里酝酿了很久,只是现在倒出来。调度算法的数学证明,死锁检测的图论算法,都是研究生级別的难度,但林浩写得行云流水,像写hello world。
凌晨四点二十,新调度模块写完。林浩敲下最后一行注释,保存,编译。没有错误。
“部署。”他说。
阿坤接过编译好的jar包,替换掉原来的中间件,重启服务。伺服器再次轰鸣起来,监控屏幕上的数字开始跳动。
凌晨四点五十,玩家重新连接。在线人数慢慢爬升:五千,八千,一万……突破一万二,稳住了。
cpu占用降到85%,內存80%,资料库响应时间200毫秒。帧率稳在30以上。
世界频道又开始刷屏,但这次是:
“不卡了?”
“流畅了!”
“浩宇牛逼!”
“刚才是不是在偷偷优化?”
玻璃房里,九个人盯著屏幕,没人说话。直到在线人数突破一万五,所有指標依然平稳,王磊才长出一口气,瘫在椅子上。
“过了。”他说,声音发虚。
“过了。”阿坤重复,摘下眼镜,这次是真的哭了,眼泪无声地流下来。不是难过,是释放,是这四十八小时紧绷的神经终於松下来的生理反应。
林浩也靠在椅背上,看著屏幕。窗外,天快亮了,深蓝色的天幕边缘泛起鱼肚白。他拿起保温杯,喝了一口水,很凉。
“但这只是第一关。”他说,声音很轻,但每个人都听见了,“资料库中间件的问题解决了,但偽3d渲染的瓶颈还在。万人同图,集显跑不动,这是硬体天花板。我们要做的不是突破天花板,是在天花板底下,找到能让玩家觉得『爽』的平衡点。”
“怎么找?”张一鸣问。
“数据。”林浩说,“这次压力测试,產生了十亿条玩家行为日誌。一鸣,你分析这些数据,找出玩家的核心行为路径:他们大部分时间在干什么?是打怪,是社交,是探索,还是发呆?然后我们优化核心路径的体验,非核心路径可以简化甚至砍掉。我们要做的不是让游戏在所有方面都完美,是让玩家在意的方面,做到极致。”
张一鸣点头,手指已经在键盘上敲起来了。
窗外,天彻底亮了。晨光照进玻璃房,照在九张疲惫但兴奋的脸上。
伺服器还在跑,在线人数稳在一万六千。
“山海”的第一场压力测试,在崩溃边缘被拉回来了。
但所有人都知道,真正的战爭还没开始。
这只是第一次衝锋。
后面还有更难的:性能优化,內容填充,商业模式,市场竞爭,以及……盛大的虎视眈眈。
但至少今夜,他们贏了。
贏了一小局。
林浩站起身,走到窗边,看著外面渐渐甦醒的深圳。
城市在晨光中轮廓分明,像一头刚睡醒的巨兽。
而浩宇,刚刚在这头巨兽的脚边,点亮了一盏小小的灯。
灯很弱,但亮著。
而且,会一直亮下去。