专栏名称: 51CTO技术栈
有趣 | 有料 | 有内涵,为您提供最优质的内容,愿我们一起悦享技术,成就人生。
目录
相关文章推荐
51好读  ›  专栏  ›  51CTO技术栈

再见,Kafka!再见,RocketMQ!

51CTO技术栈  · 公众号  · 程序员  · 2021-01-20 18:05

正文

送福利啦

关注 鸿蒙技术社区 ,回复 【鸿蒙】 价值 399元 鸿蒙智能小车 开发套件 (数量有限,先到先得) ,还可以 免费下载 鸿蒙 入门资料


👇 扫码 立刻关注 👇

专注开源技术,共建鸿蒙生态


Pulsar 是一个由 Yahoo 公司于 2016 年开源的消息中间件,2018 年成为 Apache 的顶级项目。


图片来自 Pexels


在开源的业界已经有这么多消息队列中间件了,Pulsar 作为一个新势力到底有什么优点呢?


Pulsar 自从出身就不断的再和其他的消息队列(Kafka,RocketMQ 等等)做比较。


但是 Pulsar 的设计思想和大多数的消息队列中间件都不同,具备了高吞吐,低延迟,计算存储分离,多租户,异地复制等功能。


所以 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


Step3: 调用 Send 方法发送消息,这里也提供了 sendAsync 方法支持异步发送。

上面三个步骤中,步骤 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 和其他所有的消息队列差别比较大,而正是因为这个差别也成为了他的特点。


计算和存储分离


首先我们来说说他最大的特点:计算和存储分离。







请到「今天看啥」查看全文