从 Kafka 到 Pulsar 的数据流演进之路| 青训营笔记
这是我参与「第四届青训营 」笔记创作活动的的第12天
消息队列概述
消息队列的应用场景
- MQ 消息通道
- EventBridge 事件总线
- Data Platform 流数据平台
MQ 消息通道
- 异步解耦
- 削峰填谷
- 高可用
- 发布订阅
EventBridge 事件总线
- 事件源: 将云服务、自定义应用、SaaS 应用等应用程序产生的事件消息发布到事件集。
- 事件集:存储接收到的事件消息,并根据事件规则将事件消息路由到事件目标。
- 事件目标:消费事件消息。
Data Platform 流数据平台
- 提供批流数据处理能力
- 各类组件提供各类Connect
- 提供Streaming/Function 能力
- 根据数据 schema 灵活的进行数据预处理
主流消息队列的相关介绍
| RabbitMQ | RocketMQ | Kafka | Pulsar | |
|---|---|---|---|---|
| 推出时间 | 2007 | 2012 | 2010 | 2016 |
| 使用语言 | Erlang | Java | Scala/Java | Java |
| 单机吞吐量 | 一般 | 较高 | 高 | 高 |
| 延迟 | 低 | 低 | 一般 | 低 |
| 可用性(分片) | 高(主从架构) | 高(主从架构) | 非常高(分布式) | 非常高(分布式) |
| 一致性 | 较高 | 高 | 高 | 高 |
| 扩展性 | 较高 | 高 | 高 | 非常高 |
Kafka 详解
Kafka 架构介绍
Zookeeper
- 选举机制:Paxos 机制
- 提供一致性:写入(强一致性)、读取(会话一致性)
- 提供可用性:一半以上节点存活即可读写
- 提供功能:watch 机制 持久/临时节点能力
Kafka在Zookeeper存储哪些数据:
- Broker Meta 信息(临时节点)
- Controller 信息(临时节点)
- 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(in-sync replica set):
- 一直都在同步数据的副本
- 可以作为热备进行切换的副本
- 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 的消息为C onsumer 可见的消息
Kafka 副本选举
- Clean 选举
- 优先选取 Isr 中的副本作为 leader
- 如果 Isr 中无可用副本,则 partition 不可用
- Unclean 选举
- 优先选取 Isr 中的副本作为 leader
- 如果 Isr 中无可用副本,则选择其他存活副本
Kafka 集群扩缩容
Kafka 集群扩缩容之后的目标
- 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 上有需要同步数据的副本则进行数据同步
Kafka 集群缩容步骤
- 计算均衡的Replica分布拓扑
- 保证 Topic 的 partition 在 broker 间分布均匀
- 保证 Broker之间Replica 分布均匀
- Controller负责新的副本分布元数据广播
- Controller 将新的 leader/follower 信息广播给broker
- Broker负责新副本的数据同步
- Broker 上有需要同步数据的副本则进行数据同步
- 下线缩容的 Broker 节点
- 数据同步完毕之后下线缩容的 Broker节点
Kafka 集群扩缩容问题
- 扩缩容时间长
- 涉及到数据迁移,在生产环境中一次扩缩容可能要迁移TB甚至PB的数据
- 扩缩容期间集群不稳定
- 保证数据的完整性,往往会从最老的数据进行同步,这样会导致集群时刻处于从磁盘读取数据的状态,disk/net/cpu 负载都会比较高
- 扩缩容期间无法执行其他操作
- 在一次扩缩容操作结束之前,无法进行其他运维操作(扩缩容)
Kakfa 未来演进之路
Kafka 去除 zk 依赖
依赖 ZooKeeper 存在问题
- 元数据存取困难
- 元数据的存取过于困难,每次重新选举的controller需要把整个集群的元数据重新restore,非常的耗时且影响集群的可用性。
- 元数据更新网络开销大
- 整个元数据的更新操作也是以全星推的方式进行,网络的开销也会非常大。
- 强耦合违背软件设计原则
- zookeeper对于运维来说,维护Zookeeper也需要一定的开销,并且kaika强耦合与zk也并不好,还得时刻担心zk的宕机问题,违背软件设计的高内聚,低耦合的原则。
- 网络分区复杂度高
- Zookeeper本身并不能兼顾到broker与broker之间通信的状态,这就会导致网络分区的复杂度成几何倍数增长。
- 并发访问 zk 问题多
- Zookeeper本身并不能兼顾到broker与broker之间通信的状态,这就会导致网络分区的复杂度成几何倍数增长。
Kafka 依赖 KRaft
- Process.Roles = Broker
- 服务器在 KRaft 模式下充当 Broker
- Process.Roles = Controller
- 服务器在 KRaft 模式下充当 Controller
- Process.Roles = Broker,Controller
- 服务器在 KRaft 模式下充当 Broker 和 Controller
- Process.Roles = null
- 那么集群就假定是运行在ZooKeeper模式下。
Raft算法
不同于Paxos算法直接从分布式一致性问题出发推导出来,Raft算法则是从多副本状态机的角度提出,用于管理多副本状态机的日志复制。Raft实现了和Paxos相同的功能,它将一致性分解为多个子问题:Leader选举(Leader election)、日志同步(Log replication)、安全性(Safety)、日志压缩(Log compaction)、成员变更(Membership change)等。同时,Raft算法使用了更强的假设来减少了需要考虑的状态,使之变的易于理解和实现。
Raft将系统中的角色分为领导者(Leader)、跟从者(Follower)和候选人(Candidate):
- Leader:接受客户端请求,并向Follower同步请求日志,当日志同步到大多数节点上后告诉Follower提交日志。
- Follower:接受并持久化Leader同步的日志,在Leader告之日志可以提交之后,提交日志。
- Candidate:Leader选举过程中的临时角色。
Raft要求系统在任意时刻最多只有一个Leader,正常工作期间只有Leader和Followers。
Raft算法角色状态转换如下:
Follower只响应其他服务器的请求。如果Follower超时没有收到Leader的消息,它会成为一个Candidate并且开始一次Leader选举。收到大多数服务器投票的Candidate会成为新的Leader。Leader在宕机之前会一直保持Leader的状态。
参考文档zhuanlan.zhihu.com/p/32052223
Kafka 运维/调优经验介绍
Kafka 单机吞吐
- Kafka Version
- 2.3.1
- 机器配置
- 40C 500GB 12 * 1TB 25GB网卡
- 写入配置
- Ack = -1, replica = 3,in_sync_replica = 2-3之间
- 单条消息 5 KB
- 吞吐
- 单机150MB/s
参数调用
zookeeper.session.timeout.ms = 30000 # zookeeper连接超时时长
log.segment.bytes = 536870912
log.retention.hours = 36
log.retention.bytes = 274877906944
num.network.threads = 32
num.io.threads = 200
auto.create.topics.enable = false # 自动创建topic
auto.leader.rebalance.enable = false # 自动leader的平衡(负载均衡)
unclean.leader.election.enable = false # unclean选举
advertised.listeners = SASL_PLAINTEXT://:,PLAINTEXT://:
security.inter.broker.protocol = SASL_PLAINTEXT
扩缩容优化
- Topic-Partition均匀分布在 Broker 间
- Broker 间的Replica是均匀的
指标可视化
Pulsar 详解
Pulsar 架构介绍
Pulsar Proxy
- Pulsar 客户端连接集群的两种方式
- Pulsar Client -> Broker
- Pulsar Client -> Proxy
- Pulsar Proxy 的作用及应用场景
- 部分场景无法知道 Broker 地址,如云环境或者Kubernetes环境
- Proxy提供类似 GateWay 代理能力,解耦客户端和 Broker,保障 Broker 安全
Pulsar Broker
- Pulsar Broker 无状态组件,负责运行两个模块
- Http 服务器
- 暴露了restful 接口,提供生产者和消费者 topic 查找 api
- 调度分发器
- 异步的tcp服务器,通过自定义二进制协议进行数据传输
- Http 服务器
- Pulsar Broker 作为数据层代理
- Bookie通讯
- 作为Ledger代理负责和Bookie进行通讯
- 流量代理
- 消息写入 Ledger 存储到 Bookie
- 消息缓存在堆外,负责快速响应
- Bookie通讯
Pulsar Storage
- Pulsar数据存储 Segment 在不同存储中的抽象
- 分布式 Journal 系统(Bookeeper) 中为 Joumal/Ledger
- 分布式文件系统(GFS/HDFS)中为文件
- 普通磁盘中为文件
- 分布式对象存储中为对象
- 定义好抽象之后,即可实现多介质存储
- L1(缓存):
- Broker使用堆外内存短暂存储消息
- 适用于 Tail-Read 读场景
- L2(Bookkeeper):
- Bookkeeper使用Qurom 写,能有效降低长尾, latency 低
- 适用于Catch-Up较短时间内的较热数据
- L3(S3等冷存):
- 存储成本低,扩展性好
- 适用于 Catch-Up 长时间内的冷数据
Pulsar IO 连接器
- Pulsar IO分为输入 (Input)和输出(Output)两个模块,输入代表数据从哪里来,通过Source 实现数据输入。输出代表数据要往哪里去,通过Sink实现数据输出。
- Pulsar 提出了 IO (也称为Pulsar Connector),用于解决Pulsar 与周边系统的集成问题,帮助用户高效完成工作。
- 目前 Pulsar IO 支持非常多的连接集成操作:例如HDFS、Spark、Flink、Flume、Es、HBase等。
Pulsar Functions (轻量级计算框架)
- Pulsar Functions 是一个轻量级计算框架,提供一个部署简单、运维简单、API简单的FAAS平台。
- Pulsar Functions 提供基于事件的服务,支持有状态与无状态的多语言计算,是对复杂的大数据处理框架的有力补充。
- 使用 Pulsar Functions,用户可以轻松地部署和管理 function,通过 function 从 Pulsar topic 读取数据或者生产新数据到 Pulsar topic。
Bookeeper 整体架构
Bookeeper 读写流程
Bookkeeper 基本概念
- Ledger: BK 的一个基本存储单元, BK Client 的读写操作都是以Ledger为粒度的
- Fragment: BK 的最小分布单元 (实际上也是物理上的最小存储单元),也是 Ledger 的组成单位,默认情况下一个Ledger 会对应的一个Fragment(一个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 决定发送给哪些 Bookies
- Ack Quorum Size 决定收到几个Ack 即为成功
Bookkeeper 写一致性
- LastAddPushed
- LesAqacommed
- Fencing 避免脑裂
Bookkeeper 读一致性
所有的 Reader 都可以安全读取 Entry ID 小于或者等于 LAC 的记录,从而保证reader 不会读取未确认的数据,从而保证了 reader之间的一致性
Bookkeeper 读写分离
-
写入优化:
- 写入时,不但会写入到 Journal 中还会写入到缓存(memtable)中,定期会做刷盘(刷盘前会做排序, 通过聚合+排序优化读取性能)
-
读取优化:
- 先读Memtable, 没命中再通过索引读磁盘
- Ledger Device 中会维护一个索引结构,存储在RocksDB中,它会将(Ledgerld, Entryld) 映射到(EntryLogld,文件中的偏移量)
Bookkeeper with pulsar
- Topic-Partition:
- Topic 由多个 partition组成
- Partition 由多个segment组成
- Segment 对应 Ledger
- 可以发现:
- Partition <-> Broker之间只是映射关系
- Broker在扩缩容的过程中只需要更改映射即可
Pulsar 功能介绍
Pulsar 生产模式
| Access Mode | Describtion |
|---|---|
| Shared | 多个 Producer 可以同时往一个 Topic 中生产消息 |
| Exclusive | 独占模式生产,只有一个Producer 可以 connect 并生产消息其他Producer可以启动成功,作为Stand-by |
| ExclusiveWithFencing | 独占模式生产,只有一个Producer可以connect 并生产消息其他Producer启动时,老的Producer 会断开连接 |
| WaitForExclusive | 独占模式生产,只有一个Producer 可以connect 并生产消息其他Producer会卡在创建 Producer 环节 |
Pulsar 消费模式
- Exclusive
- Failover
- Shared
- Key_Shared
Exclusive 消费模式
独占订阅(Stream流模型)
- 独占订阅中,在任何时间,一个消费者组(订阅)中有且只有一个消费者来消费Topic中的消息。
Failover 消费模式
故障切换(Stream流模型)
- 使用故障切换订阅,多个消费者(Consumer) 可以附加到同一订阅。但是,一个订阅中的所有消费者, 只会有一个消费者被选为该订阅的主消费者。 其他消费者将被指定为故障转移消费者。
Shared 消费模式
共享订阅 (Queue队列模型)
- 使用共享订阅,在同一个订阅背后,用户按照应用的需求挂载任意多的消费者。订阅中的所有消息以循环分发形式发送给订阅背后的多个消费者,并且一个消息仅传递给一个消费者。
Key_Shared 消费模式
按Key共享订阅(Queue队列模型)
- 使用共享订阅,在同一个订阅背后,用户按照应用的需求挂载任意多的消费者。订阅中的所有消息以 key-hash 发送给订阅背后的多个消费者,并且一个消息仅传递给一个消费者。
Pulsar 多租户
Pulsar 多租户体现在Url中 persistent://tenant/namespace/topic
Pulsar Plugin
- 当前支持 Plugin 类型
- KOP (Kafka on Pulsar)
- ROP (RocketMQ on Pulsar)
- AOP (AMQP on Pulsar)
- Mop (MQTT on Pulsar)
- 实现PIugin需要支持的功能
- 路由查询
- Message Protocol
- Offset & Msgld
Pulsar GEO Relication
- 跨数据中心复制
- 消费其他地域数据
Pulsar HA & Scale-up
- Topic <-> Bundle 完成映射
- Bundle 分配给 Broker
- Lookup Topic
- Lookup Result
- Establish TCP Connection
Pulsar vs Kafka
-
存储架构
- 存储计算分离之后带来的优劣势
- 多层架构,状态分离之后的优势
-
运维操作
-
应对突发流量变化,集群扩缩容是否便捷
-
运维任务是否影响可用性
-
集群部署是否灵活
-
-
功能特性
- 多语言 & 多协议
- 多租户管理
- 生产消费模式
-
生态集成
存储计算分离
-
分层架构优势
- 流量代理层和数据存储层解耦
- 流量代理层无状态,可快速扩缩容(k8s等弹性平台)
- 流量代理层可以对接海量的客户端连接
- 存储层负责数据存储,可以使用多级存储
-
计算层
- 对于写入的数据,可以做预处理, 简单 ETL
- 可以做数据缓存,应对高扇出度场景
- 无状态,扩缩容之后,能快速完成负载均衡Balance
-
存储层
- 按照数据冷热进行存储介质区分,降低成本
- 历史数据可海量保存,数据无价
- 可直接通过存储层接口读取数据,批式计算
周边和生态
Pulsar IO
source
sink
Kafka Schema
- 向 Kafka 发送数据时,需要先向Schema Registry 汪册 schema,然后序列化发送到 Kafka 里
- Schema Registryerver 为每个注册的 schema 提供一个全局唯ID.分配的ID保证单调递增,但不一定是连续的
- 当我们需要从 Kafka 消费数据时,消费者在反序列化前,会先判断schema 是否在本地内存中,如果不在本地内存中,则需要从 Schema Registry 中获取 schema,否则,无需获取