携手创作,共同成长!这是我参与「掘金日新计划 · 8 月更文挑战」的第3天,点击查看活动详情 >>
\
基本概念:Ledger:BK中的基本存储单元;Fragment:BK的最小分布单元;Entry:日志
架构设计:Clients、ZooKeeper、Bookie pool;Bookies 在启动的时候向 ZooKeeper 注册节点,Client 通过 ZooKeeper 发现可用的 Bookie。
1、Journals :存储事务日志,提供sync
2、EntryLogFile:存储真正数据的文件。所有ledger的数据先缓存在buffer中,然后聚合然后flush顺序写入到同一个EntryLog文件中,避免磁盘随机写。
- 刷盘前会做排序,通过 聚合+排序 优化读取性能
3、Index 文件:数据的索引文件,存储在Rocksdb中。为了加速数据读取,设置的 ledgerId + entryId 到文件 offset 的映射。先缓存在内存中,称为IndexCache,容量达到上限时,通过sync写入磁盘。
写入流程
-
Writer 会先分配对应的 id,然后按照 round-robin 算法从3个 Bookie 中选取2个 Bookie;
-
Writer 会向两个 Bookie 发送写入请求,因为 Qa 设置为2,只有收到两个 ack 响应后,才会认为这条 Entry 写入成功;
如果写入过程中有一台 Bookie 挂了怎么办?
- 那么只能向另外2台 Bookie 写入数据;
- 这时候这个 Ledger 会新建一个 Fragment,假设挂的是A,之前 Ensemble 是 A、B、C,现在的是 B、C;
- 这个变化会更新到 zk 中这个 Ledger 的 meta 中。
如果写入过程中有两个 Bookie 挂了怎么办?
- Ensemble 里面的存活的 Bookies 不能满足 Qw 的要求;
- Client 会进行一个 Ensemble Change 操作;
- Ensemble Change 将从 Bookie Pool 中根据数据放置策略挑选出额外的 Bookie 用来取代那些不存活的 Bookie 。
Bookie写细节
- 数据同时写入Journal和Memtable
- 写入Memtable对写入请求进行相应
- Memtable写满之后flush到Entry Logger和Index cache
- 后台线程帮助数据落盘
读取流程
-
在读取会选择最优的 Bookie,有了 Entry 的 id 和 Ledger 的 Ensemble 就可以根据 round-robin 计算出其所在 Bookie 信息,会选择向其中一个 Bookie 发送读请求。
- 优化:读取时一般是选择读一段数据,如果 entries 在同一台机器上,会从同一个 Bookie 把这批 Entry 全部读取。
-
长尾效应处理——Speculative Read,先发送给第一个副本,如果在指定时间没有response,向第二副本发送请求,同时等待,又一个返回即读取成功。
Bookie读细节
- Tailing read:直接从Memtbale中读取Entry
- Catch-up read(滞后消费)请求:先读取Index信息,然后索引从Entry Logger文件读取Entry
一致性保证
写的一致性:
- 写入的数据都是先缓存在内存中,如果BK奔溃,可以根据journal中的事务信息进行数据恢复,LastLogMark记录了journal恢复位置信息
- LastLogMark被持久化磁盘上之后,journal在此之前的数据都可以被清除了。
- BK限制每个Ledger只能被一个writer写,Fencing机制防止脑裂现象出现。
- 每条记录会被 writer 赋予一个严格递增的 id,写成功更新Last-Add-Confirm指针。LAC 与 LAP(Pushed)差值为正在写数据
读的一致性:
所有的 Reader 都可以安全读取 Entry ID 小于或者等于 LAC 的记录
容错机制
-
Fencing机制:严格保证一个 Ledger 只能被一个 Writer 来写
- 触发时机:如果一个 Writer 打开一个 Ledger,发现这个 Ledger 存在,并且没有 close,这种情况下,就会触发 Fencing 策略,并且触发 Ledger Recovery。
-
Log Recovery机制:正常关闭异常 Ledger,并将 The Last Entry 及状态更新到 metadata 中
-
实现方式:
- 遍历ledger所有entry进行恢复;
- 利用LAC加速恢复
-
生产模式
- Shared:多个Producer可以同时往一个Topic中生产消息
- Exclusive:独占模式生产,只有一个Producer可以connect并生产消息,其他Producer可以启动成功,作为stand-by
- ExclusiveWithFencing:独占模式生产,只有一个Producer可以connect并生产消息,其他Producer启动时,老的Producer会断开链接
- WaitForExclusive:独占模式生产,只有一个Producer可以connect并生产消息,其他Producer会卡在创建Producer环节
消费模式
- Exclusive:独占订阅,在任何时间,一个消费组订阅中有且只有一个消费者来消费Topic中的消息
- Failover:故障切换,多个消费者可以附加到同一订阅
- Shard:同一个订阅,用户按照应用的需求挂载任意多的消费者
- key_shared:使用共享订阅,订阅中的所有消息以key-hash发送给订阅背后的多个消费者,并且一个消息仅传递给一个消费者