这是我参与「第四届青训营 」笔记创作活动的第13天
从Kafka到Pulsar——数据流演进之路
消息队列概述
应用场景
MQ消息通道
异步解耦、削峰填谷、发布订阅、高可用
EventBridge事件总线
事件源:将云服务、自定义应用。SaaS应用等应用程序产生的事件消息发布到事件集
事件集:存储接收到的事件消息,并根据事件规则将事件消息路由到事件目标
事件目标:消费事件消息
Data Platform流数据平台
提供批/流数据处理能力、各类组件提供各类Connect、提供Streaming/Function能力、根据数据schema灵活的进行数据预处理
主流的消息队列

Kafka详解
架构介绍

Zookeeper
保存元数据以及提供选主能力
- 选举机制:Paxos机制
- 提供一致性:写入(强一致性)、读取(会话一致性)
- 提供可用性:一半以上节点存活即可读写
- 提供能力:watch机制、持久/临时节点能力
- Kafka存储数据:Broker Meta信息(临时节点)、Conctroller信息(临时节点)、Topic信息(持久节点)、Config信息(持久节点)
Broker
Broker角色
- 若干个Broker节点组成Kafka集群
- Broker作为消息的接收模块,使用React网络模型进行消息数据的接收
- Broker作为消息的持久化模块,进行消息的副本复制以及持久化
- Broker作为高可用模块,通过副本间的Failover进行高可用保证
Controller选举

- Broker启动会尝试去zk中注册Controller节点
- 注册上的Controller节点的Broker即为Controller
- 其余Broker会watch controller节点,节点出现异常则进行重新注册
Controller作用

- Broker重启/宕机时,负责副本的failover切换
- topic创建/删除时,负责topic meta信息广播
- 集群扩缩容时,进行状态控制
- partition/replica状态机维护
Coordinator

-
负责topic-partition<->consumer的负载均衡
-
根据不同的场景提供不同的分配策略
- Dynamic Membership Protocol
- Static Membership Protocol
- Incremental Cooperative Rebalance
Kafka高可用
副本同步机制
- 提供isr副本复制机制、提供热备功能
- 写入端提供ack=0,-1,1机制、控制副本同步强弱
副本切换机制
- 提供clean/unclean副本选举机制
Kafka副本ISR机制
AR:Assign Replica已经分配的所有副本
OSR:Out Sync Replica、很久没有同步数据的副本
ISR:一直都在同步数据的副本、可以作为热备进行切换的副本、min.insync.replicas最少isr数量配置
Kafka写入ACK机制
ACK=1:leader副本写入成功,producer即认为写成功
ACK=0:Oneway模式、Producer发送后即为成功
ACK=-1:ISR中所有副本都成功,Producer才认为写成功
Kafka副本同步
LEO:Log End Offset日志最末尾的数据
HW:ISR中最小的LEO作为HW、HW的消息为Consumer可见的消息
Kafka副本选举
clean选举:优先选取Isr中的副本作为leader、如果Isr中无可用副本,则partition不可用
unclean选举:优先选取Isr中的副本作为leader、如果Isr中无可用副本,则选取其他存活副本
集群扩缩容
Kafka集群扩容之后的目标分别需要在Topic、Broker维度上考虑:
Topic维度:partition在各个broker之间分布是均匀的、同一个partition的replica不会分布在一台broker
Broker维度:broker之间replica的数量是均匀的
Kafka集群扩容步骤
扩容broker节点:leader副本写入成功,producer即认为写成功
计算均衡的replica分布拓扑:保证topic的partition在broker间分布均匀、保证broker之间replica分布均匀
controller负责新的副本分布元数据广播:controller将新的leader/follower信息广播给broker
broker负责新副本的数据同步:broker上有需要同步数据的副本则进行数据同步
扩缩容问题
扩缩容时间长,涉及到数据迁移,在生产环境中一次扩缩容可能要迁移TB甚至PB的数据
扩缩容期间集群不稳定,保证数据的完整性,往往会从最老的数据进行同步,这样会导致集群时刻处于从磁盘读取数据的状态,disk/net/cpu负载都会比较高
扩缩容期间无法执行其他操作,在一次扩缩容操作结束之前,无法进行其他运维操作
Kafka演进
去除zookeeper依赖
依赖zookeeper存在问题
- 元数据存取困难,元数据的存取过于困难,每次重新选举的controller需要把整个集群的元数据重新restore,非常的耗时且影响集群的可用性。
- 元数据更新网络开销大,整个元数据的更新操作也是以全量推的方式进行,网络的开销也会非常大
- 强耦合违背软件设计原则,zookeeper对于运维来说,维护也需要一定的开销,并且kafka强耦合与zk也并不好,还得时刻担心zk的宕机问题,违背软件设计的高内聚、低耦合的原则。
- 网络分区复杂度高,zookeeper本身并不能兼顾到broker和broker之间通信的状态,这就会导致网络分区的复杂度成几何倍数增长。
- 并发访问zk问题多,Zookeeper本身并不能兼顾到broker:与broker之间通信的状态,这就会导致网络分区的复杂度成几何倍数增长。
Kafka依赖KRaft
使用KRaft作为元数据和数据存储介质
process.roles = broker:服务器在KRaft模式下充当Brokerprocess.roles = controller:服务器在KRaft模式下充当Controllerprocess.roles = broker,controller:服务器在KRaft模式下充当Broker和Controllerprocess.roles = null:集群假定是运行在ZooKeeper模式下
Pulsar详解
架构

Pulsar proxy
连接集群的两种方式
Pulsar Client -> BrokerPulsar Client -> Proxy
作用及应用场景
- 部分场景无法知道broker地址,如云环境或者Kubernetes环境
- Proxy提供类似gateway代理能力,解耦客户端和broker,保障broker安全
Pulsar broker
Pulsar broker无状态组件,负责运行两个模块
-
Http服务器
- 暴露了restful接口,提供生产者和消费者topic查找api
-
调度分发器
- 异步的tcp服务器,通过自定义二进制协议进行数据传输
Pulsar broker作为数据层代理
-
Bookie通讯
- 作为Ledger代理负责和Bookie进行通讯
-
流量代理
- 消息写入Ledger存储到Bookie
- 消息缓存在堆外,负责快速响应
Pulsar storage
Pulsar数据存储Segment在不同存储中的抽象
- 分布式journal系统(bookkeeper)中为Jornal/ledger
- 分布式文件系统(GFS/HDFS)中为文件
- 普通磁盘中为文件
- 分布式blob存储中为blob
- 分布式对象存储中为对象
定义好抽象后,即可实现多介质存储
Pulsar storage 多级存储
L1(缓存)
- Broker使用堆外内存短暂存储消息
- 适用于Tail-Read读场景
L2(Bookkeeper)
- Bookkeeper使用Qurom写,能有效降低长尾,latency低
- 适用于catch-up较短时间内的较热数据
L3(S3等冷存)
- 存储成本低,扩展性好
- 适用于catch-up长时间内的冷数据
Pulsar IO连接器

- 分为输入和输出两个模块,通过source实现数据输入。通过sink实现数据输出
- Pulsar提出IO用于解决Pulsar与周边系统的集成问题,帮助用户高效完成工作
- 目前Pulsar IO支持非常多的连接集成操作,例如HDFS、spark、flink、flume、ES、HBase等
Pulsar Functions(轻量级计算架构)

- 一个轻量级计算框架,提供一个部署简单、运维简单、API简单的FAAS平台
- 提供基于数间的服务,支持有状态和无状态的多语言计算,是对复杂大数据处理框架的有力补充
- 用户可以轻松地部署和管理function,通过function从Pulsar topic读取数据或者生产新数据到Pulsar topic
Bookkeeper介绍
Bookkeeper结构

Bookkeeper基本概念
Ledger:BK的一个基本存储单元,BK Client的读写操作都是以Ledger为粒度的
Fragment:BK的最小分布单元,物理最小存储单元,也是Ledger的储存单位,默认情况下一个Ledger会对应一个Fragment,也可以对应多个
Entry:每条日志都是一个Entry,代表一个record,每条record都会有一个对应的Entry id
Bookkeeper新建Ledger
核心概念
Ensemble Size(E):一个Ledger所涉及的Bookie集合
Write Quorum Size(Qw):副本数
Ack Quorum Size(Qa):写请求成功需要满足的副本数
Bookkeeper Ledger分布

- 从bookie pool挑选bookies构成Ensemble
- Write Quorum Size(Qw)决定发送给哪些bookies
- Ack Quorum Size(Qa )决定收到几个ack*算成功
Bookkeeper写一致性

- LastAddPushed
- LastAddConfirmed
- Fencing避免脑裂
Bookkeeper读一致性
所有的reader都可以安全读取entry ID小于或者等于LAC的记录,从而保证reader不会读取未确认的数据,从而保证了reader之间的一致性
Bookkeeper读写分离
写入优化
- 写入时,不但会写入到journal中还会写入到缓存memtable中,定期会做刷盘(刷盘前会做排序、通过聚合+排序优化读取性能)
读取优化
- 先读memtable,没命中再通过索引读磁盘
- ledger driver中会维护一个索引结构,存储在rocksDB中,它会将(legerId,EntryId)映射到(EntryLogId,文件中的偏移量)
Bookkeeper with pulsar
Topic-Partition
- Topic由多个partition组成
- partition由多个segment组成
- segment对应ledger
可以发现,partition<->broker之间只是映射关系,broker在扩缩容过程中只需要更改映射
特性介绍
Pulsar生产模式

Pulsar消费模式
exclusive:独占订阅(stream模式):独占订阅中,在任何时间,一个消费者组(订阅)中有且只有一个消费者来消费topic中的消息
failover故障切换(stream流模式):使用故障切换订阅,多个消费者可以附加到同一订阅。但是,一个订阅中所有的消费者,只会有一个消费者被选为该订阅的主消费者。其他消费者将被指定为故障转移消费者。
shared共享订阅(queue队列模型):使用共享订阅,在同一个订阅背后,用户按照应用的需求挂载任意多的消费者。订阅中的所有消息以循环分发形式发送给订阅背后的多个消费者,并且一个消息仅传递给一个消费者。
key_shared按key共享订阅(queue队列模型):使用共享订阅,在同一个订阅背后,用户按照应用的需求挂载任意多的消费者。订阅中的所有消息以key-hash发送给订阅背后的多个消费者,并且一个消息仅传递给一个消费者。
Pulsar多租户
Pulsar多租户体现在url中,使用多级映射做资源管理。

Pulsar Plugin
Pulsar Plugin支持无缝迁移,支持多种协议
当前支持Plugin的类型:KOP(Kafka on Pulsar) 、ROP(RocketMQ on Pulsar) 、AOP(AMQP on Pulsar) 、Mop(MQTT on Pulsar)
实现plugin需要支持的功能:路由查询、Message Protocol、Offset & Msgld
Pulsar GEO Relication
支持数据容灾能力
- 跨数据中心复制
- 消费其他地域数据
集群HA & Scale-up
- Topic<->Bundle完成映射
- Bundle分配给broker

Pulsar 对比 Kafka
多层架构,状态分离之后的优势
- 流量代理层和数据存储层解耦
- 流量代理层无状态,可快速扩缩容(k8s等弹性平台)
- 流量代理层可以对接海量的客户端连接
- 存储层负责数据存储,可以使用多级存储
存储计算分离之后带来的优劣势,在计算层上,对于写入的数据,可以做预处理,简单ETL。可以做数据缓存,应对高扇出度场景。无状态、扩缩容之后,能快速完成负载均衡balance。在存储层上,按照数据冷热进行存储介质区分,降低成本;历史数据可海量保存,数据无价;可直接通过存储层接口读取数据,批式计算。