Roblox 如何将其 Kafka 平台扩展至每日处理超过 18 万亿条消息
高效系统的端到端优化

在过去的两年里,我们每年将内部消息队列平台的处理能力扩大2.5倍以上,目前已达到每天超过18万亿条消息。 目前,我们每秒处理超过3亿条消息,同时保持99.995%的可用性。这个基于Apache Kafka构建的消息队列平台,已成为公司内部数据传输的关键环节。它支持分析事件、审核流程、匹配系统等众多工作负载。这些数据固然令人振奋,但更重要的是我们如何实现这一目标。
端到端优化的影响
关于大规模系统,一个常见的误解是:容量主要取决于硬件。根据我们的经验,将可扩展性视为一个端到端的工程问题,才能带来最具影响力的改进。
Roblox 各团队在消息队列的基础上构建了各种平台,包括 AI 训练管道、流处理抽象层和遥测系统。通过与这些平台的负责人合作,我们能够确保遵循最佳实践,例如启用批处理和压缩功能。这使得系统能够高效运行,而无需终端用户参与详细的配置设置。

与使用该平台的团队在业务优先级、衡量标准和成功标准方面保持一致至关重要,这有助于推动跨团队协作。关键指标在系统的不同层级中各不相同:虽然我们可能会优化 Kafka 服务器,以便用更少的机器高效处理海量数据,但最终用户可能希望将端到端延迟优化到毫秒级,或者优化其批处理作业完成数据写入所需的总时间。
并非所有流量都是一样的
我们学到的一条关键经验是:大规模平台若不将所有流量一视同仁,其可靠性会更高。某些工作负载在流量高峰期间需要极低的延迟和强有力的保护,例如我们的匹配系统,其端到端延迟必须控制在 10 毫秒以内。 而另一些工作负载虽然流量极大,但对延迟的容忍度更高,例如我们的分析事件采集管道,即使端到端延迟达到几秒,也能平稳运行。如果将这两类流量一视同仁,流量最大的那类可能会挤占对业务更为关键的流量,尤其是在负载高峰期间。
为解决这一问题,我们根据用例需求对流量进行分组,以确保每种用例都能得到良好支持。在 Kafka 中,端到端延迟与吞吐量之间的权衡是设置新用例时的主要考虑因素之一。这一原则贯穿从生产者到服务器再到消费者的整个数据路径:每个环节都有可调配置,用于优化吞吐量或延迟。 客户端配置可根据具体用例进行调整,但服务器端配置的灵活性较低,因为为每个不同的用例维护一个专用集群是不可行的。
我们将这种“针对所服务的用例优化每个集群”的方法,从上到下应用到了硬件层。
硬件依然至关重要
起初,我们在采用 RAID10 硬盘的机器上运行 Kafka。随着流量的增长,机器的磁盘 I/O 容量逐渐成为限制单台服务器 Kafka 吞吐量的主要瓶颈。为解决这一问题,我们进行了两项优化:首先,我们将代理(brokers)尽可能转换为使用 SSD,从而将瓶颈从磁盘 I/O 容量转移到网络吞吐量或磁盘空间上(因为我们采购的 SSD 磁盘空间较小); 此举使磁盘 I/O 吞吐量提高了 10 倍。此外,我们还开始将剩余的 HDD 从 RAID10 转换为 RAID0。由于 RAID0 每次写入只需写入单个磁盘而非两个,因此磁盘 I/O 容量大约翻了一番。
其权衡在于:由于每个消息副本在每个经纪人上仅写入一个磁盘而非两个,从 RAID10 转换为 RAID0 可能会带来可靠性风险。为保持可靠性,我们针对关键用例增加了确认机制并加强了数据复制,包括向多个区域进行复制。
要充分利用每台机器的存储和吞吐量能力,还需要额外的工作。我们优化了内存分配和页面缓存管理配置,使 Kafka 能够缓存消息,并从内存中处理读取请求或提供近期数据,仅在必要时(例如读取历史数据时)才访问磁盘并消耗宝贵的 IOPS。
我们还运行了性能基准测试,以了解硬件限制,并确定需要设置哪些软件层面的限制。实际上,这意味着要设置 Kafka 代理的读写限流,具体数值取决于集群所运行的硬件。我们的 SSD 服务器每台代理可处理更高的吞吐量,而 HDD 服务器则更适合需要长期保留历史数据的使用场景。
构建可靠性
队列平台是 Roblox 基础设施的关键组成部分,保持其可靠性至关重要。上线前测试以及持续进行的故障场景测试(有时称为“实战演练”)在验证我们的基础设施能否抵御单台主机故障、机架故障、网络问题等多种故障模式方面发挥了关键作用。 我们可靠性团队的内部混沌测试平台使模拟各种故障场景的过程得以实现并得到优化。这些测试有助于识别系统改进点或退化问题,并通过定期验证警报以及确保运维人员熟悉处理各类问题(甚至包括罕见问题)的步骤,从而构建卓越的运维能力。
我们还设计并实施了故障转移流程,能够快速为相关用例恢复队列功能。即使单个集群发生全面故障,我们也能将客户端切换到具有充足容量的健康集群。为了确保有足够的闲置容量可用,我们维护了一个备用 Kafka 集群。这有助于在活跃集群之间转移流量时消除“噪音邻居”问题的风险。
下图展示了执行故障转移的高层级步骤。首先,运维人员确定故障转移的范围。然后,通过自动批量操作在备用集群上创建主题和访问控制列表(ACL)。最后,运维人员更新我们的控制平面服务配置,客户端会轮询这些配置以确定是否需要重新连接。下图适用于通用的实时用例。 针对其他用例和场景,我们还提供了额外的策略和工具,包括使用 Mirror Maker 2 进行主题镜像、数据回填工具等。

Roblox 对安全性有严格的要求,这也是可靠性不可或缺的一部分。我们已实施了标准的安全功能,例如传输中加密(使用 mTLS)、静止状态加密(使用加密磁盘)、数据保留期限要求,以及基于 Kafka 主体和访问控制列表 (ACL) 的身份验证/授权。我们还会自动轮换用户的凭据,以进一步提升安全性。
除此之外,我们在控制平面中构建了自定义处理机制,以支持快速的操作系统补丁更新和机架清空操作。在操作系统补丁更新期间,我们会将数据保留在磁盘上,以便经过补丁更新的机器在重新加入 Kafka 集群后能迅速恢复正常运行状态。通过这种方式,即使服务器规模持续增长,我们也能在 30 天内完成整个机队的补丁更新。 借助 Cruise Control 等工具,我们以“机架感知”的方式进行数据复制,从而可在不影响数据可用性的前提下,将整个机架离线进行维护和网络补丁更新。
未来展望
我们队列平台的下一个发展阶段,是让平台对开发者而言更加简便,同时继续提升弹性和隔离性。我们正在投入资源开发新的抽象层,包括生产者和消费者代理,以及完全托管的连接器。这些系统将进一步使应用团队摆脱基础设施的复杂性,并让我们的平台在规模化部署时更易于采用和管理。 例如,消费者代理将支持“竞争消费者”模式,使平台能够支持更广泛的应用场景,并使处理器扩展与 Kafka 分区的扩展解耦。这对处理大量突发通知等用例非常有价值——此类用例不需要严格排序,且可以通过临时增加处理器数量来提升处理能力,而无需受限于必须达成的端到端交付目标。
Kafka 的扩展不仅仅关乎集群数量和规模的扩展。它更关乎构建工程规范、抽象层和运营模式,从而使 Roblox 能够继续我们的征程,每天以乐观和文明的态度连接十亿人。
致谢:感谢Danny Yuan和George Li对Roblox队列平台做出的重要贡献。


