直播回顾 | 云原生消息队列架构破局:Pulsar 存算分离深度解析

本文整理自周鹏 11 月 18 日的线上分享,一起来从架构师视角看 Pulsar 架构设计!
架构全景与优势
传统 MQ 的困境与破局之道
在消息中间件的发展历程中,传统消息队列架构逐渐暴露出诸多瓶颈。以 Kafka 为代表的一体式 Broker 架构,其计算与存储紧密耦合的设计,在云原生时代面临着前所未有的挑战。
架构耦合问题已成为传统消息队列的桎梏。在 Kafka 这样的系统中,计算层与存储层深度融合,导致扩缩容、故障迁移、数据均衡等运维操作异常复杂。当需要进行容量调整或负载重新分布时,Kafka 必须进行整个分区的迁移,这一过程不仅耗时耗力,还可能在迁移期间影响系统可用性。运维团队常常需要在深夜低峰期执行此类操作,即便如此,也无法完全避免对业务的影响。
弹性限制是另一个显著问题。由于数据本地存储的特性,传统消息队列的节点故障恢复时间往往长达数小时。在金融交易、实时推荐等对可用性要求极高的场景中,这样的恢复时间是业务方完全无法接受的。节点故障不仅意味着部分服务的不可用,还可能导致整个集群的性能下降,形成连锁反应。
相比之下,Pulsar 采用了一种全新的思路。迁移粒度差异体现了架构设计的先进性,Pulsar 采用更细粒度的分片流机制,迁移时无需整体数据搬迁,大大提升了系统的灵活性。这种设计使得 Pulsar 能够实现无状态优势,Broker 的无状态设计让其横向扩展能力显著增强,更好地适应了云原生环境对弹性和敏捷性的要求。
存储计算分离带来的四大收益
存储计算分离架构并非简单的概念创新,而是为解决实际问题而生的系统性解决方案,它为用户带来了四个核心价值。
动态负载均衡是存储计算分离最直接的优势。Pulsar 支持分区秒级迁移,无需数据拷贝,这在多租户、流量波动剧烈的场景中表现出色。对比传统 Kafka 迁移需要完整数据复制,耗时可能达 1-2 小时的窘境,Pulsar 实现了真正的弹性伸缩。在实际生产环境中,这意味着业务团队可以根据流量预测或突发营销活动,实时调整集群资源,而无需担心数据迁移带来的延迟和风险。
快速故障恢复能力在关键时刻体现价值。健康 Broker 可快速接管异常 Broker,无需副本追赶,RTO(恢复时间目标)可降至秒级。这种快速恢复机制对于保障业务连续性至关重要。想象一下,在双十一大促或春节红包等高压场景下,单个节点故障能够在秒级内自动恢复,而不是等待数小时的人工干预,这对技术团队的信心的业务稳定性的保障都是质的飞跃。
独立扩缩容解决了资源利用率的痛点。计算资源不足时单独扩展 Broker,存储不足时单独扩展 Bookie,两者互不影响。这种解耦使得资源规划更加精确,成本控制更加精细。企业不再需要为了应对计算峰值而过度配置存储资源,也不必因为存储需求增长而被迫升级计算节点,实现了真正意义上的资源优化。
高性能保障通过多项技术协同实现。Entry 并行写、多副本机制、读写分离缓存等技术共同构建了高性能存储体系,消息落库效率显著提升。在实际测试中,Pulsar 在保证强一致性的前提下,仍然能够实现数百万级的 TPS,满足了绝大多数互联网业务对消息吞吐量的要求。
Pulsar 整体集群架构
Pulsar 的整体架构体现了现代分布式系统的设计理念,各组件职责清晰,协同高效。
核心组件
构成了 Pulsar 的处理骨架。生产者(Producer)和消费者(Consumer)作为客户端与无状态 Broker 集群进行通信,Broker 仅负责路由和协议转换,这种轻量级设计使得 Broker 可以快速启动和销毁,完美契合容器化部署模式。在微服务架构中,这种无状态特性让服务编排和调度变得异常简单。
存储分离
架构的核心创新。通过 BookKeeper 客户端与 Bookie 节点交互,所有数据持久化存储在 Bookie 集群,Broker 完全不保留状态数据。这种设计使得 Broker 真正实现了无状态化,任何一个 Broker 实例都可以处理任何客户端的请求,为负载均衡和故障转移提供了基础保障。
元数据管理
经历了持续的演进。早期版本依赖 ZooKeeper,现在支持多种元数据存储方案,仅保存分区映射等指针信息,不存储消息本体。这种元数据轻量化设计降低了协调服务的压力,提高了系统的整体稳定性。在现代云原生环境中,这种设计也使得 Pulsar 能够更容易地与各种基础设施集成。
Pulsar 整体交互
消息在 Pulsar 集群中的流动路径体现了架构的高效性。
消息路径设计简洁而高效。客户端直接与 Broker 交互,Broker 作为 BookKeeper 客户端将消息写入 Bookie 集群的多副本存储。这种直接的数据路径减少了不必要的网络跳转,降低了延迟。在实际部署中,这种设计使得运维团队可以针对 Broker 和 Bookie 的不同特点进行独立的网络优化和硬件选型。
协议效率通过二进制协议保障。Broker 与 Bookie 之间采用二进制协议进行通信,比文本协议更高效,这在海量消息场景中带来的性能提升尤为明显。二进制协议不仅减少了序列化和反序列化的开销,还降低了网络带宽的占用,对于跨数据中心部署的场景具有重要意义。
副本机制在保证可靠性的同时兼顾性能。虽然类似 Kafka 的 Topic Partition 副本机制,但 Pulsar 在存储层实现了更彻底的解耦。这种设计使得副本的分布更加灵活,可以根据机架、可用区等物理拓扑进行优化部署,在保证数据安全的同时最大化利用硬件资源。
Broker 无状态架构深度解析
Broker 的无状态设计是 Pulsar 架构的精髓之一,它通过精巧的机制实现了服务层的高度弹性。
动态负载调整基于哈希环机制实现,类似 Redis 集群的数据分布策略。Namespace Band 内包含多个 Topic 的哈希段,这种分段管理方式既保证了数据的均匀分布,又避免了单一 Topic 过大导致的"热点"问题。在实际运行中,LoadManager 持续监控各 Broker 的负载情况,当检测到不均衡时自动触发重新分配。
迁移机制的自动化程度很高。当 Broker 负载过高时,系统自动将部分分区迁移到低负载节点,相关元数据保存在 ZK/etcd 等协调服务中。这种自动化运维能力大大降低了人工干预的需求,特别是在大规模集群中,人工监控和迁移几乎是不现实的。
写入流程采用三阶段设计。Client→Broker→Bookie 的写入路径清晰明确,通过 AddEntry 操作与 Bookie 交互,写入完成后回调 publishComplete 确认。这种异步确认机制既保证了写入的可靠性,又不会阻塞后续操作,实现了高吞吐与数据安全的平衡。
扩展优势在实际运维中体现明显。无状态设计使得 Broker 增减不影响数据分布,新 Broker 可立即参与服务而无需数据预热。在弹性伸缩场景中,这一特性让 Pulsar 能够快速响应流量变化,在业务高峰时快速扩容,在流量低谷时及时缩容以节约成本。
BookKeeper 存储层原理剖析
BookKeeper 核心概念与数据模型
BookKeeper 作为 Pulsar 的存储引擎,其架构设计直接决定了整个系统的性能和可靠性。
存储架构采用无中心节点的集群设计。由多个 Bookie 组成的对等集群避免了单点瓶颈,每个 Bookie 都是独立的存储节点,这种去中心化设计提高了系统的可用性和扩展性。在实际部署中,Bookie 集群可以独立于 Broker 进行扩展和维护,为系统演进提供了灵活性。
Ledger 结构是 BookKeeper 的基本存储单元。由一系列顺序写入且不可修改的 Entry 组成,类似于 Kafka 中的消息。这种不可变性设计简化了并发控制,提高了读写效率。Ledger 的抽象使得 BookKeeper 能够同时支持消息队列和流处理两种场景,为 Pulsar 的多模式消息处理能力奠定了基础。
Entry 组成包含完整的元信息。LedgerID、EntryID、LAC (最后确认位点)和校验码共同保证了数据的完整性和一致性。其中 LAC 用于保证读一致性,Journal 机制类似于数据库中的 WAL(Write-Ahead Logging),确保了即使在故障恢复时也不会出现数据不一致的情况。
三参数控制提供了灵活的可靠性配置。Ensemble(参与一个 Ledger 写入的 Bookie 总数)、WQ (Write Quorum,总副本数)和 AQ (Ack Quorum,确认数)三个参数协同工作,用户可以根据业务对可靠性和性能的不同要求进行精细调整。这种灵活性是传统消息队列所不具备的,使得 Pulsar 能够适应从金融交易到日志收集等各种不同可靠性要求的场景。
Bookie 的数据写入流程
Bookie 的写入流程经过精心优化,在保证持久化的同时实现高性能。
三阶段写入机制平衡了性能与可靠性。Journal 写入首先写入 Journal(WAL),实时落盘保证持久性,这一阶段类似于 MySQL 的 binlog,确保了即使系统崩溃也不会丢失已确认的写入。Memtable 缓存写入内存缓冲区后即可响应客户端,视为写入成功,这种异步持久化机制是高性能的关键。批量刷盘阶段在 Memtable 满后批量 flush 到 EntryLog (数据文件)和 Index (索引),通过批量操作减少了 IO 次数,提升了吞吐量。
优化机制多方面提升性能。顺序追加让 Journal 采用顺序写提升 IO 性能,充分利用了现代存储设备的特性。双缓冲技术类似 JVM 新生代的 S1/S2 区,交替刷盘提升吞吐,这种技术在高性能存储系统中广泛应用。索引分离使用 RocksDB 构建 LedgerID+EntryID 到物理偏移的索引,避免 IO 竞争,专门的索引引擎优化了查询效率。
线程模型精细化的并发控制。SyncThread 定期同步缓冲区数据,ForceWriteThread 处理强制刷盘请求,这种分工确保了在各种负载条件下都能保持稳定的性能。写入链路的每个环节都有专门的线程池处理,避免了不同操作之间的相互阻塞。
组件协作体现了模块化设计的优势。客户端写入请求通过 ClientBookieImpl 处理, WriteCache 实现内存缓冲和 LRU 缓存管理, EntryLogger 负责 EntryLog 的异步转储和排序,RocksDB 维护索引信息。各组件职责单一,通过清晰的接口进行协作,这种设计不仅提高了系统的可维护性,也为未来的优化和扩展留下了空间。
Bookie 的数据读取流程
读取路径的优化直接影响了消费者的体验。
三级缓存机制有效减少磁盘 IO。Broker 缓存拦截高频请求,在服务层减少向下游的压力。Bookie 内存缓存包括 WriteCache 和 ReadCache,利用局部性原理提高命中率。磁盘访问作为最后的手段,在前两级未命中时触发。这种多级缓存设计在现代存储系统中证明是有效的平衡成本与性能的方案。
读取类型针对不同场景优化。Tailing Read 读取最新数据,直接从 Memtable 获取,为实时消费者提供了最低的延迟。Catch-up Read 处理历史数据读取,需通过索引定位后访问磁盘,这种读写分离的优化避免了实时读写之间的竞争。
索引加速机制快速定位数据。通过 RocksDB 索引快速定位 <LedgerID,EntryID> 对应的物理位置,这种外部索引的设计避免了在数据文件中进行全扫描的开销。 RocksDB 的 LSM 树结构特别适合这种点查询场景,为消息检索提供了稳定的性能保证。
性能优化贯穿整个读取链路。系统尽量避免直接磁盘访问,优先从多级缓存获取数据,这种缓存优先的策略在典型工作负载下能够达到 90% 以上的命中率。对于必须访问磁盘的场景,Bookie 采用了预读、批量读取等技术来提高 IO 效率。
元数据与一致性保障
元数据抽象与多后端支持
元数据管理是分布式系统的中枢神经,其设计直接影响系统的可靠性和扩展性。
统一接口设计通过 MetadataStore 接口屏蔽 ZK/etcd/RocksDB 差异。这种面向接口的编程思想提高了系统的可扩展性,使得在不同环境中部署时可以选择最适合的元数据存储后端。在云原生环境中,这种灵活性让 Pulsar 能够更好地与各种基础设施集成。
两级缓存机制有效提升元数据操作性能。通过 MetadataCache 减少后端访问次数,这种缓存策略在元数据读取远多于写的场景中特别有效。缓存的一致性通过版本号机制保证,确保了在分布式环境中的数据正确性。
并发控制方案提供多种一致性保证。版本号乐观锁适用于大多数并发更新场景,通过轻量级的冲突检测避免了昂贵的锁开销。分布式锁用于 LeaderElection 等需要强一致性的场景,保障分区映射的正确性。这种分层的一致性策略既保证了正确性,又避免了过度同步带来的性能损失。
分布式锁与 Leader 选举机制
Leader 选举是分布式协调的核心功能,其可靠性直接关系到系统的可用性。
实现流程遵循分布式系统的最佳实践。申请锁时创建临时节点(EPHEMERAL),这种临时节点的特性确保了在客户端连接断开时能够自动清理,避免了死锁的发生。注册 watch 监听连接状态,使得参与者能够及时感知状态变化并做出响应。自动续期机制防止锁失效,通过心跳机制保持锁的活性。
关键保障机制防止脑裂问题。通过 ZKMetadataStore 在 ZooKeeper 创建临时节点,LockManagerImpl 调用 acquireLock 创建 ResourceLockImpl,确保同一 Bundle 仅一个 Broker 可写入。这种互斥保证是消息顺序性和一致性的基础,在金融、交易等场景中尤为重要。
扩展性设计面向未来。不同元数据存储组件(RocksDB/etcd)可替换实现,保持机制统一。这种可插拔架构使得 Pulsar 能够跟上基础设施演进的步伐,在新技术出现时能够快速集成而不影响上层应用。
负载均衡与分片管理
NamespaceBundle 分片机制
分片机制是 Pulsar 实现细粒度负载均衡的基础,其设计直接影响系统的扩展性和稳定性。
设计优势体现在调度粒度的选择上。以 Bundle 而非 Topic 为调度粒度,避免百万级 Topic 的惊群问题。在大规模多租户环境中,Topic 数量可能达到数十万甚至百万级别,如果以单个 Topic 为调度单位,元数据量和调度复杂度将是灾难性的。Namespace 被哈希划分为多个 Bundle,每个对应一段哈希区间,这种类似哈希环的结构既保证了均匀性,又保持了灵活性。
核心能力满足各种复杂场景的需求。动态分片支持运行时分裂与合并,应对热点场景,这种弹性是传统静态分区方案所不具备的。热点卸载机制使得高负载 Bundle 可迁移至空闲节点,实现了集群资源的全局优化。所有权控制通过分布式锁竞争 Bundle 所有权,避免多 Broker 同时持有,保证了数据的一致性。
Bundle 获取与分裂流程
Bundle 的生命周期管理体现了系统的自动化运维能力。
Bundle 获取流程高效而准确。客户端请求 Topic 时,通过 TopicHash 进行二分查找,这种查找算法的时间复杂度为 O(logN),即使在海量 Topic 场景下也能快速定位。 NamespaceService 返回对应的 NamespaceBundle,类似哈希环查找算法,确定 Bundle 归属。整个获取过程在内存中完成,避免了昂贵的 IO 操作。
Bundle 分裂流程响应负载变化。当 LoadManager 检测到热点 Bundle(基于内存等指标)时自动触发分裂。系统计算分裂边界并更新元数据,通过抢锁机制获取新 Bundle 所有权,支持多种分裂算法选择。这种自动化热点处理能力大大减轻了运维负担,特别是在流量模式快速变化的互联网业务中。
Bundle 卸载流程
Bundle 卸载是负载均衡的关键环节,其可靠性直接影响用户体验。
关键步骤确保状态正确转换。设置 isActive=false 标记卸载状态,防止新的写入进入正在迁移的 Bundle。关闭 Bundle 内所有 Topics,确保所有待处理消息完成持久化。删除元数据中的所有权节点,释放资源以便重新分配。这个状态转换过程需要严格的顺序保证,任何步骤的失败都会导致回滚。
保障措施实现无感知迁移。消息不丢失是通过持久化机制和副本保证的。连接重连在毫秒级完成,对于客户端来说,迁移过程几乎是透明的。这种平滑迁移能力对于在线业务特别重要,避免了因系统维护导致的服务中断。
元数据维护保持最终一致性。通过 MetadataStore 同步更新 Bundle 归属关系,采用乐观锁等机制处理并发更新。在分布式环境中,元数据的一致性维护是复杂的, Pulsar 通过版本控制和冲突解决机制来保证的正确性。
故障转移与容错机制
跨集群复制架构
跨集群复制是构建高可用业务系统的基础,其可靠性直接影响业务的连续性。
核心组件协同完成复制任务。通过 PersistentReplicator 实现异地多活,形成闭环流程。这种专门的复制组件设计避免了与正常消息处理的资源竞争,保证了复制性能的稳定性。
消息发送采用异步非阻塞模式。经 ProducerImpl 异步发送到远端集群,例如集群 A 配置集群 B 地址后自动转发。异步发送避免了复制过程对本地写入性能的影响,这种设计在跨地域的高延迟网络环境中特别重要。
消息读取基于游标机制。通过 ManagedCursor 从本地 Ledger 读取 Entry,类似 Kafka 的 offset 概念。游标的抽象使得复制过程能够断点续传,在网络中断恢复后从中断点继续复制,避免了重复复制带来的资源浪费。
状态确认保证最终一致性。成功复制后更新游标位置,避免重复复制。这种位置管理机制类似于传统数据库的复制位点,为故障恢复提供了准确的参考点。
流量控制防止网络过载。使用 DispatchRateLimiter 限流,通过 TTL 机制过滤过期消息。在跨数据中心的复制场景中,带宽往往是宝贵且有限的资源,合理的流量控制保证了关键业务的复制优先级。
故障检测与自动重试机制
故障处理能力是系统健壮性的试金石。
本地存储阶段确保数据安全。客户端消息先持久化到本地 ManagedLedger,返回确认后才触发复制。这种"先持久化,后复制"的语义保证了即使在复制失败的情况下,本地数据仍然是完整的,为手动修复提供了可能。
异步复制机制平衡性能与可靠性。Replicator 读取待复制条目后,通过 producer sendAsync() 异步发送。异步化避免了复制延迟对生产者响应时间的影响,这种设计哲学贯穿 Pulsar 的整个架构。
断点续传优化故障恢复。连接恢复后从 cursor.asyncDeletePosition 记录的位点继续复制。位点信息定期持久化,确保即使在复制组件崩溃重启后也能准确恢复。
故障处理采用分层策略。Connection Failed 时自动重试,通过 Handle failure & retry 机制保证可靠性。不同级别的故障采用不同的重试策略,临时性网络抖动可能立即重试,而持久性故障可能等待操作员干预。
容错机制全面保障
多层次的容错机制构建了系统的韧性。
Broker 故障处理自动化程度高。Topic1-Part1 从宕机的 Broker1 自动迁移到 Broker3,整个过程无需人工干预。通过 ZK/ETCD 记录新分区位置,元数据的持久化保证了故障恢复的准确性。分段重建机制创建新的 SegmentX+1 继续写入,原 Segment 保留在 Bookie,这种分段策略避免了单点故障影响整个分区。
Bookie 容错基于多副本机制。当 Bookie2 的 Segment4 损坏时,从其他 Bookie 复制数据到 Bookie1,这种自动修复能力保证了数据的长期可靠性。全节点恢复在 Bookie2 完全宕机时触发,所有 Segment 会重新分配到健康节点,重新分布过程考虑负载均衡,避免修复过程引发新的热点。
无感切换提升用户体验。客户端读写操作不受存储节点变更影响,这种透明性使得运维操作可以在业务无感知的情况下进行。对于 7x24 小时在线的互联网业务,这种能力是必不可少的。
多级保障构建防御纵深。存储层通过 Bookie 多副本保证数据不丢失,服务层在 Broker 故障时自动转移分区所有权,这种分层防御确保了单一组件的故障不会扩散到整个系统。
设计思想借鉴前人经验。类似 Kafka 但采用 Segment 分片流而非纯分区设计,这种改进使得故障恢复的粒度更细,影响范围更小。在架构设计中,Pulsar 团队显然深入分析了前人方案的优缺点,并做出了有针对性的改进。
总结
不同消息队列的架构差异反映了各自的设计哲学和适用场景。
Kafka 核心机制在大数据场景验证。分区副本采用有状态节点设计,扩缩容时需数据重新均衡,这种设计在集群规模稳定时表现优异,但在弹性伸缩场景中显得笨重。重平衡痛点在消费组任何变动都会触发耗时较长的重平衡过程,尤其在大规模消费者场景下影响显著。虽然 Kafka 在百万级消息吞吐场景表现优异,但 JVM 老年代 GC 问题在互联网应用中需特别注意,这种技术债务在长期运行的大型集群中逐渐显现。
RabbitMQ 设计特点体现经典架构的优劣。高可用实现依赖镜像队列机制,主队列写入后同步到镜像队列,这种主动-被动模式保证了强一致性,但牺牲了部分性能。架构优势在于元数据内嵌在 Broker 中,无独立管理组件,架构简洁轻量,适合中小规模部署。但随着规模增长,扩缩限制逐渐显现,仍需数据重平衡,运维复杂度随规模增长而提升。
Pulsar 创新的架构面向云原生时代。存算分离将副本下沉至 BookKeeper,计算层实现真正无状态,这种架构虽然引入了一定的复杂度,但换来了极致的弹性。扩缩优势使得节点变动无需数据均衡,天然适配云原生环境,在容器化部署和自动化运维方面表现出色。作为权衡,需接受因架构分层带来的网络延迟和运维复杂度提升,这种取舍在大多数现代业务场景中是值得的。
潜在演进方向
消息队列技术的演进永无止境,几个方向值得关注。
元数据管理寻求突破。Oxia 项目专为强一致性设计的元数据管理服务,解决 ZK/etcd 在大数据量写入时的性能瓶颈。未来将建立跨组件的标准化元数据管理层,这种统一管理有助于降低系统复杂度。
存储优化面向成本效益。冷热分离自动将冷数据卸载到 S3 等对象存储,实现近乎无限的存储扩展能力。成本优势明显,对象存储相比块存储成本可降低 70% 以上,这种经济性对于长期保留数据的业务场景具有重要意义。
Serverless 集成探索新范式。函数计算深度整合无服务器函数,实现消息触发式函数调度。处理流水线构建消息处理-函数执行的无缝工作流,这种模式在事件驱动架构中越来越流行。
协议兼容降低迁移成本。多协议代理原生支持 Kafka/RabbitMQ/MQTT 等协议,无需额外插件。迁移简化通过统一消息网关降低现有系统改造难度,这种兼容性策略帮助 Pulsar 在已有大量消息中间件部署的企业中快速推广。







评论