我们做了一个元宇宙概念验证项目。多人可以在虚拟空间里互动。结果在一次演示中出了大故障。几十个人同时在线。系统崩溃了。我作为技术负责人。经历了一次惊心动魄的故障排查和恢复。本文是这次故障的完整复盘。
一、项目背景
先说说这个项目的背景。
元宇宙这个概念最近很火。我们公司也想跟一下风口。就做了一个概念验证项目。简单来说就是一个多人在线的虚拟空间。用户可以用虚拟形象进入。在空间里走动。和其他人聊天。一起看3D模型。做一些简单的互动。
这个项目做了两个月。用的是Unity做客户端。Node.js做服务端。WebRTC做语音和视频传输。测试环境一直很稳定。最多测过20人同时在线。没有问题。
我们觉得可以演示了。就约了领导和客户。准备做一个30人的在线演示。结果演示当天就出了大故障。
二、故障发生
演示当天。大家陆续进入虚拟空间。最开始一切正常。20个人的时候很流畅。大家在空间里走动。聊天。看3D模型。领导和客户都觉得不错。
然后人数增加到25个。开始有一点卡。但是还能接受。到了28个人的时候。突然就不行了。所有人的画面都卡住了。语音也断了。有人直接掉线了。
我当时在后台看监控。发现服务器的CPU直接冲到了100%。内存也快满了。网络带宽打满了。然后服务就崩溃了。所有人都掉线了。
演示现场很尴尬。领导和客户都在看着。我只能说技术出了点问题。需要重启一下。然后赶紧去排查问题。
三、排查过程
故障发生之后。我和团队赶紧排查。
第一步:看日志
首先看服务器日志。发现有大量的错误。主要是内存溢出。还有消息队列堆积。但是日志里没有明确指出是哪里的问题。
第二步:看监控
然后看监控数据。CPU、内存、网络带宽都很高。特别是入站流量。比我们预估的高了好几倍。
我们当时觉得可能是带宽不够。就先升级了带宽。但是升级之后情况没有好转。因为瓶颈不在带宽。而在服务器的处理能力。
第三步:复现问题
我们在测试环境尝试复现。但是测试环境最多只能模拟20人同时在线。20人的时候一切正常。复现不了故障。
这时候我们意识到。问题可能出在高并发场景下。只有人数超过某个阈值之后才会出现。
第四步:抓包和性能分析
于是我们开始做更深入的分析。用了性能分析工具。看服务器的CPU都花在哪里了。
这一分析就发现了大问题。原来我们的服务端在做位置同步的时候。是全量广播的。每个用户的位置和动作。都会广播给其他所有用户。这就是一个N平方的问题。
20个人的时候。每个用户要接收19个人的位置数据。还能处理。30个人的时候。每个用户要接收29个人的数据。而且服务端要处理30乘29也就是870条消息。这时候就处理不过来了。
而且我们传输的数据量很大。每个用户的位置、旋转、动作、表情、语音。加起来每次传输都有几百字节。30个人每秒几十次更新。数据量就非常大了。
还有一个问题是。我们用了一个第三方的实时通信服务。这个服务在人数多的时候。延迟会增加。导致消息堆积。最后把服务端拖垮了。
四、根本原因
找到了问题所在。我们开始分析根本原因。
原因1:没有做兴趣区域管理
和之前VR项目的问题一样。我们把每个用户的数据都广播给了其他所有用户。不管用户在虚拟空间里的位置。实际上。一个用户只需要知道他附近的人的数据。远处的人不需要知道。
我们没有做兴趣区域管理。导致了N平方的复杂度。人数一多。服务器就扛不住了。
原因2:没有做数据压缩
我们传输的位置和旋转数据都是原始的浮点数。没有做压缩。实际上。位置不需要毫米级的精度。用厘米级就够了。旋转也可以压缩。这样每次传输的数据量可以减少一半以上。
原因3:没有做频率控制
我们是每帧都发送位置和动作数据。客户端的帧率是60帧。也就是说每秒发送60次。实际上。对于多人互动来说。20次每秒就足够了。人眼分辨不出来。
发送频率太高。导致了数据量过大。服务器处理不过来。
原因4:第三方服务的瓶颈
我们用的第三方实时通信服务。在人数多的时候性能下降。延迟增加。导致消息堆积。最后把我们的服务端也拖垮了。
而且第三方服务的并发数有限制。我们没有注意到这个限制。超过之后服务就不稳定了。
原因5:测试不充分
最根本的原因是测试不充分。我们只测了20人同时在线。没有测30人的情况。以为20人没问题。30人也没问题。结果人数增加了50%。负载增加了不止50%。因为是N平方的复杂度。
五、解决方法
找到了根本原因之后。我们开始紧急修复。
修复1:兴趣区域管理
首先是加了兴趣区域管理。每个用户只转发他周围一定范围内的用户的数据。范围之外的不转发。
这样数据转发量从N平方降到了接近N。因为每个用户只需要接收附近几个用户的数据。服务器的负载直接降了一个数量级。
修复2:数据压缩
然后是做数据压缩。位置数据用厘米级的整数表示。三个坐标每个用两个字节。比原来的12字节减少了一半。
旋转数据用压缩的四元数。四个分量每个用一个字节。比原来的16字节减少了四分之三。
动作和表情用枚举值表示。一个字节就够了。
这样每次传输的数据量从几百字节降到了几十字节。带宽压力大大减轻。
修复3:频率控制和插值
然后是降低发送频率。从60Hz降到了20Hz。每秒只发送20次位置和动作数据。
客户端收到数据之后。做插值处理。在两次数据之间平滑过渡。这样看起来还是很流畅。用户感觉不到差异。
发送频率降低之后。服务器的处理压力大大减轻。每秒需要处理的数据量减少了三分之二。
修复4:更换通信方案
我们把第三方实时通信服务换成了自己搭建的WebSocket服务。虽然开发成本高了一些。但是性能和可控性都更好。
自己搭建的服务可以根据需求优化。没有并发数的限制。也没有第三方服务的延迟问题。
修复5:增加压力测试
最后是完善了测试流程。加了压力测试。用脚本模拟大量用户同时在线。测试系统在高负载下的表现。确保上线之前能发现问题。
六、恢复演示
经过两天的紧急修复。我们重新做了演示。
这次演示。我们先小范围开放。10个人。一切正常。然后增加到20人。30人。40人。都很稳定。服务器的CPU、内存、带宽都在正常范围内。
最高峰的时候。我们同时在线50人。系统依然很流畅。用户体验也很好。没有出现卡顿和掉线的问题。
领导和客户都很满意。这次故障虽然惊心动魄。但是最终解决了问题。而且系统的性能比原来好了很多。
七、经验教训
这次故障给了我们很多经验教训。
教训1:概念验证也要认真做
虽然这只是一个概念验证项目。但是既然要演示。就要认真做。不能因为是概念验证就降低标准。否则演示的时候出问题。影响很不好。
教训2:多人实时系统要考虑N平方问题
和之前VR项目的教训一样。多人实时系统一定要考虑N平方的问题。用兴趣区域管理。用数据压缩。用频率控制。把复杂度降下来。
这个教训我们之前已经踩过一次了。结果这次又踩了。说明没有真正吸取教训。以后做类似的项目。一开始就要把这些设计进去。
教训3:测试要覆盖预期的最大并发
上线之前一定要做充分的压力测试。至少要测到预期最大并发的1.5倍。确保系统在高负载下也能稳定运行。不能只测正常情况下的并发。
教训4:不要过度依赖第三方服务
第三方服务虽然方便。但是有很多限制。比如并发数、延迟、稳定性。对于核心功能。尽量自己实现。或者至少有备选方案。
教训5:监控和告警要完善
故障发生的时候。我们的监控不够完善。很多指标没有监控到。导致排查问题花了很多时间。以后做项目。一开始就要把监控做好。
八、写在最后
这次元宇宙概念验证项目的故障。是我职业生涯中又一次惊心动魄的经历。演示现场系统崩溃。压力非常大。
但是通过这次故障。我们也学到了很多。对多人实时系统的架构有了更深入的理解。系统的性能和稳定性也提升了很多。
元宇宙是一个很有前景的方向。但是技术还不成熟。很多问题需要我们去探索和解决。这次踩的坑。也算是为未来的探索积累了经验。
最后用一句话结束本文:"概念验证不是随便做做。而是为未来探路。每一次故障都是一次成长。"愿每一个技术人都能从故障中学习。做出更稳定更可靠的系统。
评论(0)
暂无评论,快来抢沙发~
评论功能仅对会员开放,请先登录
登录