这两年空间计算越来越火了。从苹果Vision Pro到各种AR眼镜空间计算设备正在快速普及。我们团队也在做一个空间计算平台支持大量用户同时在线进行空间交互和协作。

做空间计算平台和做普通的Web应用有很大的不同。它对延迟带宽并发的要求都很高。特别是当用户量上来之后架构设计就变得至关重要。

这篇文章我想分享一下我们在空间计算平台架构设计方面的一些经验特别是高可用和高并发方面的方案。

空间计算的特点

先说说空间计算平台的特点以及它对架构的要求。

第一个特点是实时性要求高。空间计算用户看到的虚拟内容需要和真实世界实时叠加。如果延迟高了虚拟内容就会和真实世界错位体验很差。所以端到端的延迟必须控制在几十毫秒以内。

第二个特点是数据量大。空间计算需要处理大量的空间数据。比如环境的三维模型用户的位置和姿态虚拟物体的状态等。这些数据都需要实时传输和处理。

第三个特点是并发要求高。一个空间计算场景可能有很多用户同时在线。大家在同一个空间里进行交互和协作。这就要求系统能支持大量的并发连接。

第四个特点是状态同步复杂。多用户在同一个空间里每个人的动作每个物体的状态都需要实时同步给其他用户。这比普通的多人在线游戏还要复杂因为空间计算是三维的自由度更高。

这些特点决定了空间计算平台的架构必须是高可用高并发低延迟的。

整体架构

我们的空间计算平台整体采用了微服务架构分为几个层次。

第一层是接入层。接入层负责接收客户端的连接进行认证和鉴权然后把请求转发到后端的服务。我们用了网关集群支持水平扩展。接入层还负责协议转换。客户端可能用不同的协议比如WebSocket WebRTC私有协议等接入层统一转换成内部的协议再转发给后端。

第二层是业务层。业务层是各种微服务负责处理具体的业务逻辑。比如用户服务空间服务物体服务交互服务等。每个微服务都是无状态的可以独立部署和扩展。服务之间通过消息队列进行通信解耦。

第三层是同步层。同步层是空间计算的核心负责多用户之间的状态同步。我们用了专门的同步引擎基于状态同步的方式把每个用户的状态和物体的状态实时同步给其他用户。同步层是对延迟最敏感的部分。我们做了很多优化来降低延迟。

第四层是存储层。存储层负责持久化数据。比如用户信息空间数据物体数据等。我们用了多种存储关系型数据库存结构化数据NoSQL存非结构化数据对象存储存三维模型等大文件。

第五层是计算层。计算层负责一些计算密集型的任务。比如三维重建空间理解AI推理等。这些任务计算量很大我们用了专门的计算集群还做了GPU加速。

高可用设计

高可用是空间计算平台的基本要求。如果系统挂了用户就无法进行空间交互体验很差。我们从几个方面来保证高可用。

第一个是多可用区部署。我们的服务都部署在多个可用区。每个可用区都是独立的有自己的网络和电力。如果一个可用区出了问题其他可用区还能继续服务。流量通过负载均衡分发到各个可用区。某个可用区挂了负载均衡会自动把流量切到其他可用区用户无感知。

第二个是服务无状态化。我们的业务服务都是无状态的。所有的状态都存在外部的存储里比如Redis数据库等。这样任何一个服务实例挂了其他实例可以立刻接管不会丢失状态。无状态化也让服务的扩展变得很简单。需要更多的处理能力时直接加机器就行不用做数据迁移。

第三个是熔断和降级。在微服务架构中一个服务出问题可能会导致连锁反应把整个系统拖垮。所以我们做了熔断和降级机制。当某个服务的失败率超过阈值时熔断器会打开直接返回默认结果不再调用那个服务。这样就能防止故障扩散。同时我们还做了降级。当系统压力大的时候会自动关闭一些非核心功能保证核心功能的可用性。比如空间计算的核心是状态同步其他的比如排行榜成就系统等都可以降级。

第四个是健康检查和自动恢复。我们对所有的服务实例都做了健康检查。如果某个实例不健康调度系统会自动把它从负载均衡中摘掉然后重启它或者启动一个新的实例。这样服务的故障能被自动发现和恢复不需要人工干预。

第五个是数据多副本。存储层的数据我们都做了多副本。关系型数据库用了主从复制NoSQL用了副本集对象存储用了多副本存储。这样即使某个存储节点挂了数据也不会丢失服务也能继续运行。

高并发设计

高并发是空间计算平台的另一个挑战。当大量用户同时在线时系统必须能扛住压力。我们从几个方面来保证高并发。

第一个是水平扩展。我们的所有服务都支持水平扩展。当并发量上来的时候直接加机器就行。接入层业务层同步层都可以独立扩展。我们用了容器化部署配合自动伸缩机制。当CPU或内存使用率超过阈值时会自动增加实例。当使用率降下来时会自动减少实例。这样既能扛住高峰又能节省成本。

第二个是缓存。缓存是提高并发能力的利器。我们在很多地方都用了缓存。比如用户的基本信息空间的元数据物体的配置等这些不经常变化的数据都存在Redis里。请求来了先查缓存缓存没有再查数据库。这样大大减轻了数据库的压力。我们还做了多级缓存。本地缓存分布式缓存CDN缓存根据数据的特点选择合适的缓存层级。

第三个是异步化。对于一些不需要实时返回结果的操作我们都做成了异步的。比如用户的行为日志数据统计消息通知等都通过消息队列异步处理。这样用户的请求能快速返回不用等待这些操作完成。同时也把写压力从高峰期分散到了平时。

第四个是读写分离。数据库层面我们做了读写分离。写操作走主库读操作走从库。这样就能把读压力分散到多个从库上。对于一些读多写少的场景我们还做了分库分表进一步提高并发能力。

第五个是边缘计算。空间计算对延迟很敏感。如果所有的数据都传到中心服务器处理延迟会很高。所以我们用了边缘计算。在离用户近的地方部署边缘节点。用户的请求先到边缘节点边缘节点能处理的就直接处理不用传到中心。比如本地的空间计算状态同步等都可以在边缘节点完成。只有边缘节点处理不了的才传到中心服务器。这样大大降低了延迟也减轻了中心服务器的压力。

低延迟优化

除了高可用和高并发低延迟也是空间计算的关键。我们做了很多优化来降低延迟。

第一个是协议优化。我们用了UDP协议而不是TCP。TCP虽然可靠但握手和重传的机制会增加延迟。UDP没有这些开销延迟更低。当然UDP不可靠我们在应用层做了可靠性保证。对于关键的状态数据做了重传和确认。对于非关键的数据比如一些临时的特效丢了就丢了不影响体验。

第二个是状态压缩。空间计算的状态数据量很大。如果直接传输会占用很多带宽也会增加延迟。所以我们对状态数据做了压缩。我们用了专门的压缩算法对位置姿态物体状态等数据进行压缩。压缩率能达到5到10倍大大减少了传输的数据量。

第三个是预测和插值。网络延迟是不可避免的。为了让用户感觉不到延迟我们用了预测和插值技术。客户端会根据之前的状态预测用户和物体的运动。这样即使网络有延迟用户看到的画面也是流畅的。当真实的状态数据到达后再平滑地修正过来。

第四个是就近接入。我们在全球部署了很多接入点。用户连接的时候会自动选择离自己最近的接入点。这样网络的距离最短延迟最低。

踩过的坑

在做架构设计的过程中我们也踩过一些坑。

第一个坑是一开始就想做完美的架构。最开始我们想一步到位做一个完美的架构。结果设计了很久都没有落地。后来我们改变了思路先做一个简单能用的架构跑起来然后根据实际的压力和问题逐步优化。架构不是设计出来的是演进出来的。

第二个坑是过度设计。最开始我们考虑了很多极端情况做了很多复杂的设计。结果系统变得很复杂维护起来很困难而且很多设计根本用不上。后来我们做了减法只保留最核心的功能。简单的架构反而更可靠更容易维护。

第三个坑是忽视了监控。最开始我们对监控不够重视。系统出了问题都不知道是哪里出的。后来我们建立了完善的监控体系对系统的各个指标都做了监控和告警。这样出了问题能快速定位和解决。

第四个坑是没有做好压测。最开始我们没有做充分的压测。上线之后用户量一上来系统就扛不住了。后来我们建立了压测环境每次上线之前都做充分的压测确保系统能扛住预期的压力。

写在最后

空间计算是一个很有前景的方向。但要做好一个空间计算平台架构设计是关键。高可用高并发低延迟这三个是空间计算平台的基本要求。要做到这三点需要在架构设计上做很多的考虑和优化。

当然架构不是一成不变的。随着用户量的增长和业务的发展架构也需要不断演进。

希望这篇文章的经验分享能给做空间计算的朋友一些参考。也欢迎大家在评论区交流分享你们的经验。

最后用一句话来结束这篇文章:好的架构不是设计出来的是在实际运行中不断打磨和演进出来的。

愿每一个做技术的人都能设计出高可用高并发的好系统。