拓十年匠心定制 · 商业建站与技术教学双线并行 咨询热线:400-886-1026 service@lmnt.cn
ARTICLE DETAIL

资讯详情

深耕网站建设与运营推广的一线实战洞察。

OpenAI 的 Kafka 实践看 Kafka 的云原生演进

OpenAI 的 Kafka 实践看 Kafka 的云原生演进 2025 年 6 月, 在相关的大会上, 的实时基础设施团队连续进行了两场主题分享。他们毫无保留地完整披露了内部经验。内容涉及团队如何在短短一年的时间内, 将 Kafka 的吞吐量指标提升到了原来的 20 倍之多。同时, 系统的可用性也实现了巨大跨越。该指标原本还不到 3 个 9的水平。经过优化后, 最终达到了 5 个 9的高标准。来源: at 2025更深入一层来讲, 值得我们认真剖析的, 其实是他们为了让步于其他因素, 所选择放弃的东西, 这些被放弃的因素包括排序、事务处理以及分区处理, 而这一点恰恰是 Kafka 这套系统中最为核心的语义特性。1 37 个集群、5 万连接、3 个 9 都保不住在2024年上半年, 的流处理平台已经被几乎所有产品团队采用。在数据摄入、异步处理以及服务间通信这些方面, 的后端链路上到处都是Kafka。但是, 如果要用来形容平台本身的状态的话, 用“混乱”这个词来形容并不过分。在当时, 内部存在超过 30 个独立的 kafka 集群, 这些集群大多数是由各个产品团队在不同时期临时自建的。由于各集群的配置相互之间并不兼容, 部分集群甚至运行在不同的 kafka 兼容引擎上, 导致一名新加入到团队的工程师在面对的第一个问题, 并不是关于如何正确使用 kafka, 而是他的 topic 具体位于哪一个集群。产品团队接入Kafka, 要花数天的时间, 或者是数周的时间, 这本来是几个小时就能搞定的事情。扩展性问题变得更为严峻。其外部的服务拥有大量的副本来承载流量, 使得每一个副本都单独对Kafka 集群建立连接。这之中更让人棘手的部分是: 该系统主要使用了 这种运行环境, 而由于GIL 限制的存在, 单个pod 内部必须安排多达50 个彼此独立的进程来榨取并行处理的能力, 并且每个这样的进程也都会创建属于自己的Kafka 连接。经过观察发现, 在某个集群里头, 单个节点就承受了总计五万零个并发连接的压测情况。这种情况下, JVM的内存空间直接被彻底填满, 导致数据连接被持续不断地丢弃处理。我们明确指出, 这绝非所谓的连接风暴现象, 而是在系统稳定运行状态下出现的常态过载事件。在可用性这块儿来说, Kafka 集群成了好多内部服务的单点故障。一旦发生区域性的故障, 或者是整个集群崩溃了, 就会出现面向外部的产品不得不硬停机, 或者是数据丢失的坏情况。整个平台连 3 个 9 的水平都达不到或者说不支持这个标准, 对于支撑相关的基础设施来讲, 这种结果是大家无法接受的。这些问题大家也就只能勉强忍受一下了。然而, 真正让人感到彻底无解、几乎要把人逼入死胡同的难题是, 基础设施团队那边根本就没有操作的能力。为什么会这样呢? 原因是产品服务功能和具体的 Kafka 集群之间存在着非常紧密的绑定关系, 这种关系使得它们几乎不可分割。所以, 当工作人员想要进行集群迁移操作的时候, 或者是想要对系统版本进行升级的时候, 甚至仅仅是希望对某些配置参数进行调整的时候, 都不得不去和数量庞大的产品团队进行反复协调。2 在 Kafka 之上再建一层为了打破这种耦合的状态, 我们选择了一种比较经典的架构处理方式。具体怎么做呢? 就是在客户端与Kafka集群之间插入一层代理。这样一来, 所有的服务都需要通过这层代理来进行交互。不再采用直接连接集群这种做法了。用户首先需要解决的问题, 是连接数出现爆炸式增长的情况。针对这个问题, 他们设计并构建了 Prism。Prism 是一个极为简单的 gRPC 服务, 该服务只对外开放了一个特定的端点。在操作时, 生产者会将具体的消息内容以及目标 topic 一起发送给 Prism。随后, 由 Prism 负责将这些信息准确地路由到对应的底层 Kafka 集群中去。通过这种方式, 用户完全不再需要去知晓究竟哪一个具体的集群承载了哪一个 topic, 同时也无需再手动配置用于访问集群的凭证信息以及相关的防火墙规则。他们甚至开发了一个名为的客户端库, 从而使得接入流程简化到了仅仅需要引入这个库, 然后调用单一函数的程度。在这种情况下, 单个Prism pod服务可以同时为多个客户端pod提供服务, 由此导致Kafka的直连数量出现了大幅减少的情况。连接数已经实现了收敛的处理结果, 但是集群之间的耦合情况目前仍然存在。Prism 真正表现出强大实力的核心领域在于它支持多集群的路由功能。这意味着同一个话题可以由多个 Kafka 集群来共同提供服务。Prism 的主要职责就是需要在这些不同的集群之间执行负载均衡的工作。针对某一个集群发出的发布请求出现失败的情况, Prism 能够将处理过程透明化, 并且自动尝试重新投递给另一个可用的集群。假如某个集群出现了长时间的降级现象, 熔断器机制会立刻介入并将该集群标记为不可用的状态, 进而实现自动绕过该集群的操作。我们来配合一下 Group 这个概念, 这里的 Group 指的是什么, 它就是一群包含相同 topic 的多个 Kafka 集群组成的集合, 为了让整体具有高可用性, 会将这若干个集群分别部署在不同的地方, 当 Prism 发起写入操作时, 它会去选择任意一个处于健康状态的集群来执行写入动作, 对于所有负责发送消息的生产者来说, 这些复杂的内部运作过程对他们而言是完全隐藏的, 他们根本看不到也感受不到。在 的生产者一侧已经把关联分开了, 那么在消费者这一侧也得摆脱对 Kafka 客户端直接的依赖。所以采用了 Uber 开源的框架, 并且把它按照自己的需求进行了修改, 变成内部专用的 Kafka。这是一个采用推送模式的消息消费平台: 由系统先从 Kafka里面把消息给提取出来, 然后借助 gRPC技术把消息推送到消费者的服务上面去。消费者只需暴露出处理消息用的端点就可以啦, 不用去接触那个Kafka客户端, 也不用进行那些让人头疼的管理操作, 更不需要费劲去配置那些凭证。它还把诸如重试这样的机制、死信队列那样的生产级能力都内置好了。并且, 它支持超越某个数量的并行度。这次迁移工作的过程设计得非常巧妙, 我们在新集群上面创建 topic, 同时从旧集群和新集群一起进行消费操作。通过 Prism 平台逐步将写操作的流量切换到新集群上面。等旧集群里面的数据过期之后就把旧集群下线了。单次迁移工作大概花费30分钟左右就完成了流量的切换, 这一点对用户来说是完全透明的。最终的结果如下:3 代价为了可用性 放弃了什么这位工程师在演讲的过程里, 特别老实不客气地承认了一个情况: 这个架构的设计, 要求他们必须把Kafka里面的某些核心功能定义给全部丢掉。这根本不是去挑选一些边缘化的功能。排序能力、事务机制、分区策略, 这些要点, 它们是 Kafka 跟普通的消息队列不一样的地方, 是它的核心本事, 也是很多流处理场景在下面做计算时所默认存在的前提条件。它所秉持的那一套想法, 说白了就是 be , be , 这种思路让那些极少数确实需要用到前面说的那些核心能力的特定情况, 还保留着直接去连接 Kafka 的一条退路, 但与此同时呢, 绝大多数的使用情况都被诱导着去使用代理这一层了。那位工程师讲过, 凭经验来看, 使用者确实不怎么在乎这些约束条件, 而使用率因为流程变得简化了就一下子快起来了。这种现象在特定的背景下可能没问题, 因为他们用的 Kafka 主要是用来做异步处理和接收入手数据的, 对于需要保证顺序和事务操作的需求, 其实并不是特别高。但是对于更广泛的Kafka用户群体来说, 这个trade-off暴露了一个根本性的问题, 如果是为了获得云级别的弹性和可用性, 就必须以放弃核心语义为代价, 那就说明问题的所在不是应用层的trade-off决策, 而是Kafka引擎本身。4 绕行方案背后的根因利用代理层来规避那些问题, 它们的根本原因都是一样的。这就像是让系统和计算、还有存储这两样东西混在一起管起来, 使得内部状态和具体的服务节点被紧紧地捆绑在了一起。如果这个关键的根源在数据库引擎这一层的开发过程中就被彻底解决了。让数据库架构变成不需要保存自身状态的无状态模式。然后确保数据会被安全地持久化保存在一个所有节点共享的对象存储系统里。那么上面提到的那种用来绕行的技术手段就完全没有了存在的必要前提条件了。咱们现在回到那个有5万连接把JVM给打满了的场景里去。他们之所以要用Prism手段去把连接数收敛起来, 这本质上的原因就在于他们没法随随便便就去增加东西, 每一次去增加一个组成部分的时候, 都得去做数据搬迁这一档子事, 要把数量十分庞大的分区副本给搬来搬去, 整个过程既缓慢得要命, 又会对正在在线运行的流量造成不好的影响。假设说要是那部分组件是无状态的话, 那么要进行扩容就变得很简单了, 无非也就是加一个计算节点出来就行了, 这样以来连接的容量就会随着节点数量的增多而跟着变成倍数增长, 甚至都还能用上HPA这个玩意儿来实现自动伸缩的效果。扩容这件事儿的处理方式也差不多, 大家宁愿选择先加个新的集群来搞水平扩展, 也不愿意直接给现有的老集群加东西, 之所以这么干, 就是觉得往后者的方案风险实在太高了点, 一旦数据压根就没存在本地, 而是跑到了共享存储里去晃悠的时候, 那种情况下的迁移操作也就变成了一场纯粹的元数据游戏, 说白了也就是更新一下到底哪个服务在管这个数据的映射关系就完事儿了, 一个字节的实际数据都不带搬运的, 这样一来导致系统变弹性的那些麻烦根源问题, 自然就从根儿上彻底消失得无影无踪了。我们再来看那部分花费了最大工程量的内容, 也就是多集群 HA Group、Prism 熔断器以及跨集群重试, 这一整套故障转移体系。传统的 Kafka 多副本复制提供了某级别的容错能力, 但是面对区域故障或者整个集群不可用的场景, 副本复制就无能为力了, 原因就是因为那些副本全都位于同一个集群内部。假如数据会被直接写入到比如 S3 这样的对象存储里面去, 它的持久性就天然地具备多可用性域的多副本保护功能。大家要知道, S3 这种服务是靠纠删码技术来提供高达十一下九的持久性保证的, 也就是说, 当发生故障的时候, 任何一个还活着存活的节点都完全有能力去接管那些分区的管理工作, 并且能够在几秒钟的内部把整个服务体系重新恢复正常运行状态。其实呢, 关于多可用区域的纠删码保护机制, S3 这个存储平台自身早就已经把这些事情都给做得妥妥的了, 但是有些人在应用开发的这一层却又重复再去做一遍同样的工作, 这显得有些多余。当单集群能够通过弹性伸缩的手段来应对流量的变化, 同时利用对象存储的技术来保证跨可用区的持久性数据时, 之前需要去维护37个集群的那个前提条件就不存在了, 随着这个前提的缺失, 集群的数量会自然地实现收敛, 那些之前存在的瓶颈也会随之消失, 存算分离的这种架构天生就非常适合KRaft使用, 所以就不需要再引入外部的协调组件了。最关键的一点是, 之所以要放弃排序功能、-once 参数以及分区处理方法, 根本原因在于多集群路由行为本身破坏了从 key 到目标路径之间的那种映射关系。鉴于在存算分离这种架构下面对单个集群的时候它就能够提供充足的弹性空间并且保障了可用性, 那么也就不再需要依赖多集群路由这一前提条件了。既然那个前提不需要存在, 自然也就没有理由去放弃之前提到的那些语义定义了。所谓的百分之百的协议兼容, 指的是所有现存的客户端以及自身, 都能够实现无缝地使用, 整个过程根本不需要去改动哪怕一行的代码。就是沿着这同一个方向去构建的那个存算分离的实现方案。并且把那些痛点都放在一起了, 还有他们的代理层方案也放进去了, 同时还有引擎层的那个直接解法也都一起放在一起了:把数据写入到 S3 这个地方, 当然不是一点代价都没有的。S3 的对象存储 API 调用延迟比本地磁盘要高一些, 尾延迟, 也就是 p99 或者 p999 这些指标, 需要额外的优化措施在高频小批量写入的场景下, S3 API 调用本身的成本也是不能忽略的。这是一个完全不同的工程权衡点, 不是什么银弹。从存储成本的这个角度来看, 传统 Kafka 的做法是三副本复制, 然后还要叠加 EBSBlock Store本身自带的冗余复制, 这样实际产生的存储冗余度是非常高的。这种高水平的冗余度是远高于对象存储所采用的纠删码方案的存算分离架构就不一样了, 它把存储和计算之间的副本复制给去掉了。因为数据是直接写入 S3 的, 所以是由对象存储自身的纠删码机制来保证数据的持久性。这样的操作方式会让存储成本出现明显的降低局面, 有关具体的成本对比数据, 你可以去查看官方给出的材料。5 路标与终点在二零二五年六月的那个问答环节当中, 有相关的人询问了工程师对于卡夫卡的看法, 对方的回答是处于非常积极地思考和探索的状态, 因为你为了绕过传统的卡夫卡的限制已经付出了非常大的工程方面的代价, 所以引擎层面的根本性的演进自然也就是最具有吸引力的下一步。结果清楚地表明了, 哪怕是舍弃掉对于顺序排列的处理以及事务性功能, Kafka 依然能够在极大规模的运作场景里维持正常的运行状态。然而, 这种处理方式所付出的牺牲, 它本身就已经构成了一个最为有力的佐证信息: 底层引擎技术的升级与改进, 这已经不再是一个可供随意选择的备选方案了。对于那些目前正在着手设计并规划未来新一代流数据处理平台的基础设施工程团队的成员们来说, 他们所需要面对的核心决策问题, 仅仅只是在当下的时刻就直接在基础引擎层面上去解决这些挑战好, 还是说, 应该选择先搭建一层专门的代理服务结构来作为过渡阶段的一个缓冲环节。假如说, 你此刻正面临着多种多样的 Kafka 方面的种种难题与烦恼, 那么, 你就可以立刻前往官方网站去体验一下, 也或者进行尝试看看我们的产品。今日好文推荐
返回列表