明白Apache Pulsar工作原理

源码 2024-10-5 14:44:59 68 0 来自 中国
Apache Pulsar 是机动的发布-订阅消息体系(Flexible Pub/Sub messaging),采取分层分片架构。
发布-订阅消息体系

关于发布-订阅模型的概念,重要从多租户、机动的消息体系、云原生构架、分片的流(Segmented Streams)等方面来夸大 Apache Pulsar 的功能和特性。
多租户

租户和命名空间(namespace)是 Pulsar 支持多租户的两个焦点概念。
在租户级别,Pulsar 为特定的租户预留符合的存储空间、应用授权与认证机制。
在命名空间级别,Pulsar 有一系列的设置战略(policy),包罗存储配额、流控、消息逾期战略和命名空间之间的隔离战略。
机动的消息体系

Pulsar 做了队列模型和流模型的同一,在 Topic 级别只需保存一份数据,同一份数据可多次斲丧。以流式、队列等方式盘算差别的订阅模型大大提升了机动度。
云原生架构

Pulsar 使用盘算与存储分离的云原生架构,数据从 Broker 搬离,存在共享存储内部。上层是无状态 Broker,复制消息分发和服务;下层是恒久化的存储层 Bookie 集群。Pulsar 存储是分片的,这种构架可以制止扩容时受限定,实现数据的独立扩展和快速规复。
Segmented Streams

Pulsar 将无界的数据看作是分片的流,分片分散存储在分层存储(tiered storage)、BookKeeper 集群和 Broker 节点上,而对外提供一个同一的、无界数据的视图。其次,不必要用户显式迁移数据,镌汰存储本钱并保持近似无穷的存储。
1.png 计划焦点

保证不丢失消息(使用正确的设置且不是整个数据中央故障)
强次序性保证
可猜测的读写耽误
Apache Pulsar选择同等性而不是可用性就像BookKeeper和Zookeeper一样。Apache Pulsar尽统统积极保持同等性。
这篇文章中不会先容跨机房复制相干的内容,我们只关注一个集群。
Apache Pulsar在上层具有高级别的Topic(主题)和Subscription(订阅)的概念,在底层数据存储在二进制文件中,这些数据交织分布在多个服务器上的多个Topic。在其中包罗许多的细节部分。我个人以为把它分成差别的抽象层更轻易明白Apache Pulsar的架构计划,所以这就是我在这篇文章中要做的事故。
接下来我们按照下图,一层一层的举行分析。
第一层 - Topic、Subscription和Cursors

我们将要扼要先容Topic(主题)、Subsription(订阅)和Cursors(游标)的根本概念,不会包罗深条理的消息传递方式。

3.png 消息存储在Topic中。逻辑上一个Topic是日志布局,每个消息都在这个日志布局中有一个偏移量。Apache Pulsar使用游标来跟踪偏移量。生产者将消息发送到一个指定的Topic,Apache Pulsar保证消息一旦被确认就不会丢失(正确的设置和非整个集群故障的情况下)。
斲丧者通过订阅来斲丧Topic中的消息。订阅是游标(跟踪偏移量)的逻辑实体,而且还根据差别的订阅范例提供一些额外的保证
Exclusive(独享) - 一个订阅只能有一个消息者斲丧消息
Shared(共享) - 一个订阅中同时可以有多个斲丧者,多个斲丧者共享Topic中的消息
Fail-Over(灾备) - 一个订阅同时只有一个斲丧者,可以有多个备份斲丧者。一旦主斲丧者故障则备份斲丧者接受。不会出现同时有两个活泼的斲丧者。
一个Topic可以添加多个订阅。订阅不包罗消息的数据,只包罗元数据和游标。
Apache Pulsar通过答应斲丧者将Topic看做在斲丧者斲丧确认后删除消息的队列,大概斲丧者可以根据游标的回放来提供队列和日志的语义。在底层都使用日志作为存储模型。
假如没有对Topic设置数据保存战略(如今通过其命名空间,后面会提供Topic级别的设置),一旦一个Topic的全部订阅的游标都已经乐成斲丧到一个偏移量时,此偏移量前面的消息就会被主动删除。也就是说必要该Topic的全部订阅上得到斲丧确认。
但是,假如Topic设置了数据保存战略,已经斲丧确认的消息凌驾保存战略阈值(Topic的消息存储巨细、Topic中消息保存的时间)后会被删除。
斲丧者可以以单条大概累积的方式确认消息。累积确认会有更好的吞吐量,但是在消息斲丧失败后会引入重复的消息处置惩罚。注意,累积斲丧不实用于共享模式的订阅,由于累积确认是基于偏移量的。但是在客户端API中支持批量确认,如许会镌汰RPC调用次数来进步在共享模式下订阅竞争斲丧的吞吐量。
末了,有一些类似于kafka Topic的分区(Partition)。区别在于Apache Pulsar中的分区也是Topic。就像kafka一样,生产者可以轮询、hash大概明确指定分区来发送消息。
以上都是对上层概念的一些先容,下面我们将举行深入的研究。
第二层(1) - 逻辑存储模型

如今该先容Apache BookKeeper了。我将在Apache Pulsar的配景下讨论BookKeeper,只管BookKeeper是一个通用的分布式日志存储办理方案。
起首,BookKeeper将数据存储至集群中的节点上,每个BookKeeper节点称为Bookie。其次,Pulsar和BookKeeper都使用Apache Zookeeper来存储元数据和监控节点康健状态.
4.png 一个Topic实际上是一个ledgers流。Ledger本身就是一个日志。所以一系列的子日志(Ledgers)构成了一个父日志(Topic)。
Ledgers追加到一个Topic,条目(消息大概一组消息)追加到Ledgers。Ledger一旦关闭是不可变的。Ledger作为最小的删除单元,也就是说我们不能删除单个条目而是去删除整个Ledger。
Ledgers本身也被分解为多个Fragment。Fragment是BookKeeper集群中最小的分布单元(depending on your perspective, striping might invalidate that claim)
Topic是Pulsar中的概念。Ledger和Fragment是BookKeeper中的概念,只管Pulsar知道且使用Ledgers和Fragment。
每个Ledger(由一个或多个Fragment构成)可以跨多个BookKeeper节点(Bookies)举行复制,以实现数据容灾和提升读取性能。每个Fragment都在一组差别的Bookies中复制(存在富足的Bookies)。
6.png 每个Ledger有三个关键设置:
Ensemble Size (E)
Write Quorum Size (Qw)
Ack Quorum Size (Qa)
这些设置可以应用到Topic级别,然后pulsar会在Topic使用的BookKeeper Ledgers/Fragments上设置。
注意: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)。
Write Quorum (Qw) 是Pulsar将要写入的实际的Bookies数目。可以便是大概小于E。
Ack Quorum (Qa) 是确认写入Bookies的数目,Pulsar Broker将确认发送给客户端。为了同等性,Qa应该是:(Qw + 1) / 2 大概更大。
在实践中:
(Qa == Qw) 或
(Qa == Qw - 1) ---> 如许制止单节点响应痴钝而改善写入耽误。
终极,每个Bookie必须都继承写入。假如我们总是等候全部Bookies做出响应,我们大概由于由于单个Bookie响应痴钝带来的团体耽误上升。Pulsar究竟答应有可猜测的耽误。
当创建一个新的Topic大概Ledger滚动时会创建一个新的Ledger。Ledger在以下这些情况会发生滚动并创建新的Ledger:
已到达Ledger的巨细或时间限定。
Ledger的全部权(Pulsar Broker的全部权)发生变革(稍后会详细先容)。
以下情况会创建新的Fragment:
创建新的Ledger。
当前Fragment使用Bookies发生写入错误或超时。
当一个Bookies无法服务写入利用时,Pulsar Broker会创建一个新的Fragment,并确保写入的Bookies数目到达Qw(Write Quorum)值,会不绝的重试直到消息被恒久化。
通过前面的先容我们可以得到以下认识:
增长E以优化耽误和吞吐量。增长Qw断送吞吐量实现冗余。增长Qa提升数据的容灾但会增长耽误和单一节点响应慢导致的耽误增长。
E和Qw不是Bookies的列表。它们支持表明可为给定的Ledger服务的Bookies池有多大。Pulsar将在创建新的Ledger或Fragment时使用E和Qw。每个Fragment都有一组固定的Bookies且不可变。
添加一个新的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。
第二层(2)- Pulsar Broker 和 Topic 全部权

同样在第二层抽象层,我们有Pulsar Brokers。Pulsar Broker是无状态的,没有不能丢失的恒久化状态。它们与存储层分开。Bookeeper集群本身并不实验复制,每个Bookies只是一个跟随者被领导者人知做什么,领导人是Pulsar Broker。每个Topic都由一个Pulsar Broker拥有,该Broker提供Topic的全部读写利用。
当Pulsar Broker汲取到写入哀求时,它会对当前Topic的当前Fragment使用的Bookies实验写入。(这一段必要确认一下)。
在通常情况下,当前的Ledger中将有一个Fragment。一旦Broker承认写入(满足Qa),Pulsar Broker将向生产者客户端发送确认。
只有在全部之前消息都已经通过确认时(满足Qa)才华发送确认。假如对于给定的消息,Bookie响应错误大概根本没有响应,则Broker将在新的Bookies上创建新的Fragment(不包罗有题目的Bookie)。
请注意,Broker只会等候Bookies的Qa确认。
读取也是通过拥有此Topic的Broker。作为给定Topic的单一入口点,Broker知道那些偏移量已经安全的保存到BookKeeper中。它只必要从一个Bookie读取即可举行读取。我们将在第3层中看到它怎样使用缓存从其内存缓存中提供读利用而不必要将读取发送到BookKeeper。
8.png Pulsar Broker的康健状态由Zookeeper监控。当Broker不可用时(Zookeeper以为),会发生全部权变更。新的Broker成为Topic的全部者,全部客户端毗连都会被重定向到此Broker。此Topic的读写将由新的全部者提供服务。
BookKeeper有一个非常紧张的功能成为Fencing。Fencing保证了BookKeeper只有一个编写器(Pulsar Broker)可以写入Ledger。
工作原理如下:
Topic X 的当前拥有者(B1)不可用(通过Zookeeper)。
其他Broker(B2)将Topic X 的当前Ledger状态从OPEN修改为IN_RECOVERY。

  • B2向Ledger的当前Fragment的Bookies发送fence信息并等候(Qw-Qa) + 1个Bookies响应。收到此响应数后Ledger将变成fenced。假如旧的Broker仍旧处于活泼状态则无法再举行写入,由于无法得到Qa确认(由于fencing导致非常响应)。

  • B2然后从Fragment的Bookies得到他们末了确认的条目是什么。它必要最新条目的ID,然后从该点开始向前读。它确保从哪一点开始的全部条数(大概从前未向Pulsar Broker承认)都会被复制到Qw Bookies。一旦B2无法读取并复制任何条目,Ledger将完全规复。
  • B2将Ledger的状态更改为CLOSED。
  • B2如今可以创建新的Ledger并继承写入哀求。
这种架构的巨大之处在于,通过让领导人(Pulsar Broker)没有状态,BookKeeper的fencing特性可以很好的处置惩罚脑裂题目。没有脑裂,没有分歧,没有数据丢失。
第二层(3) - Cursor跟踪

每个Subscription都存储一个Cursor。Cursor是日志中的当前偏移量。Subscription将其Cursor存储至BookKeeper的Ledger中。这使Cursor跟踪可以像Topic一样举行扩展。
第三层(1) - Bookie 存储

Ledgers和Fragments是在Zookeeper中维护和跟踪的逻辑布局。物理上数据不存储在Ledgers和Fragments对应的文件中。BookKeeper中存储的实现是可拔插的,Pulsar默认使用名称为DbLedgerStorage的存储实现。
当在Bookie上写入数据时,起首将该消息写入日志文件,这是一个预写日志(WAL),它可以资助BookKeeper在发生故障时制止数据丢失。它与关系型数据库恒久化保证的机制雷同。
写入利用也会写入缓存。写入的缓存会在内存中做积聚并定期举行排序和刷盘。对写入举行排序以便将同一Ledger的条目放在一起,从而进步读取性能。假如条目以严格的时间次序写入,在读取时无法使用磁盘的高效次序利用。通过聚合和排序,我们实现了Ledger级别的时间次序,这是我们关心的。
写入缓存还将条目写入RocksDB,存储每个条目的位置索引。它只是将(LedgerId,EntryId)映射到(EntryLogId,文件中的偏移量)。
由于写入缓存具有最新的消息,因此在读取时实验读取写缓存,假如没有掷中实验读取读缓存。假如两者都没有掷中,则会从RocksDB中查找条目的位置,然后在日志文件中读取该条目而且会更新到读缓存中以便后续哀求掷中缓存。这两层缓存意味着读取通常可以在内存中完成。
BookKeeper容许将磁盘IO做读写分离。写入都按次序写入日志文件可以存储在专用的磁盘上,而且可以批量刷盘以得到搞得吞吐量。除此之外从写入利用来看没有其他的同步磁盘IO利用,数据都是写入到内存的缓存区。
写缓存通过异步的方式批量将条目写入到日志文件和RocksDB,因此,一个磁盘用于同步写入日志文件,另一个磁盘用于异步写入条目和读取利用,
在读取的一边,读取利用由Read Cache或Log Entry文件和RocksDB提供。
还要考虑到写入会占满入口网络带宽,读取会占满出口网络带宽,但是他们不会相互影响。
优雅的实现了磁盘和网络中读和写的隔离。
第三层(2) - Pulsar Broker 缓存

每个Topic都有一个所属的Broker,所以读写都是通过该Broker举行的。如许提供了许多的利益。
起首,Broker可以将日志的尾部缓存在内存中,这意味着Broker可以不必要BookKeeper的情况下为读取尾部数据的利用提供服务。这比制止了网络的开销以及Bookie上大概的磁盘读取。
Broker也知道Last Add Confirmed条目的ID。如允许以跟踪那条消息是末了一个安全恒久化的消息。
当Broker的缓存中没有消息时将从消息地点的Fragment使用的一个Bookie哀求数据。如许大概必须负担额外的网络开销和大概的磁盘读取本钱。
总结

每个Topic都有一个归属的Broker。
每个Topic在逻辑上分解为Ledgers、Fragments和Entries。
Fragments分布在Bookie集群中。Topic与Bookie并不耦合。
Fragments可以跨多个Bookies带状分布。
当Pulsar Broker不可用时,该Broker持有的Topic全部权将转移至其他的Broker。Fencing机制制止了同一个Topic当前的Ledger同时有两个全部者(Broker)。
当Bookie不可用时,主动规复(假如启用)将主动举行数据重新复制到其他的Bookies。假如禁用,则可以手动启动此过程。
Broker缓存尾部消息日志,可以非常高效的为尾部读取利用提供服务。
Bookies使用Journal提供恒久化保证。该日志可用于故障规复时规复尚未写入Entry Log文件的数据。
全部Topic的的条目都保存在Entry Log文件中。查找索引保存在RocksDB中。
Bookies读取逻辑如下:Write Cache -> Read Cache -> Log Entry Files(RocksDB 作为索引)
Bookies可以通过单独的磁盘做IO读写分离。
Zookeeper存储Pulsar和BookKeeper的全部元数据。假如Zookeeper不可用整个Pulsar将不可用。
存储可以单独扩展。假如存储是瓶颈,那么只必要添加更多的Bookies,他们会主动负担负载,不必要Rebalance。
您需要登录后才可以回帖 登录 | 立即注册

Powered by CangBaoKu v1.0 小黑屋藏宝库It社区( 冀ICP备14008649号 )

GMT+8, 2024-11-21 21:42, Processed in 0.158853 second(s), 35 queries.© 2003-2025 cbk Team.

快速回复 返回顶部 返回列表