1、BookKeeper
将在Apache Pulsar的背景下讨论BookKeeper
尽管BookKeeper是一个通用的分布式日志存储解决方案。
首先,BookKeeper将数据存储至集群中的节点上,每个BookKeeper节点称为Bookie。
其次,Pulsar和BookKeeper都使用Apache Zookeeper来存储元数据和监控节点健康状况。
Apache Pulsar、BookKeeper和Zookeeper
如下
2、Topic&Ledgers&Fragment$Entry
一个Topic实际上是一个ledgers流。Ledger本身就是一个日志。所以一系列的子日志(Ledgers)组成了一个父日志(Topic)。
Ledgers追加到一个Topic,
条目(Entry)(消息或者一组消息)追加到Ledgers。
Ledger一旦关闭是不可变的。Ledger作为最小的删除单元
,也就是说我们不能删除单个条目(Entry)而是去删除整个Ledger。
Ledgers本身也被分解为多个Fragment。Fragment是BookKeeper集群中最小的分布单元
(depending on your perspective, striping might invalidate that claim)
在底部的条目Entry
如下
Topic是Pulsar中的概念。Ledger和Fragment是BookKeeper中的概念,尽管Pulsar知道且使用Ledgers和Fragment。
每个Ledger(由一个或多个Fragment组成)可以跨多个BookKeeper节点(Bookies)进行复制,以实现数据容灾和提升读取性能。每个Fragment都在一组不同的Bookies中复制(存在足够的Bookies)。
Apache Pulsar、BookKeeper和Zookeeper协同工作
如下
3、Ledger的E、Qw、Qa配置
每个Ledger有三个关键配置:
-
Ensemble Size (E)
-
Write Quorum Size (Qw)
-
Ack Quorum Size (Qa)
这些配置可以应用到Topic级别
,然后pulsar会在Topic使用的BookKeeper Ledgers/Fragments上设置。
3.1、Ensemble Size(E)
Ensemble表示将要写入的实际的Bookies数量,用E表示。
E表示Pulsar需要使用的Bookies数量。
请注意,在配置时您至少需要E个bookies才能正常的使用。
默认情况下,从可用的bookies列表中随机选取E个bookies(每个bookie在Zookeeper中注册自己)。
通过将Bookies标记为属于特定机架,还可以选择配置机架感知。
机架可以是逻辑结构(例如:云环境中的可用区域)。通过机架感知策略,Pulsar Broker的BookKeeper客户端将尝试从不同的机架选择Bookies。
也可以自定义策略是定制化Bookies的选择。
Ensemble Size (E) 决定了Pulsar写入Ledger可用的Bookies池的大小
。
每个Fragment可能有不同的Bookies列表,Broker将在创建Fragment时选择一组Bookies,E的数量是一致的
。
必有足够的Bookies数量(> E)。
3.2、Write Quorum(Qw)
Write Quorum (Qw) 是Pulsar将要写入的实际的Bookies数量。可以等于或者小于E。
E=3,Qw=3
如下所示
当Qw小于E时
如下所示
以条带化
的方式分配读/写即每个Bookie只提供读写请求的子集。因此可以提升吞吐量,将次延迟。
3.3、Ack Quorum (Qa)
Ack Quorum (Qa) 是确认写入Bookies的数量,Pulsar Broker将确认发送给客户端。为了一致性,Qa应该是:(Qw + 1) / 2 或者更大。
在实践中:
(Qa == Qw) 或
(Qa == Qw - 1) —> 这样避免单节点响应缓慢而改善写入延迟。
最终,每个Bookie必须都接受写入。如果我们总是等待所有Bookies做出响应,我们可能因为因为单个Bookie响应缓慢带来的整体延迟上升。Pulsar毕竟承诺有可预测的延迟。
4、何时创建新的Ledger、Fragment?
当创建一个新的Topic或者Ledger滚动时会创建一个新的Ledger。
Ledger在以下这些情况会发生滚动并创建新的Ledger:
-
已达到Ledger的大小或时间限制。
-
Ledger的所有权(Pulsar Broker的所有权)发生变化(稍后会详细介绍)。
以下情况会创建新的Fragment:
-
创建新的Ledger。
-
当前Fragment使用Bookies发生写入错误或超时。
当一个Bookies无法服务写入操作时,Pulsar Broker会创建一个新的Fragment,并确保写入的Bookies数量达到Qw(Write Quorum)值,会不断的重试直到消息被持久化。
5、总结
通过前面的介绍我们可以得到以下认识:
1、增加E以优化延迟和吞吐量。增加Qw牺牲吞吐量实现冗余。增加Qa提升数据的容灾但会增加延迟和单一节点响应慢导致的延迟增加。
2、E和Qw不是Bookies的列表。它们支持表明可为给定的Ledger服务的Bookies池有多大。Pulsar将在创建新的Ledger或Fragment时使用E和Qw。每个Fragment都有一组固定的Bookies且不可变。
3、添加一个新的Bookies不意味着需要手动Rebalance。这些新的Bookies将自动成为Fragment的候选人。加入集群后,将在创建新的Fragment/Ledger后立即写入新的Bookies。每个Fragment都可以存储在不同的Bookies的子集中!我们不需要明确将Topic或Ledger分配指定的Bookies。
总结一下。相对于kafka,这是一个非常不同且复杂的模型。对于kafka,每个Partition副本都完整的存储在kafka节点上。Partition以及Partition副本由一系列的Segment和索引文件组成。
kafka模型的优点在于简单快捷。所有读写都是顺序的。不好的是,单个节点必须有足够的磁盘空间来处理副本,因此非常大的副本可能会迫使你是用非常大的磁盘。第二个缺点是,在集群扩展时必须做Rebalance。这个过程是比较痛苦的,需要良好的计划和执行来保证没有任何故障的情况下分散节点的存储压力。
回到Pulsar + BookKeeper模型。Topic中的数据分布在多个Bookies上。Topic被分割成Ledgers,Ledgers被分割成Fragments分布在Fragment使用的Bookies上。当需要做集群扩展时,只需添加更多Bookies,它们就会在创建新的Fragment时开始在的Bookies上写入数据,不再需要kafka的Rebalance操作。但是,读取和写入现在在Bookies之间跳跃。我们很快将看到Pulsar是如何管理的。
但现在每个Pulsar Broker都需要跟踪每个Topic所包含的Ledgers和Fragments。这个元数据存储在Zookeeper中,如果丢失了将会遇到非常严重的问题。
在存储层中,我们往BookKeeper集群中均匀的写入一个Topic的数据。我们避免了将Topic或者副本的数据整体写到一个特定节点的缺陷,这避免了痛苦的Rebalance。