Pulsar-Consumer

“Pulsar is a distributed pub-sub messaging platform with a very flexible messaging model and an intuitive client API.”

Pulsar是pub-sub模式的分布式消息平台,拥有灵活的消息模型和直观的客户端API。

Pulsar由雅虎开发并开源的下一代消息系统,目前是Apache软件基金会的孵化器项目。

本片文章简单介绍Pulsar的Consumer,包含以下内容:

  • Consumer的体系
  • 消费逻辑的实现

1. Consumer体系

A consumer is a process that attaches to a topic via a subscription and then receives messages.

Consumer通过订阅关系绑定到Topic(和Producer类似,都是绑定到一个Topic上),并接收消息。

Consumer支持:

  • 同步接收消息:阻塞用户线程等待消息
  • 异步接收消息:异步等待消息(通过Future返回消息)
  • 通过MessageListener返回消息:接收消息后回调用户的MessageListener

Consumer提供了三类获取消息的方式,其中异步的方式包含通过Future异步等待消息和通过MessageListener被动接收消息。MessageListener和另外两种方式是互斥的,一旦Consumer注册了MessageListener接口,则必须通过MessageListener处理消息,主动触发receive获取消息将抛出异常。

Consumer的继承关系:

  • Consumer:定义了消费者相关的接口
  • ConsumerBase:接口中基础方法的实现,抽象类
  • ConsumerImpl:在ConsumerBase基础上的Consumer具体实现
  • MultiTopicsConsumerImpl:组合多个ConsumerImpl完成对多Topic/Partition的消费

Consumer的设计和Producer是一致的,通过接口定义行为,基础类实现基本能力,在通过组合的方式来实现消费多个Topic/Partition(Producer则是像多个Topic发送消息)。

1.1 消费进度提交

Consumer处理消息后需要发送acknowledgement到Broker,这样Broker可以丢弃消息(应该是移动消费offset的操作,类似RocketMQ,并不是真正的删除消息)。支持单挑消息提交或者批量提交,批量提交则以最后一条消息的offset为准。(只是记录一个offset比较某个位置之前的消息都已经被Consumer处理,所以批量提交其实只是把最大的offset提交)

1.2 订阅模型

订阅模型决定了消息时如何被投递给Consumer的。在Pulsar中,订阅模型有: exclusive、shared、 failover。

Exclusive

只能有一个Consumer绑定到订阅关系上,其他Consumer尝试绑定到订阅关系上时会报错(Exclusive是默认的订阅模型)。

Shared

在Shared模型中,多个Consumer可以绑定到一个订阅关系上。消息按照round-robin模式被投递给各个Consumer。若某个Consumer宕机,被投递给该Consumer的未被ACK(没有acknowledgement)的消息将被重新投递给其他的Consumer进行消费。

Shared模式带来的限制:

  1. 消息时按照round-robin模式投递给各个Consumer的,所以消息顺序无法得到保障
  2. 同样因为round-robin模式,无法使用批量提交acknowledgement的功能(如上图Consumer C-3如果提交了m4会导致m3被标记为已经消费,但实际Consumer C1可能还没处理m3)

failover

在Failover模型中,多个Consumer可以绑定到一个订阅关系上,但是只有一个称为Master Consumer的消费者能消费消息。对多个Consumer按照name进行排序,第一个Consumer则为Master Consumer。

在Master Consumer失效(比如断开连接)后,Master Consumer未提交的消息和后续的消息会提交给后续的Consumer。

2. 消费逻辑的实现

Consumer获取消息的核心API有以下两个,分别实现同步获取消息和异步获取消息:

/**
* Receives a single message.
* <p>
* This calls blocks until a message is available.
*
* @return the received message
* @throws PulsarClientException.AlreadyClosedException
* if the consumer was already closed
* @throws PulsarClientException.InvalidConfigurationException
* if a message listener was defined in the configuration
*/
Message<T> receive() throws PulsarClientException; /**
* Receive a single message
* <p>
* Retrieves a message when it will be available and completes {@link CompletableFuture} with received message.
* </p>
* <p>
* {@code receiveAsync()} should be called subsequently once returned {@code CompletableFuture} gets complete with
* received message. Else it creates <i> backlog of receive requests </i> in the application.
* </p>
*
* @return {@link CompletableFuture}<{@link Message}> will be completed when message is available
*/
CompletableFuture<Message<T>> receiveAsync();

MessageListener则通过ConsumerBuilder接口进行设置并传入到Consumer的构造方法中。

这三个API都由ConsumerImpl#messageReceived触发,即Consumer接收到消息后根据请求的类型来决定:

  • 同步获取消息的,将消息放入内存队列,被挂起的线程会从队列中获取消息
  • 异步获取消息的,执行callback将消息放入future
  • 通过MessageListener处理消息的,通过ListenerExecutor执行逻辑

可见Pulsar在消费模式上处理是统一的,即无论客户端采用何种方式进行消息的接收,消息统一由服务端进行“推送”,而在Consumer内部根据用户请求的类型进行处理。

通过ConsumerImpl#messageReceived的实现可以发现Pulsar的消息消费是一种“推”的模型,这和RocketMQ的“拉”的模型差异是很大的(RocketMQ采用一种Long-Polling的方式,由Consumer主动发起请求从服务端获取数据,若服务端有需要处理的消息,请求立即返回;如果没有消息,这个请求会在服务单阻塞一段时间,直到新消息到达或者请求即将超时,返回给客户端)。

Consumer获取消息的模型

具体看Pulsar-Consumer获取消息的代码实现会发现它也不是一种纯粹的,类似淘宝Notify的推的模式,而是一种推拉结合的方式,示意如下:

  1. Consumer向Broker发送FLOW请求,通知Broker可以推送消息给Consumer
  2. Broker将消息通过MESSAGE请求将消息推送给Consumer

这是一个反复的过程,每次Consumer接收消息处理后都会继续发送FLOW请求给Broker。

这是在RocketMQ或者Kafka的设计中都没有的一种方式,这种方式进行一定的拓展则可以实现类似akka的Dynamic Push/Pull模式(详见公众号历史文章:《Push or Pull?》)。

在阅读Pulsar Consumer部分代码的时候还发现非常有趣的一点,当你搜索“Consumer”时会出现一个Consumer接口和一个Consumer类:

  • 接口: org.apache.pulsar.client.api.Consumer
  • 类: org.apache.pulsar.broker.service.Consumer

Consumer接口是Client模块定义Consumer行为的,为什么在Broker模块会有一个Consumer类?

实际在Broker端会给链接上来的Consumer构造一个对应的Consumer对象,维护远端的Consumer的链接等信息。所有对远端的Consumer的操作会封装在Broker端的Consumer中。这样可以更好的实现代码的可插拔性,降低耦合,提升代码的可测试性。比如在测试Broker端的逻辑时,只需要Mock一个Consumer类来模拟各种正常和网络异常的情况,而不需要真正的启动一个Consumer。

总结

本文主要是介绍一下Pulsar Consumer模块的相关概念和一些模型,没有深入的解读代码实现。Pulsar Consumer的实现方式还是非常有趣的,和大家比较熟悉的RocketMQ的实现方式差异比较大,值得一读。

Pulsar Consumer实现介绍的更多相关文章

  1. 深入Pulsar Consumer的使用方式&源码分析

    原文链接 1.使用前准备 引入依赖: <dependency> <groupId>org.apache.pulsar</groupId> <artifactI ...

  2. 最佳实践:Pulsar 为批流处理提供融合存储

    非常荣幸有机会和大家分享一下 Apache Pulsar 怎样为批流处理提供融合的存储.希望今天的分享对做大数据处理的同学能有帮助和启发. 这次分享,主要分为四个部分: 介绍与其他消息系统相比, Ap ...

  3. 分布式消息流平台:不要只想着Kafka,还有Pulsar

    摘要:Pulsar作为一个云原生的分布式消息流平台,越来越频繁地出现在人们的视野中,大有替代Kafka江湖地位的趋势. 本文分享自华为云社区<MRS Pulsar:下一代分布式消息流平台全新发布 ...

  4. 【Apache Pulsar】Apache Pulsar单机环境及Go语言开发环境搭建

    0x01 简介 Apache Pulsar是一个开源的分布式发布-订阅消息系统,与Kafka类似,但比后者更加强大.Pulsar最初由Yahoo开发并维护,目前已经成为Apache软件组织的一个孵化子 ...

  5. java8 函数式接口——Function/Predict/Supplier/Consumer

    Function 我们知道Java8的最大特性就是函数式接口.所有标注了@FunctionalInterface注解的接口都是函数式接口,具体来说,所有标注了该注解的接口都将能用在lambda表达式上 ...

  6. 用Jmeter测试RabbitMQ

    1.下载AMQP插件 github上面有源码,可以通过ant+ivy在本地进行打包(下载IDEA实践成功) https://github.com/jlavallee/JMeter-Rabbit-AMQ ...

  7. 转用Jmeter测试RabbitMQ

    转自:https://blog.csdn.net/luozhuwang/article/details/62044872 1.下载AMQP插件 github上面有源码,可以通过ant+ivy在本地进行 ...

  8. 将RabbitMq用好需要了解的一些基础知识

    本文面向有一定RabbitMq基础的童鞋. 首先,我们来理理RabbitMq的一些基本概念: Connection: 客户端与RabbitMq服务器节点的Tcp链接. Channel: 信道,因为一条 ...

  9. HSF简单实现记录(基于 Pandora Boot 开发)

    文章目录 声明 注意 安装轻量配置中心 启动轻量配置中心 配置 hosts 结果验证 开发工具准备 在 Maven 中配置 EDAS 的私服地址 验证配置是否成功 开发 demo下载 服务注册与发现 ...

随机推荐

  1. 【JAVA】学习笔记(2)

    Java完整的类的定义 [pubilc][abstact|final] class className [extends superclassName] [implements InterfaceNa ...

  2. JAVA微信公众号通过openid发送模板消息~

    1,问题产生 在微信公众号开发过程中,我们有时候做不同权限的时候,比如在注册的时候,需要审核,然后我们要想办法让对方知道审核的结果.这时候我们可以通过模板消息来通知. 2,第一步,首先在微信公众号上获 ...

  3. ios  国际化开发

    一,.xib 1.首先选中xib文件,在右边的inspector中选择对应的国际化语言,如下图

  4. Cookie的使用(14)

    一:cookie的简要介绍: (1)什么是cookie a.cookie是一种客户端的状态管理技术b.当浏览器向服务器发送请求的时候,服务器会将少量的数据以set-cookie消息头的方式发送给浏览器 ...

  5. OpenCV3.30 画图函数

    画图函数(Draw Functions)都放在imgpro. 例如C++中用: #include <opencv2\imgproc.hpp>

  6. appium安装问题集锦

    问题一: MacBook-Air:Cellar$ npm -v dyld: Library not loaded: /usr/local/opt/icu4c/lib/libicui18n.61.dyl ...

  7. 人工智能之一般合一算法Java实现之

    import java.util.ArrayList; import java.util.Arrays; import java.util.List; import java.util.Scanner ...

  8. javaWeb中MVC的编程思想示例

    没有学习MVC之前我只写了一个Servlet类(Note_List.java),分层之后,我将这个类分成了5个类(NoteDao.java,,NoteDaoImpl.java,,NoteService ...

  9. 搭建node js的运行环境。

    第一步:首先安装一个NVM,就是一个node的版本管理器. nvm的下载地址::https://github.com/coreybutler/nvm-windows/releases,我选择下载的是n ...

  10. Web browser发展演变

    浏览器是指可以显示网页服务器或者文件系统的HTML文件内容,并让用户与这些文件交互的一种软件.网页浏览器主要通过HTTP协议与网页服务器交互并获取网页,这些网页由URL指定,文件格式通常为HTML.大 ...