关于消息队列:博文推荐-下一代消息平台-Pulsar-到底是什么

22次阅读

共计 8032 个字符,预计需要花费 21 分钟才能阅读完成。

本文转载自社区用户:咖啡拿铁,猿辅导后端开发工程师,带你直击 Pulsar 的外围常识!Apache Pulsar 欢送大家踊跃踊跃投稿、与社区共同进步,也欢送大家与作者进行交换!
编辑:Tango@StreamNative

对于 Apache Pulsar

Apache Pulsar 是 Apache 软件基金会顶级我的项目,是下一代云原生分布式音讯流平台,集音讯、存储、轻量化函数式计算为一体,采纳计算与存储拆散架构设计,反对多租户、长久化存储、多机房跨区域数据复制,具备强一致性、高吞吐、低延时及高可扩展性等流数据存储个性。
GitHub 地址:http://github.com/apache/pulsar/

背景

Pulsar 是一个由 Yahoo!于 2016 年开源的消息中间件,2018 年成为 Apache 的顶级我的项目。在我之前的文章中写过很多其余消息中间件的文章,比方 Kafka,RocketMQ 等等,如果大家对于音讯队列不理解的能够浏览以下我之前的文章:

  • 你须要理解的 Kafka
  • 你应该晓得的 RocketMQ
  • 聊聊计算和存储拆散

在开源的业界曾经有这么多音讯队列中间件了,Pulsar 作为一个新权势到底有什么长处呢?Pulsar 自从出身就一直的再和其余的音讯队列(Kafka,RocketMQ 等等)做比拟,然而 Pulsar 的设计思维和大多数的音讯队列中间件都不同,具备了高吞吐,低提早,计算存储拆散,多租户,异地复制等性能,所以 Pulsar 也被誉为下一代音讯队列中间件,接下来我会一一对其进行具体的解析。

Pulsar 架构原理

整体的架构和其余的音讯队列中间件差异不是太大,置信大家也看到了很多相熟的名词,接下来会给大家一一解释这些名词的含意。

名词解释

  • Producer:音讯生产者,将音讯发送到 Broker;
  • Consumer:音讯消费者,从 Broker 读取音讯到客户端,进行生产解决;
  • Broker:能够看作是 Pulsar 的 Server,Producer 和 Consumer 都看作是 Client. 音讯解决的节点,Pulsar 的 Broker 和其它消息中间件不一样,它是无状态的没有存储,所以能够无限度的扩大,这个前面也会详解讲到;
  • Bookie::负责所有音讯的长久化,这里采纳的是 Apache Bookeeper;
  • ZK:和 Kafka 一样 Pulsar 也是应用 ZK 保留一些元数据,比方配置管理,Topic 调配,租户等等;
  • Service Discovery:能够了解为 Pulsar 中的 Nginx,只用一个 url 就能够和整个 Broker 打交道,当然也能够应用本人的服务发送。客户端收回的读取,更新或删除主题的初始申请将发送给可能不是解决该主题的 Broker。如果这个 Broker 不能解决该主题的申请,Broker 将会把该申请重定向到能够解决主题申请的 Broker。

不论是 Kafka,RocketMQ,还是 Pulsar 作为音讯队列中间件最为重要的组成为以下三个局部:

  • Producer 是如何生产音讯,发送到对应的 Broker;
  • Broker 是如何解决音讯,将高效的长久化以及查问;
  • Consumer 是如何进行生产音讯。

前面会围绕这三个局部开展解说。

Producer 生产音讯

先简略看一下如何用代码进行音讯发送:

PulsarClient client = PulsarClient.create("pulsar://pulsar.us-west.example.com:6650"); 

 Producer producer = client.createProducer("persistent://sample/standalone/ns1/my-topic"); 

 // Publish 10 messages to the topic
for (int i = 0; i < 10; i++) {producer.send("my-message".getBytes());
}  
  • Step1:首先应用 url 创立一个 Client,这个 url 是 Service Discovery 的地址,如果应用单机模式能够进行直连;
  • Step2:传入一个相似 url 的参数,只须要传递就能晓得在哪个 Topic 或者 Namespace 上面创立的:

url 的格局为:{persistent|non-persistent}://tenant/namespace/topic

下面三个步骤中,步骤 1,2 属于筹备阶段,用于构建客户端,构建 Producer,而外围逻辑在 Send 中,那这里先提几个小问题,大家能够先想想在其它音讯队列中是怎么做的,而后再比照 Pulsar 的看一下:

  • 调用了 Send 之后是会立刻发送吗?
  • 如果是多 Partition,怎么找到?应该发送到哪个 Broker 呢?

发送模式

咱们下面说了 Send 分为 Async 和 Sync 两种模式,但实际上在 Pulsar 外部 Sync 模式也是采纳的 Async 模式,在 Sync 模式下模仿回调阻塞,达到同步的成果,这个在 Kafka 中也是采纳的这个模式,然而在 RocketMQ 中,所有的 send 都是真正的同步,都会间接申请到 Broker。

基于这个模式,在 Pulsar 和 Kafka 中都反对批量发送,在 RocketMQ 中是间接发送,批量发送有什么益处呢?当发送的 TPS 特地高的时候,如果每次发送都间接和 Broker 直连,可能会做很多的反复工作,比方压缩,鉴权,创立链接等等。比方发送 1000 条音讯,那么可能会做 1000 次反复的工作,如果是批量发送的话这 1000 条音讯合并成一次申请,相对来说压缩,鉴权这些工作就只须要做一次。

有同学可能会问,批量发送会不会导致发送的工夫会有肯定的延误?这个其实不须要放心,在 Pulsar 中默认定时每隔 1ms 发送一次 Batch,或者当 Batchsize 默认到了 1000 都会进行发送,这个发送的频率都还是很快的。

发送负载平衡

在音讯队列中通常会将 Topic 进行程度扩大,在 Pulsar 和 Kafka 中叫做 Partition,在 RocketMQ 中叫做 Queue,实质上都是分区,能够将不同分区落在不同的 Broker 上,达到程度扩大的成果。

发送时能够本人制订抉择 Partition 的策略,也能够应用它默认轮询 Partition 策略。当抉择了 Partition 之后,怎么确定哪一个 Partition 对应哪一个 Broker 呢?

能够先看看上面这个图:

  • Step1:所有的信息分区映射信息在 ZK 和 Broker 的缓存中都有进行存储;
  • Step2:通过查问 Broker,能够获取到分区和 Broker 的关系,并且定时更新;
  • Step3:在 Pulsar 中每个分区在发送端的时候都被形象成为一个独自的 Producer,这个和 Kafka,RocketMQ 都不一样,在 Kafka 外面大略就是抉择了 Partition 之后而后再去找 Partition 对应的 Broker 地址,而后进行发送。Pulsar 将每一个 Partition 都封装成 Producer,在代码实现上就不须要去关注它具体对应的是哪个 Broker,所有的逻辑都在 Producer 这个代码外面,整体来说比拟洁净。

压缩音讯

消息压缩是优化信息传输的伎俩之一,咱们通常看见一些大型文件都会是以一个压缩包的模式提供下载,在音讯队列中也能够用这种思维,将一个 Batch 的音讯,比方有 1000 条可能有 1M 的传输大小,然而通过压缩之后可能就只会有几十 Kb,减少了咱们和 Broker 的传输效率,然而与之同时 cpu 也带来了损耗。Pulsar 客户端反对多种压缩类型,如 lz4、zlib、zstd、snappy 等。

client.newProducer()
    .topic(“test-topic”)
    .compressionType(CompressionType.LZ4)
    .create();

Broker

接下来咱们来说说第二个比拟重要的局部 Broker, 在 Broker 的设计中 Pulsar 和其余所有的音讯队列差异比拟大,而正是因为这个差异也成为了他的特点。

计算和存储拆散

首先来说说它最大的特点:计算和存储拆散。咱们在开始的说过 Pulsar 是下一代音讯队列,就十分得益于它的架构设计,无论是 Kafka 还是 RocketMQ, 所有的计算和存储都放在同一个机器上,这个模式有几个弊病:

  • 扩大艰难:当咱们须要扩大的集群的时候,通常是因为 cpu 或者磁盘其中一个起因影响,然而却要申请一个可能 cpu 和磁盘配置都很好的机器,造成了资源节约。并且 Kafka 这种进行扩大,还须要进行迁徙数据,过程非常繁冗;
  • 负载不平衡:当某些 Partion 数据特地多的时候,会导致 Broker 负载不平衡, 如上面图,如果某个 Partition 数据特地多,那么就会导致某个 Broker(轮船)承载过多的数据,然而另外的 Broker 可能又比拟闲暇。

Pulsar 计算拆散架构可能十分好的解决这个问题:

  • 对于计算:也就是咱们的 Broker,提供音讯队列的读写, 不存储任何数据,无状态对于咱们扩大十分敌对,只有你机器足够,就能轻易上。扩容 Broker 往往实用于减少 Consumer 的吞吐,当咱们有一些大流量的业务或者流动,比方电商大促,能够提前进行 Broker 的扩容;
  • 对于存储:也就是咱们的 Bookie,只提供音讯队列的存储,如果对音讯量有要求的,咱们能够扩容 Bookie,并且咱们不须要迁徙数据,扩容非常不便。

音讯存储

名词解析:

上图是 Bookie 的读写架构图,外面有一些名词须要先介绍一下:

  • Entry:Entry 是存储到 Bookkeeper 中的一条记录,其中蕴含 Entry ID,记录实体等;
  • Ledger:能够认为 Ledger 是用来存储 Entry 的,多个 Entry 序列组成一个 Ledger;
  • Journal:其实就是 Bookkeeper 的 WAL(Write Ahead Log),用于存 Bookkeeper 的事务日志,Journal 文件有一个最大大小,达到这个大小后会新起一个 Journal 文件;
  • Entry log:存储 Entry 的文件,Ledger 是一个逻辑上的概念,Entry 会先按 Ledger 聚合,而后写入 Entry log 文件中。同样,Entry log 会有一个最大值,达到最大值后会新起一个新的 Entry log 文件;
  • Index file:Ledger 的索引文件,Ledger 中的 Entry 被写入到了 Entry log 文件中,索引文件用于 Entry log 文件中每一个 Ledger 做索引,记录每个 Ledger 在 Entry log 中的存储地位以及数据在 Entry log 文件中的长度;
  • MetaData Storage:元数据存储,是用于存储 Bookie 相干的元数据,比方 Bookie 上有哪些 Ledger,Bookkeeper 目前应用的是 ZK 存储,所以在部署 Bookkeeper 前,要先有 ZK 集群。

整体架构上的写流程为:

  • Step1:Broker 发动写申请,首先对 Journal 磁盘写入 WAL,相熟 MySQL 的敌人晓得 Redolog,Journal 和 Redolog 作用一样都是用于复原没有长久化的数据;
  • Step2:而后再将数据写入 Index 和 Ledger,这里为了放弃性能不会间接写盘,而是写 Pagecache,而后异步刷盘;
  • Step3:对写入进行 Ack。

读流程为:

  • Step1:先读取 Index,当然也是先读取 Cache,再走 Disk;
  • Step2:获取到 Index 之后,依据 Index 去 Entry logger 中去对应的数据。

如何高效读写?

在 Kafka 中当咱们的 Topic 变多了之后,因为 Kafka 一个 Topic 一个文件,就会导致咱们的磁盘 IO 从程序写变成随机写。在 RocketMq 中尽管将多个 Topic 对应一个写入文件,让写入变成了程序写,然而读取很容易导致咱们的 Pagecache 被各种笼罩刷新,这对于 IO 的影响是十分大的。所以 Pulsar 在读写两个方面针对这些问题都做了很多优化:

  • 写流程:程序写 + Pagecache。在写流程中所有的文件都是独立磁盘,并且同步刷盘的只有 Journal,Journal 是程序写一个 Journal-wal 文件,程序写效率十分高。Ledger 和 Index 尽管都会存在多个文件,然而咱们只会写入 Pagecache,异步刷盘,所以随机写不会影响咱们的性能;
  • 读流程:Broker Cache + Bookie Cache,在 Pulsar 中对于追尾读(tailing read)十分敌对根本不会走 IO,个别状况下咱们的 Consumer 是会立刻去拿 Producer 发送的音讯的,所以这部分在长久化之后仍然在 Broker 中作为 Cache 存在,当然就算 Broker 没有 Cache(比方 Broker 是新建的),咱们的 Bookie 也会在 Memtable 中有本人的 Cache,通过多重 Cache 缩小读流程走 IO。

咱们能够发现在最现实的状况下读写的 IO 是齐全隔离开来的,所以在 Pulsar 中能很容易就反对百万级 Topic,而在 Kafka 和 RocketMQ 中这个是十分艰难的。

有限流式存储

Topic 实际上是一个 Ledgers 流(Segment),通过这个设计所以 Pulsar 它并不是一个单纯的音讯队列零碎,也能够代替流式零碎,所以也叫流原生平台, 能够代替 Flink 等零碎。

能够看见咱们的 Event Stream(Topic/Partition),由多个 Segment 存储组成,而每个 Segment 由 Entry 组成,这个能够看作是咱们每零售送的音讯通常会看成一个 Entry。

Segment 能够看作是咱们写入文件的一个根本维度,同一个 Segment 的数据会写在同一个文件下面,不同 Segment 将会是不同文件,而 Segment 之间的在 Metadata 中进行保留。

分层存储

在 Kafka 和 RocketMQ 中音讯是会有肯定的保留工夫的,因为磁盘会有空间限度,在 Pulsar 中也提供这个性能,然而如果想让本人的音讯永恒存储,那么能够应用分级存储,能够将一些比拟老的数据,定时刷新到便宜的存储中,比方 S3, 就能够有限存储咱们的音讯队列了。

数据复制

在 Pulsar 中的数据复制和 Kafka,RocketMQ 都有很大的不同,在其它音讯队列中通常是其它正本被动同步,通常这个工夫就会变得不可预测,而在 Pulsar 采纳了相似 Qurom 协定,给一组可用的 Bookie 池,而后并发的写入其中的一部分 Bookie,只有返回局部胜利(通常大于 1/2)就好。

  • Ensemble Size(E)决定给定 Ledger 可用的 Bookie 池大小;
  • Write Quorum Size(Qw)指定 Pulsar 向其中写入 Entry 的 Bookie 数量;
  • Ack Quorum Size(Qa)指定必须 Ack 写入的 Bookie 数量。

采纳这种并发写的形式,会更加高效的进行数据复制,尤其是当数据正本比拟多的时候。

Consumer

接下来咱们来聊聊 Pulsar 中最初一个比拟重要的组成 Consumer

订阅模式

订阅模式是用来定义咱们的音讯如何调配给不同的消费者,不同音讯队列中间件都有本人的订阅模式,个别咱们常见的订阅模式有:

  • 集群模式:一条音讯只能被一个集群内的消费者所生产;
  • 播送模式:一条音讯能被集群内所有的消费者生产。

在 Pulsar 中提供了 4 种订阅模式,别离是独占、灾备、共享、键共享:

  • 独占:顾名思义只能由一个消费者独占,如果同一个集群内有第二个消费者去注册,第二个就会失败,这个实用于全局有序的音讯。
  • 灾备:加强版独占,如果独占的那个挂了,会主动的切换到另外一个好的消费者,然而还是只能由一个独占;
  • 共享模式:这个模式看起来有点像集群模式,一条音讯也是只能被一个集群内消费者生产,然而和 RocketMQ 不同的是,RocketMQ 是以 Partition 维度,同一个 Partition 的数据都会被发到一个机器上。在 Pulsar 中生产不会以 Partition 维度,而是轮询所有消费者进行音讯发送。这有个什么益处呢?如果你有 100 台机器,然而你只有 10 个 Partition 其实你只有 10 台消费者能运行,然而在 Pulsar 中 100 台机器都能够进行生产解决;
  • 键共享:相似下面说的 Partition 维度去发送,在 RocketMQ 中同一个 Key 的程序音讯都会被发送到一个 Partition,然而这里不会有 Partition 维度,而只是依照 Key 的 Hash 去调配到固定的 Consumer,也解决了消费者能力限度于 Partition 个数问题。

音讯获取模式

不论是在 Kafka 还是在 RocketMQ 中都是用 Client 定时轮询 Broker 获取音讯,这种模式叫做长轮询(Long-Polling)模式。这种模式有一个毛病网络开销比拟大,来计算一下 Consumer 被生产的时延,假如 Broker 和 Consumer 之间的一次网络延时为 R, 那么总共的工夫为:

  • 当某一条音讯 A 刚到 Broker 的,这个时候 Long-polling 刚好打包完数据返回,Broker 返回到 Consumer 这个工夫为 R;
  • Consumer 又再次发送 Request 申请,这个又为 R;
  • 将音讯 A 返回给 Consumer 这里又为 R。

如果只思考网络时延,能够看见这条音讯的生产时延大略是 3R,所以必须想点什么对其进行一些优化,有同学可能马上就能想到,音讯来了间接推送给 Consumer 不就对了,这下时延只会有一次 R, 这个就是常见的推模式,然而简略的推模式是有问题的,如果生产速度远远大于生产速度,那么推送的音讯必定会干爆咱们的内存,这个就是背压。那么如何解决背压呢?能够优化推送形式,将其变为动静推送,联合 Long-polling,在 Long-polling 申请时将 Buffer 残余空间告知给 Broker,由 Broker 负责推送数据。此时 Broker 晓得最多能够推送多少条数据,那么就能够管制推送行为,不至于冲垮

举个例子:
Consumer 发动申请时 Buffer 残余容量为 100,Broker 每次最多返回 32 条音讯,那么 Consumer 的这次 long-polling 申请 Broker 将在执行 3 次 Push(共 Push 96 条音讯) 之后返回 response 给 Consumer(Response 蕴含 4 条音讯)。

如果采纳 Long-polling 模型,Consumer 每发送一次申请 Broker 执行一次响应,这个例子须要进行 4 次 Long-polling 交互(共 4 个 Request 和 4 个 Response,8 次网络操作;Dynamic Push/Pull 中是 1 个 Request,三次 Push 和一个 Response,共 5 次网络操作)。

所以 Pulsar 就采纳了这种音讯获取模式,从 Consumer 层进一步优化音讯达到工夫。我感觉这个设计十分奇妙,很多中间件的这种 Long-polling 模式都能够参考这种思维去做一个改善。

总结

Apache Pulsar 很多设计思维都和其它中间件不一样,但无疑于其更加贴近于将来,大胆预测一下其它的一些消息中间件将来的倒退也都会向其聚拢,目前国内的 Pulsar 使用者也是越来越多,腾讯云提供了 Pulsar 的云版本 TDMQ,当然还有一些其它的出名公司华为、知乎、虎牙等等有都在对其做一个逐渐的尝试,我置信 Pulsar 真的是一个趋势。最初也让我想起了最近《大江大河》大结局的一句话:

所有的变动, 都可能随同着苦楚和弯路, 凋谢的路线, 也不会是阔野坦途, 但大江大河, 奔涌向前的趋势, 不是任何险滩暗礁, 可能阻挡的。道之所在,虽千万人吾往矣。

参考资料

push or pull?

架构决策之消息中间件 -Pulsar

相干浏览

· 译文|Apache Pulsar 性能调优之架构

·  Pulsar 读写过程的性能调优

· 为什么说 Pulsar 是云原生的音讯平台?

Pulsar 2020 用户考察流动行将截止,没有填写的小伙伴不要错失为 Pulsar 提倡议的良机???? 赶快扫描下方二维码填写,有机会取得新版 Pulsar 社区周边哦!


正文完
 0