写点什么

重大技术转向:LinkedIn 抛弃自家创造的 Kafka ,又重新造了个 Pulsar 的轮子?

作者:AscentStream
  • 2025-08-05
    上海
  • 本文字数:7977 字

    阅读完需:约 26 分钟

重大技术转向:LinkedIn 抛弃自家创造的 Kafka ,又重新造了个 Pulsar 的轮子?

导读

在消息流领域,一场技术演进正在悄然发生:LinkedIn,作为 Kafka 的创造者和消息队列技术革命的引领者,在今天,几乎完全放弃 Kafka,转而构建一个新的轮子:Northguard。这一转变标志着 LinkedIn 技术战略的重要调整


值得注意的是,LinkedIn 打造的这个新系统在架构理念上与 Apache Pulsar 展现出令人惊讶的相似性。存储计算分离、多层数据模型、存储条带化等 Northguard 的核心特性,与 Pulsar 早已实现的设计理念高度重合。


这着实令人又惊又喜:惊讶的是 LinkedIn 在经历了 Kafka 的挑战后,重新设计的系统竟与 Apache Pulsar 的核心架构理念如此相似;欣喜的是这再次证明了 Pulsar 的设计方向是正确的、极具前瞻性的。


让我们一起深入了解这场技术转向背后的故事,看看 LinkedIn 是如何从 Kafka 的创造者变成了与 Pulsar 架构趋同的新系统开发者,以及为什么这一切对正在选择消息系统的你至关重要。


Northguard :新一代可扩展 log 存储系统



数据是 LinkedIn 数千种服务的核心。各个服务之间还需要订阅并获取其他服务发布的数据,这些订阅者需要处理来自原始服务或发布者的所有历史数据,而非仅仅是最新更新。订阅者还可能会出现错误,因此这些服务最好能够重新处理数据,以便修复错误、重新处理数据并验证服务是否正常运行。


为解决这一需求,LinkedIn 团队在 15 年前开发了 Kafka,一个为发布者和订阅者设计的集中式管道。Kafka 解决了分布式系统中的常见问题,如以一致、可重放和容错的方式存储大量数据。它已成为基础设施的支柱,不仅支持用户活动事件,还支持日志、度量、跟踪、应用间消息传递、近实时应用、流处理、数据湖导入/导出、人工智能功能,甚至数据库复制。这种有序的数据管道被称为日志,而将数据生产者与消费者分离的模式则被称为 Pub/Sub(发布/订阅)模式。


然而,随着 LinkedIn 的快速发展和用例需求的不断提高,Kafka 的扩展和运维变得越来越具有挑战性。这正是团队转向 Northguard(一种具有更高可扩展性和可运维性的日志存储系统)的原因。


为什么需要新的解决方案

2010 年,LinkedIn 拥有 9000 万会员。如今,这一数字已增长至超过 12 亿。这种显著增长给业务带来了巨大挑战,特别是在跟上 Kafka 用例数量、容量和复杂性快速增长方面。Kafka 系统规模庞大:每天处理超过 32 万亿条记录,产生 17 PB 数据,分布在 40 万个主题上,运行在 150 个集群的 1 万多台机器上。业务面临的主要挑战包括:

  • 可扩展性 - 随着用例增加,不仅流量增大,元数据也随之增多,需要更多机器支持。元数据管理和集群规模的瓶颈日益严重,不得不持续增加集群数量。

  • 可运维性 - 流量增长带来负载均衡挑战,现有的 100 多个集群需要一个完整的服务生态系统进行管理。

  • 可用性 - 受限于分区作为复制单位的重量级特性。

  • 一致性 - 由于分区作为复制单位对可用性的影响,常常需要牺牲可用性来保证一致性。

  • 持久性 - 现有系统提供的保证相对较弱,无法满足关键应用的需求。


业务需要一个全方位高性能的系统,无论在数据层面还是在元数据和集群规模方面都应当具备出色的扩展能力。同时,无论规模大小,都能通过精心设计和快速部署支持均衡负载分布的无缝运维。此外,系统还需要提供强一致性的数据和元数据管理,同时兼具高吞吐量、低延迟、高可用性、高持久性、低成本、兼容多种硬件类型,并具备可插拔性和可测试性。


介绍 Northguard

Northguard 是一款专注于可扩展性和易操作性的日志存储系统。为实现高扩展性,它采用了数据和元数据分片技术,维持最小化的全局状态,并利用分散式的集群成员协议。其卓越的可操作性主要得益于日志条带化技术,这种设计能够自动将负载均匀分布到整个集群中。


在实际运行时,Northguard 以 Broker 集群的形式存在,这些 broker 仅与连接到它们的客户端以及集群内部的其他 Broker 进行交互,形成一个高效的封闭系统。


接下来,让我们深入探讨支撑 Northguard 强大功能的核心要素:数据模型、元数据模型以及底层支撑协议。


数据模型

在 Northguard 系统中,客户端生成和消费消息,称之为 Record 记录,这些记录是数据读写操作的最小单位。如图 1 所示,每条记录由键、值和用户自定义的标题三部分组成,它们本质上都是字节序列。

图 1. 一条单独的记录(record)


Segment(图 2)是一系列有序记录的集合,也是复制操作的基本单位。Segment 分为活动状态和密封状态两种:活动状态的 Segment 可以继续添加新记录,而密封状态的则不可修改。每条记录在 Segment 中都有一个相对于起始位置的逻辑偏移量。当出现复制失败、Segment 大小达到 1GB 限制,或者 Segment 活动时间超过一小时时,系统会自动将其密封。


图 2.包含多条记录的 Segment


Range(图 3)是 Northguard 的日志抽象概念,它由与密钥空间(keyspace)的连续区间相关联的一系列 Segment 组成。Range 可以处于活动状态或密封状态。活动状态的 Range 可能不包含任何 Segment,也可能仅包含已密封的 Segment,或者最近的一个 Segment 处于活动状态。而密封状态的 Range 可能不包含任何 Segment,也可能只包含已密封的 Segment,但绝不能包含活动状态的 Segment。


图 3.包含三个 Segment 的一个 Ranger


Topic(图 4)是一个已命名的 Range 集合,它们共同覆盖整个密钥空间。一个 Topic 中的 Range 可以进行拆分或合并操作。当拆分一个 Range 时,该 Range 会被封闭,同时创建两个全新的 Range。当合并两个 Range 时,这两个 Range 会被封闭,并创建一个新的子 Range。值得注意的是,一个 Range 只能与其唯一的"伙伴 Range"进行合并,这一机制与伙伴内存分配器(**buddy memory allocator)**的工作原理完全一致。此外,Topic 可以被封存或删除:封存一个 Topic 会导致其所有 Range 被封存,而删除一个 Topic 则会删除其所有 Range。


图 4. 展示了一个 Topic 中的多个 Range 经过拆分和合并的情况


每个 Topic 都配置有一个存储策略,该策略由集群管理员提供。存储策略包含一个名称、一个定义 Segment 删除时机的保留期以及一组约束条件。这些约束条件通过表达式定义了哪些 Broker 可以被选为 Segment 副本,以及需要多少个副本。表达式基于绑定到 Broker 的键值对(称为属性),这些属性由管理员分配给 Broker 进程。策略和属性构成了一个强大的抽象层。例如,Northguard 本身并不直接识别机架或数据中心等物理结构,LinkedIn 的管理员只需将这些信息编码到部署在 Broker 上的策略和属性中,从而使这一机制成为支持机架感知复制分配的通用解决方案。团队还利用策略和属性来分配副本,这种方式能够在固定时间内安全地向集群部署构建和配置,而不会受到集群规模的限制。


日志条带化

复制单位的粒度越粗,越需要关注集群中的资源倾斜问题。平衡资源分布是一项挑战,甚至可能需要依赖外部系统(如 LinkedIn 的 Cruise Control)来均衡整个集群的资源分配。当复制单位与分区同等粗粒度时,每个副本都必须存储整个分区日志的完整副本,这会引发一系列资源分布不均的问题:


  1. 当某些 Broker 比其他 Broker 承载更多日志时,会出现资源倾斜。新加入集群的 Broker 将处于闲置状态,直到有新日志分配给它或现有日志迁移至此。由于日志创建频率低,且迁移现有日志会带来运维挑战,这种情况难以快速改善。

  2. 如果某个 Broker 不幸承载了过多资源密集型日志,同样会导致资源分布不均。


Northguard 通过实施日志条带化(log striping)技术来避免这些问题,即将日志分割成较小的块以平衡 IO 负载。与完整日志相比,这些小块各自拥有独立的副本集。在 Northguard 中,Range 和 Segment 分别对应于日志和块的概念。由于 Segment 的创建频率较高,团队无需将现有 Segment 迁移到新加入的 Broker 上。新的 Broker 会自然地开始接收并存储新 Segment 的副本。这种设计还意味着,即使某些不理想的 Segment 组合恰好分配给了同一个 Broker,也不会造成长期问题,因为随着新 Segment 被创建并分配给其他 Broker,系统会逐渐自我平衡。整个集群能够实现自动负载均衡。


图 5. 新增 Broker 5 的集群示意图


图 6. 一个新的 Segment 被添加到 Range 并分配给 Broker 5


Range 分区 vs 索引分区

在考虑如何扩展这些条带化日志主题的吞吐量时,团队有以下目标:

  1. 确保客户端能够正确放置记录

  2. 最小化对不相关日志的干扰

  3. 维持一定程度的排序保证

  4. 便于流处理框架避免数据洗牌操作


Range 完美满足了这些要求。


另一种常见实现是索引分区,但索引分区需要"全局暂停"的同步机制,才能让客户端继续将记录发送到正确的日志,而 Range 分区只会中断向被分割 Range 发送记录的客户端。Range 分割作为同步障碍,要求客户端在继续生产前必须响应主题的变更。


此外,Range 还提供了优秀的排序保证,即使在 Range 拆分和合并操作中也能保持总体顺序:


  • 当 Range R1 被拆分为 R2 和 R3 时:

  • R1 中的所有记录均发生在 R2 中任何记录之前

  • R1 中的所有记录均发生在 R3 中任何记录之前

  • 当 Range R2 和 R3 合并为 R4 时:

  • R2 中的所有记录均发生在 R4 中任何记录之前

  • R3 中的所有记录均发生在 R4 中任何记录之前

在流处理作业中,连接多个数据流是常见需求。为了高效执行这类连接操作,系统必须将共享同一连接键的记录集中处理。当利用日志存储系统的键分区功能时,如果所有相关数据流采用一致的连接键分区策略,这一过程可以顺利进行。但当面对分区不一致的情况(例如一个数据流包含 10 个分区,而另一个包含 16 个分区),流处理任务往往不得不引入资源密集型的数据洗牌环节来重新分配记录。


Northguard 的 Range 机制为此提供了更为优雅的解决方案。由于 Northguard 中不同 Topic 的伙伴式 Range 在设计上保持了本质一致性,这实现了对数据洗牌这一昂贵操作的完全规避。


元数据模型

Northguard 拥有用于管理 Topic、Range 和 Segment 的元数据系统。


一个集群包含一个或多个 vnode,每个 vnode 负责存储集群元数据的一个分片。vnode 是基于 Raft 协议实现的容错复制状态机,构成了 Northguard 分布式元数据存储和元数据管理的核心组件。


图 7 vnode 的 Raft 组


协调器(coordinator)是特定 vnode 的主节点,负责管理该 vnode 拥有的所有元数据,实现元数据的"业务逻辑"。当 vnode 的状态机选出新的主节点时,协调器角色会随之转移。协调器在 vnode 状态机中持久保存状态,确保新当选的协调器能够无缝接续前任的工作。


对于 vnode 所拥有的 Topic,协调器负责跟踪其变化,包括封存或删除 Topic,以及拆分或合并该 Topic 的 Range。对于归属于 vnode 的 Range,协调器会监控其活动/封存/删除状态、创建时间、保留时间和 Topic 名称等元数据。同时,协调器还维护 Segment 的元数据,如 Segment 的副本集、Segment 的激活/封存/重新分配状态、Segment 的起始偏移和长度以及创建时间和封存时间。通过这些 Segment 状态信息,协调器能够为复制不足的 Segment 启动密封 Segment 复制,从而实现 Northguard 的自我修复能力。


动态分片复制状态机(DS-RSM)由覆盖整个散列环的虚拟节点集合组成。元数据通过一致性散列方式分布在各节点间:Topic 元数据按 Topic 名称散列,而 Range 和 Segment 元数据则按 Range ID 散列。这种设计有效减少了元数据热点问题。


图 8. 由 3 个虚拟节点组成的 DS-RSM


集群管理员可以通过提供元数据策略来配置集群。每个元数据策略包含一个名称和一个或多个约束条件。这些约束条件的工作原理与存储策略中的约束完全相同,其表达式基于管理员为 Broker 设置的属性键值对。元数据策略主要用于定义如何选择虚拟节点的副本。


集群状态与成员管理

Northguard 采用 SWIM 协议作为其可扩展的群组成员管理机制。SWIM 通过随机探测方式进行故障检测,同时利用病毒式传播方式处理成员变更和信息广播。团队利用这种广播机制发布精简的全局集群状态信息,包括基本的主机信息、端口配置和集群中 Broker 的属性,以及 DS-RSM 哈希环的关键信息,如每个 vnode 的哈希边界范围、vnode 的领导者信息、当前任期和复制状态等。这些信息有效地帮助系统将请求准确路由至相应的 vnode 领导者。


图 9.运行中的 SWIM 协议


Northguard 的元数据协议采用一元式设计:每个请求对应一个响应。例如 CreateTopicRequest、DeleteTopicRequest、TopicMetadataRequest 和 SegmentMetadataRequest 等。客户端可以将请求发送给集群中任意一个充当代理的 Broker。接收请求的 Broker 会利用本地缓存的 gossip 全局状态副本来确定哪个 vnode 负责处理该请求,然后将请求转发给相应 vnode 的领导者。响应信息则沿原路径返回给客户端。


与一元式的元数据协议不同,Northguard 的生产、消费和复制协议都采用会话化流设计。通过将状态绑定到流中,团队有效降低了协议开销。这些协议利用流水线技术保持数据的持续传输,并通过窗口机制控制任意时刻可以在流水线中传输的数据量。


以生产流为例:生产客户端首先生成一个流 ID,并与活动 Segment 的领导者进行握手,获取 Broker 设定的初始窗口大小。只要记录数量未超出窗口限制,生产者就可以向 Broker 持续发送多个 Append 请求。每个 Append 请求包含流 ID、序列号以及一条或多条记录。Broker 可以对 N 个 Append 请求回复 M 个确认(Ack),但只会为已提交的记录向生产者发送确认。这些确认信息包含与 Append 序列号相关的确认编号,以及更新后的窗口大小,用于接收更多的 Append 请求。


图 10. 生产者通过生产流发送记录并获取确认


消费流与生产流的原理十分相似,但数据流向恰好相反,且窗口大小由客户端决定。完成握手后,消费者会发送 Reads 请求,告知 Broker 它们的流处理进度以及可能更新的窗口大小。只要推送的记录数量不超过窗口限制,Broker 就会持续发送"Push"响应。


图 11. 消费者通过消费流接收记录并发送请求获取更多记录


主动 Segment 复制的工作原理与生产流类似,区别在于它使用记录偏移量而非序列号。ReplicaAppends 还包含已提交状态信息,使追随者能够准确跟踪已提交内容的进度。


图 12. 活动 Segment 的追随者通过复制流接收记录并发送请求获取更多记录


封闭 Segment 的复制机制可以为复制不足的 Segment 提供补充,本质上是一种消费协议,只不过是在两个 Broker 之间进行的。


Segment 存储

Northguard 中的 Segment 存储采用了可插拔设计,其主要实现被称为"fps 存储"。该存储具有预写日志(WAL)机制,按 Segment 创建文件,使用直接 I/O,并在 RocksDB 中维护稀疏索引。数据追加操作会在批次中累积,直到满足特定条件:时间达到足够长度(如 10 毫秒)、批次大小超过配置阈值或追加次数超过配置限制。当批次准备好刷新时,存储系统会同步写入 WAL,将记录追加到一个或多个 Segment 文件中,同步这些文件并更新索引。


通过采用直接 I/O,Northguard 有效避免了双重缓冲问题,转而使用应用级缓存,并利用系统对已建立消费流的理解来智能填充缓存。直接 I/O 还能在 fsync 发生故障时保持状态一致性,从而增强 Northguard 的持久性。这种设计还有助于避免页面缓存中出现的缓存降级问题,这类问题通常会导致客户端无法从副本中消费数据,或者消费者或封闭 Segment 复制机制尝试消费旧 Segment 时遇到困难。


除了实施数千个测试用例、微基准测试和严格的认证流程外,团队还在确定性模拟环境下运行 Northguard,也即在单线程环境中运行集群和客户端,并将非确定性组件替换为确定性组件。团队每天在各种场景下模拟多年的运行活动,这些场景中会注入多种故障类型:

  • Broker 关闭

  • 滚动重启

  • 网络分区

  • 数据包丢失

  • 数据包损坏

  • 磁盘损坏

  • 磁盘 I/O 错误

  • 配置部署


通过这种方法能够方便地共享、重放和逐步检查失败的运行情况,在问题出现在生产环境之前就能及时发现并解决它们。


要点回顾


深度对比:Northguard vs. Apache Pulsar

LinkedIn 在解决 Kafka 扩展性和可操作性问题时,实际上采取了与 Apache Pulsar 非常相似的架构设计理念。接下来我们将尝试分析 LinkedIn 的新技术栈与 Apache Pulsar 的异同,并探讨为什么 LinkedIn 实质上是在重新发明 Apache Pulsar 的轮子


存储层设计:惊人的相似性

Northguard 的核心设计与 Apache Pulsar 的分层架构有着惊人的相似之处:

  • 分离的计算和存储层:LinkedIn 的 Northguard 将存储层与服务层分离,这正是 Apache Pulsar 的核心设计理念。Pulsar 从 2016 年就采用了这种架构,将 broker 层与 BookKeeper 存储层分离,实现了更高的可扩展性和更灵活的扩展能力。

  • 以分段为单位的存储:Northguard 采用分段作为复制和存储的基本单位,这与 Pulsar 中 BookKeeper 的 ledger 概念几乎完全一致。这使得系统可以更高效地处理存储和复制,提高了整体性能和可靠性。

  • 存储分片:LinkedIn 提到的"与本地存储分离的多存储层"架构,在 Apache Pulsar 中已经通过 BookKeeper 的分布式存储实现了。Pulsar 的每个 topic 可以分布在多个 BookKeeper 实例上,提供了极高的横向扩展能力。


性能与可靠性:Pulsar 的强势优势

LinkedIn 团队阐述了 Northguard 的几项改进,但这些在 Apache Pulsar 中早已实现:

    

云原生和开箱即用:Pulsar 的云原生优势

尽管 Northguard 解决了 Kafka 的许多架构限制,但 Apache Pulsar 在云原生方面仍然具有明显优势:

  • 天然的多租户支持:Pulsar 从设计之初就考虑了多租户场景,通过命名空间和严格的资源隔离,提供了真正的云原生多租户体验。

  • Kubernetes 原生集成:Pulsar 提供了完善的 Kubernetes helm 部署等,支持在云环境中的自动化部署、扩展和管理。

  • 存储层的弹性扩展:Pulsar 的 BookKeeper 存储层可以独立于 broker 层扩展,使得系统在云环境中能够更灵活地适应不同的工作负载。


为何 LinkedIn 不直接采用 Apache Pulsar?

从技术角度看,LinkedIn 基本上是在重新发明 Apache Pulsar 的轮子。那么,为什么 LinkedIn 不直接采用已经成熟的 Apache Pulsar,而是要重新开发 Northguard?可能的原因包括:

  • 历史包袱:作为 Kafka 的发源地,LinkedIn 可能有大量特定于 Kafka 的工具和集成。

  • 内部需求:LinkedIn 可能有一些特定于其环境的需求,但这些需求本可以通过 Pulsar 的插件机制或贡献代码到 Pulsar 社区来解决。

  • NIH 综合症:不使用外部解决方案的偏好(Not Invented Here),这在大型技术公司中较为常见。


然而,LinkedIn 的这一举动实际上证明了 Apache Pulsar 架构的正确性。LinkedIn 面临的问题——Kafka 的扩展性和可操作性挑战——正是 Pulsar 在设计之初就试图解决的问题。


完整对比:Kafka, Northguard & Pulsar


结论:Pulsar 的架构理念获得验证

LinkedIn 的 Northguard 实际上是对 Apache Pulsar 架构设计的一种间接认可。Pulsar 早在 2016 年就采用了分离计算与存储的架构,实现了更高的可扩展性、更好的可靠性和更灵活的部署选项,成为云原生时代消息流系统的首选。


对于企业来说,选择 Apache Pulsar 提供了以下优势:

  • 经过验证的架构:Pulsar 的架构已在 Yahoo!、Tencent、Verizon Media 等大型企业中得到大量验证。

  • 云原生设计:Pulsar 天生适合云环境,支持 Kubernetes 部署和弹性扩展。

  • 丰富的功能集:Pulsar 不仅提供消息队列功能,还包括流处理、事务支持等高级特性。

  • 多协议支持:原生支持 Kafka、AMQP 等多种协议,简化了迁移过程。

  • 成熟的开源生态系统:Pulsar 拥有活跃的社区和广泛的商业支持。


LinkedIn 开发 Northguard 的选择,实际上是对 Apache Pulsar 架构理念的最好验证,进一步证明了 Apache Pulsar 在消息和流处理领域的前瞻性设计。对于其他面临类似挑战的企业,Apache Pulsar 提供了一条已经铺好的道路,无需重新发明轮子。


针对 Linkedin 提到的具体挑战,Pulsar 是如何解决的呢?社区后续将从扩展性与存储架构、存储条带化设计和计算与存储分离等多个角度来讨论

立即关注 Apache Pulsar 公众号,获取独家深度解析!无论你是技术爱好者、架构师,还是面临消息系统选型决策的工程师,我们将为你揭秘 Pulsar 内部机制,与你一同探索消息系统的未来方向。


参考资料:

https://www.linkedin.com/blog/engineering/infrastructure/introducing-northguard-and-xinfra

发布于: 刚刚阅读数: 5
用户头像

AscentStream

关注

还未添加个人签名 2017-10-19 加入

还未添加个人简介

评论

发布
暂无评论
重大技术转向:LinkedIn 抛弃自家创造的 Kafka ,又重新造了个 Pulsar 的轮子?_kafka_AscentStream_InfoQ写作社区