我们做了一个VR多人在线协作的项目。上线第一天就出了大故障。几百个用户同时在线。系统崩溃了。我作为技术负责人。经历了一次惊心动魄的故障排查和恢复。本文是这次故障的完整复盘。
一、项目背景
先说说这个项目的背景。
我们公司做了一个VR多人在线协作平台。多个用户可以在同一个虚拟空间里协作。比如一起看3D模型。一起开会。一起做设计。用户用VR头显接入。也可以用电脑或者手机接入。
这个项目做了半年。测试环境一直很稳定。最多测过50人同时在线。没有问题。我们觉得可以上线了。就选了一个周五的下午上线。想着周末用户不多。可以慢慢观察。
结果上线之后。用户比我们预想的多很多。第一天下午就有几百个用户同时在线。然后故障就发生了。
二、故障发生
上线当天下午三点左右。我们开始收到用户反馈。说进入虚拟空间之后很卡。画面延迟很严重。有时候会直接掉线。
我们看了一下监控。发现服务器的CPU和内存都很高。网络带宽也打满了。当时觉得可能是用户比预想的多。服务器配置不够。就先加了几台服务器。
但是加了服务器之后。情况没有好转。反而更严重了。有的用户直接连不上了。有的用户进去之后看到别人的头像卡在原地不动。语音也断断续续的。
到了下午四点。整个系统基本瘫痪了。用户根本无法正常使用。我们只能紧急下线。挂了一个维护中的页面。然后开始排查问题。
三、排查过程
故障发生之后。我们整个团队都投入了排查。我负责整体协调。
第一步:看日志
首先是看服务器日志。发现有大量的超时错误。很多请求都超时了。还有一些内存溢出的错误。
但是日志里没有明显的报错。不知道具体是哪里出了问题。只能看到服务器负载很高。响应很慢。
第二步:看监控
然后看监控数据。发现CPU、内存、网络带宽都很高。特别是网络带宽。入站和出站的流量都很大。比我们预估的高了好几倍。
我们当时觉得可能是网络带宽不够。就升级了带宽。但是升级之后情况还是没有好转。因为瓶颈不在带宽。而在服务器的处理能力。
第三步:复现问题
我们在测试环境尝试复现问题。但是测试环境最多只能模拟100人同时在线。100人的时候一切正常。没有复现故障。
这时候我们意识到。问题可能出在高并发场景下。只有几百人同时在线的时候才会出现。测试环境模拟不了这么大的并发。
第四步:抓包分析
于是我们开始抓包分析。看服务器和客户端之间传输的数据。
这一抓包就发现了大问题。原来每个客户端都在向服务器发送大量的位置和姿态数据。而且是每帧都发。VR头显的刷新率是90Hz。也就是说每个用户每秒发送90次位置和姿态数据。
几百个用户同时在线。每秒就是几万次数据传输。服务器要处理这些数据。然后再转发给其他所有用户。这就是一个N平方的问题。100个用户的时候还能处理。几百个用户的时候。服务器就处理不过来了。
而且我们传输的数据量也很大。位置和姿态是用浮点数表示的。没有做压缩。每次传输都要几十字节。几百个用户每秒几万次传输。带宽自然就打满了。
四、根本原因
找到了问题所在。我们开始分析根本原因。
原因1:没有做数据压缩
我们传输的位置和姿态数据都是原始的浮点数。没有做任何压缩。实际上。位置和姿态的精度不需要那么高。可以用整数或者压缩算法来减少数据量。
比如位置可以用厘米级的精度。用整数表示。姿态可以用四元数压缩。这样每次传输的数据量可以减少一半以上。
原因2:没有做频率控制
我们是每帧都发送位置和姿态数据。90Hz的刷新率。每秒90次。实际上。对于多人协作来说。不需要这么高的频率。30Hz甚至20Hz就足够了。人眼分辨不出来。
而且可以做插值。客户端收到数据之后。在两次数据之间做插值。这样即使发送频率低。看起来也很流畅。
原因3:没有做兴趣区域管理
我们把每个用户的数据都转发给了其他所有用户。不管用户在虚拟空间里的位置。实际上。一个用户只需要知道他附近的用户的位置和姿态。远处的用户不需要知道。
这就是兴趣区域管理(Area of Interest)。只把用户附近的数据转发给他。远处的不发或者降低频率。这样可以大大减少数据传输量。
我们当时没有做这个。所以是N平方的复杂度。用户越多。服务器压力越大。
原因4:服务器架构不合理
我们的服务器是单体架构。所有的逻辑都在一个服务器进程里。用户多了之后。一个进程处理不过来。而且没有做水平扩展。虽然加了服务器。但是负载均衡没有做好。有的服务器压力大。有的服务器压力小。
而且我们用的是TCP协议。TCP的开销比较大。对于实时性要求高的VR应用。应该用UDP协议。减少延迟和开销。
五、解决方法
找到了根本原因之后。我们开始紧急修复。
修复1:数据压缩
首先是做数据压缩。位置数据用厘米级的整数表示。三个坐标每个用两个字节。总共六个字节。比原来的12字节减少了一半。
姿态数据用压缩的四元数。四元数的四个分量。每个用一个字节表示。总共四个字节。比原来的16字节减少了四分之三。
这样每次传输的数据量从原来的几十字节减少到了十几个字节。带宽压力大大减轻。
修复2:频率控制和插值
然后是降低发送频率。从90Hz降到了20Hz。每秒只发送20次位置和姿态数据。
客户端收到数据之后。做插值处理。在两次数据之间平滑过渡。这样看起来还是很流畅。用户感觉不到差异。
发送频率降低之后。服务器的处理压力大大减轻。每秒需要处理的数据量减少了四分之三。
修复3:兴趣区域管理
然后是加了兴趣区域管理。每个用户只转发他周围一定范围内的用户的数据。范围之外的不转发。
这样数据转发量从N平方降到了接近N。因为每个用户只需要接收附近几个用户的数据。不需要接收所有用户的数据。
这一步优化效果最明显。服务器的负载直接降了一个数量级。
修复4:服务器架构优化
最后是优化服务器架构。把单体服务器拆分成了多个微服务。网关服务、匹配服务、房间服务、数据转发服务。每个服务可以独立扩展。
数据转发服务用了UDP协议。减少延迟和开销。而且做了更好的负载均衡。把用户均匀分配到不同的服务器上。
六、恢复上线
经过一个周末的紧急修复。我们在周一重新上线了。
这次上线之后。我们先小范围开放。只允许100个用户同时在线。观察了一天。一切正常。没有出现卡顿和掉线的问题。
然后逐步增加到200人、300人、500人。都很稳定。服务器的CPU、内存、带宽都在正常范围内。
最后完全开放。最高峰的时候有800多个用户同时在线。系统依然很稳定。用户体验也很好。没有出现卡顿和掉线的问题。
这次故障从发生到完全恢复。用了三天时间。虽然过程很惊心动魄。但是最终解决了问题。而且系统的性能比原来好了很多。
七、经验教训
这次故障给了我们很多经验教训。
教训1:上线前要做压力测试
这是最深刻的教训。我们在测试环境只测了50人同时在线。就觉得没问题了。实际上线之后有几百人同时在线。问题就暴露出来了。
上线之前一定要做充分的压力测试。模拟真实的并发量。甚至要超过预期的并发量。确保系统在高负载下也能稳定运行。
教训2:实时系统要考虑N平方问题
多人实时系统。很容易出现N平方的问题。每个用户都要和其他所有用户通信。用户多了之后。系统就扛不住了。
设计多人实时系统的时候。一定要考虑这个问题。用兴趣区域管理。用数据压缩。用频率控制。把复杂度从N平方降下来。
教训3:不要低估带宽的需求
VR/AR应用的数据量很大。位置、姿态、语音、视频。加起来带宽需求很高。我们当时低估了带宽需求。导致带宽打满。
做VR/AR应用的时候。一定要仔细计算带宽需求。包括上行和下行。而且要做数据压缩。减少传输量。
教训4:监控和告警要完善
故障发生的时候。我们的监控不够完善。很多指标没有监控到。导致排查问题花了很多时间。
上线之前一定要完善监控和告警。CPU、内存、带宽、延迟、错误率等都要监控。设置合理的告警阈值。出了问题能第一时间发现。
教训5:要有降级和回滚方案
故障发生的时候。我们没有降级方案。只能全部下线。影响了所有用户。
上线之前要准备好降级方案。比如用户太多的时候。可以限制新用户进入。或者降低发送频率。保证核心功能可用。还要有回滚方案。出了问题可以快速回滚到旧版本。
八、写在最后
这次VR/AR故障。是我职业生涯中最惊心动魄的一次经历。上线第一天系统就崩溃了。几百个用户无法使用。压力非常大。
但是通过这次故障。我们也学到了很多。对VR/AR实时系统的架构有了更深入的理解。系统的性能和稳定性也提升了很多。
做技术就是这样。不可能永远不出问题。出了问题不可怕。可怕的是不总结教训。同样的问题反复出现。
希望这篇复盘能给做VR/AR开发的人一些参考。避免踩我们踩过的坑。
最后用一句话结束本文:"故障是最好的老师。每一次故障都是一次成长的机会。"愿每一个技术人都能从故障中学习。做出更稳定更可靠的系统。
评论(0)
暂无评论,快来抢沙发~
评论功能仅对会员开放,请先登录
登录