RabbitMQ第二天

一、RabbitMQ基础概念

1.定义

RabbitMQ是基于AMQP协议开发的一个MQ产品, 首先我们以Web管理页面为入口,来了解下RabbitMQ的一些基础概念,这样我们后续才好针对这些基础概念进行编程实战。

可以参照下图来理解RabbitMQ当中的基础概念:

2.虚拟主机 virtual host

这个在之前搭建时已经体验过了。RabbitMQ出于服务器复用的想法,可以在一个RabbitMQ集群中划分出多个虚拟主机,每一个虚拟主机都有AMQP的全套基础组件,并且可以针对每个虚拟主机进行权限以及数据分配,并且不同虚拟主机之间是完全隔离的。

3.连接 Connection

客户端与RabbitMQ进行交互,首先就需要建立一个TPC连接,这个连接就是Connection。

4.信道 Channel

一旦客户端与RabbitMQ建立了连接,就会分配一个AMQP信道 Channel。每个信道都会被分配一个唯一的ID。也可以理解为是客户端与RabbitMQ实际进行数据交互的通道,我们后续的大多数的数据操作都是在信道 Channel 这个层面展开的。

RabbitMQ为了减少性能开销,也会在一个Connection中建立多个Channel,这样便于客户端进行多线程连接,这些连接会复用同一个Connection的TCP通道,所以在实际业务中,对于Connection和Channel的分配也需要根据实际情况进行考量。

5.交换机 Exchange

这是RabbitMQ中进行数据路由的重要组件。消息发送到RabbitMQ中后,会首先进入一个交换机,然后由交换机负责将数据转发到不同的队列中。RabbitMQ中有多种不同类型的交换机来支持不同的路由策略。从Web管理界面就能看到,在每个虚拟主机中,RabbitMQ都会默认创建几个不同类型的交换机来。

交换机多用来与生产者打交道。生产者发送的消息通过Exchange交换机分配到各个不同的Queue队列上,而对于消息消费者来说,通常只需要关注自己感兴趣的队列就可以了。

6.队列 Queue

 队列是实际保存数据的最小单位。队列结构天生就具有FIFO的顺序,消息最终都会被分发到不同的队列当中,然后才被消费者进行消费处理。这也是最近RabbitMQ功能变动最大的地方。最为常用的是经典队列Classic。RabbitMQ 3.8.X版本添加了Quorum队列,3.9.X又添加了Stream队列。

6.1Classic 经典队列

这是RabbitMQ最为经典的队列类型。在单机环境中,拥有比较高的消息可靠性。

 在这个图中可以看到,经典队列可以选择是否持久化(Durability)以及是否自动删除(Auto delete)两个属性。

其中,Durability有两个选项,Durable和Transient。 Durable表示队列会将消息保存到硬盘,这样消息的安全性更高。但是同时,由于需要有更多的IO操作,所以生产和消费消息的性能,相比Transient会比较低。

Auto delete属性如果选择为是,那队列将在至少一个消费者已经连接,然后所有的消费者都断开连接后删除自己。后面的Arguments部分,还有非常多的参数,可以点击后面的问号逐步了解。

其实这时,应该结合kafka和RocketMQ这几个MQ产品,对队列有一个更全面的理解。在MQ当中,队列其实是MQ集群中的一个数据分片的最小单位。在MQ集群中,一个Topic会对应多个队列,而这些队列会均匀的分配到集群的各个节点当中。

6.2Quorum 仲裁队列

仲裁队列,是RabbitMQ从3.8.0版本,引入的一个新的队列类型,整个3.8.X版本,也都是在围绕仲裁队列进行完善和优化。仲裁队列相比Classic经典队列,在分布式环境下对消息的可靠性保障更高。官方文档中表示,未来会使用Quorum仲裁队列代替传统Classic队列。

关于Quorum的详细介绍见 https://www.rabbitmq.com/quorum-queues.html,这里只是对其中的重点进行下解读。

 Quorum是基于Raft一致性协议实现的一种新型的分布式消息队列,它实现了持久化,多备份的FIFO队列,主要就是针对RabbitMQ的镜像模式设计的。简单理解就是quorum队列中的消息需要有集群中多半节点同意确认后,才会写入到队列中。这种队列类似于RocketMQ当中的DLedger集群。这种方式可以保证消息在集群内部不会丢失。同时,Quorum是以牺牲很多高级队列特性为代价,来进一步保证消息在分布式环境下的高可靠。

从整体功能上来说,Quorum队列是在Classic经典队列的基础上做减法,因此对于RabbitMQ的长期使用者而言,其实是会影响使用体验的。它与普通队列的区别:

 从官方这个比较图就能看到,Quorum队列大部分功能都是在Classic队列基础上做减法,比如Non-durable queues表示是非持久化的内存队列。Exclusivity表示独占队列,即表示队列只能由声明该队列的Connection连接来进行使用,包括队列创建、删除、收发消息等,并且独占队列会在声明该队列的Connection断开后自动删除。

其中有个特例就是这个Poison Message(有毒的消息)。所谓毒消息是指消息一直不能被消费者正常消费(可能是由于消费者失败或者消费逻辑有问题等),就会导致消息不断的重新入队,这样这些消息就成为了毒消息。这些读消息应该有保障机制进行标记并及时删除。Quorum队列会持续跟踪消息的失败投递尝试次数,并记录在"x-delivery-count"这样一个头部参数中。然后,就可以通过设置 Delivery limit参数来定制一个毒消息的删除策略。当消息的重复投递次数超过了Delivery limit参数阈值时,RabbitMQ就会删除这些毒消息。当然,如果配置了死信队列的话,就会进入对应的死信队列。

Quorum队列更适合于 队列长期存在,并且对容错、数据安全方面的要求比低延迟、不持久等高级队列更能要求更严格的场景。例如:电商系统的订单,引入MQ后,处理速度可以慢一点,但是订单不能丢失。

也对应以下一些不适合使用的场景:

1、一些临时使用的队列:比如transient临时队列,exclusive独占队列,或者经常会修改和删除的队列。

2、对消息低延迟要求高: 一致性算法会影响消息的延迟。

3、对数据安全性要求不高:Quorum队列需要消费者手动通知或者生产者手动确认。

4、队列消息积压严重 : 如果队列中的消息很大,或者积压的消息很多,就不要使用Quorum队列。Quorum队列当前会将所有消息始终保存在内存中,直到达到内存使用极限。

6.3Stream队列

Stream队列是RabbitMQ自3.9.0版本开始引入的一种新的数据队列类型,也是目前官方最为推荐的队列类型。这种队列类型的消息是持久化到磁盘并且具备分布式备份的,更适合于消费者多,读消息非常频繁的场景。

Stream队列的官方文档地址: https://www.rabbitmq.com/streams.html

Stream队列的核心是以append-only只添加的日志来记录消息,整体来说,就是消息将以append-only的方式持久化到日志文件中,然后通过调整每个消费者的消费进度offset,来实现消息的多次分发。下方有几个属性也都是来定义日志文件的大小以及保存时间。如果你熟悉Kafka或者RocketMQ,会对这种日志记录消息的方式非常熟悉。这种队列提供了RabbitMQ已有的其他队列类型不太好实现的四个特点:

1、large fan-outs 大规模分发

当想要向多个订阅者发送相同的消息时,以往的队列类型必须为每个消费者绑定一个专用的队列。如果消费者的数量很大,这就会导致性能低下。而Stream队列允许任意数量的消费者使用同一个队列的消息,从而消除绑定多个队列的需求。

2、Replay/Time-travelling 消息回溯

RabbitMQ已有的这些队列类型,在消费者处理完消息后,消息都会从队列中删除,因此,无法重新读取已经消费过的消息。而Stream队列允许用户在日志的任何一个连接点开始重新读取数据。

3、Throughput Performance 高吞吐性能

Strem队列的设计以性能为主要目标,对消息传递吞吐量的提升非常明显。

4、Large logs 大日志

RabbitMQ一直以来有一个让人诟病的地方,就是当队列中积累的消息过多时,性能下降会非常明显。但是Stream队列的设计目标就是以最小的内存开销高效地存储大量的数据。

整体上来说,RabbitMQ的Stream队列,其实有很多地方借鉴了其他MQ产品的优点,在保证消息可靠性的基础上,着力提高队列的消息吞吐量以及消息转发性能。因此,Stream也是在视图解决一个RabbitMQ一直以来,让人诟病的缺点,就是当队列中积累的消息过多时,性能下降会非常明显的问题。RabbitMQ以往更专注于企业级的内部使用,但是从这些队列功能可以看到,Rabbitmq也在向更复杂的互联网环境靠拢,未来对于RabbitMQ的了解,也需要随着版本推进,不断更新。

但是,从整体功能上来讲,队列只不过是一个实现FIFO的数据结构而已,这种数据结构其实是越简单越好。而当前RabbitMQ区分出这么多种队列类型,其实极大的增加了应用层面的使用难度,应用层面必须有一些不同的机制兼容各种队列。所以,在未来版本中,RabbitMQ很可能还是会将这几种队列类型最终统一成一种类型。例如官方已经说明未来会使用Quorum队列类型替代经典队列,到那时,应用层很多工具就可以得到简化,比如不需要再设置durable和exclusive属性。虽然Quorum队列和Stream队列目前还没有合并的打算,但是在应用层面来看,他们两者是冲突的,是一种竞争关系,未来也很有可能最终统一保留成一种类型。至于未来走向如何,我们可以在后续版本拭目以待。

二、RabbitMQ编程模型

 RabbitMQ的使用生态已经相当庞大,支持非常多的语言。而就以java而论,也已经支持非常多的扩展。我们接下来会从原生API、SpringBoot集成、SpringCloudStream集成,三个角度来详细学习RabbitMQ的编程模型。在学习编程模型时,要注意下,新推出的Stream队列,他的客户端跟另外两种队列稍有不同。

1.原生API

使用RabbitMQ提供的原生客户端API进行交互。先来了解下如何使用Classic和Quorum队列。至于Stream队列,目前他使用的是和这两个队列不同的客户端,所以会在后面一个章节单独讨论。

1.1maven依赖

1.2基础编程模型

 这些各种各样的消息模型其实都对应一个比较统一的基础编程模型。

step1、首先创建连接,获取Channel

step2、声明queue队列

关键代码:

api说明:

Declare a queue

Parameters:

queue the name of the queue

durable true if we are declaring a durable queue (the queue will survive a server restart)

exclusive true if we are declaring an exclusive queue (restricted to this connection)

autoDelete true if we are declaring an autodelete queue (server will delete it when no longer in use)

arguments other properties (construction arguments) for the queue

Returns:

a declaration-confirm method to indicate the queue was successfully declared

Throws:

java.io.IOException - if an error is encountered

See Also:

com.rabbitmq.client.AMQP.Queue.Declare

com.rabbitmq.client.AMQP.Queue.DeclareOk

如果要声明一个Quorum队列,则只需要在后面的arguments中传入一个参数,x-queue-type,参数值设定为quorum

注意:1、对于Quorum类型,durable参数就必须是true了,设置成false的话,会报错。同样,exclusive参数必须设置为false

如果要声明一个Stream队列,则 x-queue-type参数要设置为 stream 

注意:1、同样,durable参数必须是true,exclusive必须是false。 -- 你应该会想到,对于这两种队列,这两个参数就是多余的了,未来可以直接删除。

2、x-max-length-bytes 表示日志文件的最大字节数。x-stream-max-segment-size-bytes 每一个日志文件的最大大小。这两个是可选参数,通常为了防止stream日志无限制累计,都会配合stream队列一起声明。

声明的队列,如果服务端没有,那么会自动创建。但是如果服务端有了这个队列,那么声明的队列属性必须和服务端的队列属性一致才行。

step3、Producer根据应用场景发送消息到queue

关键代码:

api说明:

Publish a message. Publishing to a non-existent exchange will result in a channel-level protocol exception, which closes the channel. Invocations of Channel#basicPublish will eventually block if a resource-driven alarm is in effect.

Parameters:

exchange the exchange to publish the message to

routingKey the routing key

props other properties for the message - routing headers etc

body the message body

其中exchange是一个Producer与queue的中间交互机制。可以让Producer把消息按一定的规则发送到不同的queue,不需要的话就传空字符串。

step4、Consumer消费消息

定义消费者,消费消息进行处理,并向RabbitMQ进行消息确认。确认了之后就表明这个消息已经消费完了,否则RabbitMQ还会继续让别的消费者实例来处理。

主要收集了两种消费方式:

1、被动消费模式,Consumer等待rabbitMQ 服务器将message推送过来再消费。一般是启一个一直挂起的线程来等待。

关键代码:

api说明:

Start a non-nolocal, non-exclusive consumer, with a server-generated consumerTag.

Parameters:

queue the name of the queue

autoAck true if the server should consider messages acknowledged once delivered; false if the server should expect explicit acknowledgements

callback an interface to the consumer object

Returns:

the consumerTag generated by the server

其中autoAck是个关键,autoAck为true则表示消息发送到该Consumer后就被Consumer消费掉了,不需要再往其他Consumer转发;为false则会继续往其他Consumer转发。要注意如果每个Consumer一直为false,会导致消息不停的被转发,不停的吞噬系统资源,最终造成宕机。

2、另一种是主动消费模式,Comsumer主动到rabbitMQ服务器上去获取指定的messge进行消费。

关键代码:

api说明:

Retrieve a message from a queue using com.rabbitmq.client.AMQP.Basic.Get

Parameters:

queue the name of the queue

autoAck true if the server should consider messages acknowledged once delivered; false if the server should expect explicit acknowledgements

Returns:

a GetResponse containing the retrieved message data

Throws:

java.io.IOException - if an error is encountered

See Also:

com.rabbitmq.client.AMQP.Basic.Get

com.rabbitmq.client.AMQP.Basic.GetOk

com.rabbitmq.client.AMQP.Basic.GetEmpty

3、Stream队列消费,在当前版本下,消费Stream队列时,需要注意三板斧的设置。

a)channel必须设置basicQos属性。

b)正确声明Stream队列。

c)消费时需要指定offset。

具体参看示例代码。注意其中的注释。

这三点要尤其注意,因为当前版本的错误提示非常让人着急。

step5、完成以后关闭连接,释放资源

channel.close(); 

1.3官网的消息场景

原生API重点就是学习并理解RabbitMQ的官方消息模型。具体参见 https://www.rabbitmq.com/getstarted.html 。其中可以看到,RabbitMQ官方提供了总共七种消息模型,这其中,6 RPC部分是使用RabbitMQ来实现RPC远程调用,这个场景通常不需要使用MQ来实现,所以也就不当作重点来学习。而7 Publisher Confirms是当前版本新引进来的一种消息模型,对保护消息可靠性有很重要的意义。

1:hello world 体验

最直接的方式,P端发送一个消息到一个指定的queue,中间不需要任何exchange规则。C端按queue方式进行消费。

关键代码:(其实关键的区别也就是几个声明上的不同)

producer:

consumer:

2: Work queues 工作序列

这就是kafka同一groupId的消息分发模式

工作任务模式,领导部署一个任务,由下面的一个员工来处理。

producer:

consumer:

这个模式应该是最常用的模式,也是官网讨论比较详细的一种模式,所以官网上也对这种模式做了重点讲述。

首先。Consumer端的autoAck字段设置的是false,这表示consumer在接收到消息后不会自动反馈服务器已消费了message,而要改在对message处理完成了之后,再调用channel.basicAck来通知服务器已经消费了该message.这样即使Consumer在执行message过程中出问题了,也不会造成message被忽略,因为没有ack的message会被服务器重新进行投递。

但是,这其中也要注意一个很常见的BUG,就是如果所有的consumer都忘记调用basicAck()了,就会造成message被不停的分发,也就造成不断的消耗系统资源。这也就是 Poison Message(毒消息)

其次,官方特意提到的message的持久性。关键的message不能因为服务出现问题而被忽略。还要注意,官方特意提到,所有的queue是不能被多次定义的。如果一个queue在开始时被声明为durable,那在后面再次声明这个queue时,即使声明为 not durable,那这个queue的结果也还是durable的。

然后,是中间件最为关键的分发方式。这里,RabbitMQ默认是采用的fair dispatch,也叫round-robin模式,就是把消息轮询,在所有consumer中轮流发送。这种方式,没有考虑消息处理的复杂度以及consumer的处理能力。而他们改进后的方案,是consumer可以向服务器声明一个prefetchCount,我把他叫做预处理能力值。channel.basicQos(prefetchCount);表示当前这个consumer可以同时处理几个message。这样服务器在进行消息发送前,会检查这个consumer当前正在处理中的message(message已经发送,但是未收到consumer的basicAck)有几个,如果超过了这个consumer节点的能力值,就不再往这个consumer发布。

这种模式,官方也指出还是有问题的,消息有可能全部阻塞,所有consumer节点都超过了能力值,那消息就阻塞在服务器上,这时需要自己及时发现这个问题,采取措施,比如增加consumer节点或者其他策略:

Note about queue size If all the workers are busy, your queue can fill up. You will want to keep an eye on that, and maybe add more workers, or have some other strategy.

另外官网上没有深入提到的,就是还是没有考虑到message处理的复杂程度。有的message处理可能很简单,有的可能很复杂,现在还是将所有message的处理程度当成一样的。还是有缺陷的,但是目前也只看到dubbo里对单个服务有权重值的概念,涉及到了这个问题。

3:Publish/Subscribe 订阅 发布 机制

type为fanout 的exchange:

这个机制是对上面的一种补充。也就是把preducer与Consumer进行进一步的解耦。producer只负责发送消息,至于消息进入哪个queue,由exchange来分配。如上图,就是把producer发送的消息,交由exchange同时发送到两个queue里,然后由不同的Consumer去进行消费。

关键代码 ===》 producer: //只负责往exchange里发消息,后面的事情不管。

receiver: //将消费的目标队列绑定到exchange上。

关键处就是type为”fanout” 的exchange,这种类型的exchange只负责往所有已绑定的队列上发送消息。

4:Routing 基于内容的路由

type为”direct” 的exchange

这种模式一看图就清晰了。 在上一章 exchange 往所有队列发送消息的基础上,增加一个路由配置,指定exchange如何将不同类别的消息分发到不同的queue上。

关键代码===> producer:

receiver:

5:Topics 话题

type为"topic" 的exchange

这个模式也就在上一个模式的基础上,对routingKey进行了模糊匹配

单词之间用,隔开,* 代表一个具体的单词。# 代表0个或多个单词。

关键代码===> producer:

receiver:

6:RPC 远程调用

远程调用是同步阻塞的调用远程服务并获取结果。

RPC远程调用机制其实并不是消息中间件的处理强项。毕竟消息队列机制很大程度上来说就是为了缓冲同步RPC调用造成的瞬间高峰。而RabbitMQ的同步调用示例,看着也确实怪怪的。并且,RPC远程调用的场景,也有太多可替代的技术会比用消息中间件处理得更优雅,更流畅。

官网上这一大堆说明,其实我觉得就是表明,叫你不要用消息中间件来做RPC。所以关于这个RPC调用功能,就不再多做解释了。代码实现可以参见官网,或者配套的示例代码。

7:Publisher Confirms 发送者消息确认

 RabbitMQ的消息可靠性是非常高的,但是他以往的机制都是保证消息发送到了MQ之后,可以推送到消费者消费,不会丢失消息。但是发送者发送消息是否成功是没有保证的。我们可以回顾下,发送者发送消息的基础API:Producer.basicPublish方法是没有返回值的,也就是说,一次发送消息是否成功,应用是不知道的,这在业务上就容易造成消息丢失。而这个模块就是通过给发送者提供一些确认机制,来保证这个消息发送的过程是成功的。

如果了解了这个机制就会发现,这个消息确认机制就是跟RocketMQ的事务消息机制差不多的。而对于这个机制,RocketMQ的支持明显更优雅。

发送者确认模式默认是不开启的,所以如果需要开启发送者确认模式,需要手动在channel中进行声明。

在官网的示例中,重点解释了三种策略:

a)发布单条消息

即发布一条消息就确认一条消息。核心代码:

channel.waitForConfirmsOrDie(5_000);这个方法就会在channel端等待RabbitMQ给出一个响应,用来表明这个消息已经正确发送到了RabbitMQ服务端。但是要注意,这个方法会同步阻塞channel,在等待确认期间,channel将不能再继续发送消息,也就是说会明显降低集群的发送速度即吞吐量。

官方说明了,其实channel底层是异步工作的,会将channel阻塞住,然后异步等待服务端发送一个确认消息,才解除阻塞。但是我们在使用时,可以把他当作一个同步工具来看待。

然后如果到了超时时间,还没有收到服务端的确认机制,那就会抛出异常。然后通常处理这个异常的方式是记录错误日志或者尝试重发消息,但是尝试重发时一定要注意不要使程序陷入死循环。

b)发送批量消息

之前单条确认的机制会对系统的吞吐量造成很大的影响,所以稍微中和一点的方式就是发送一批消息后,再一起确认。

核心代码:

这种方式可以稍微缓解下发送者确认模式对吞吐量的影响。但是也有个固有的问题就是,当确认出现异常时,发送者只能知道是这一批消息出问题了, 而无法确认具体是哪一条消息出了问题。所以接下来就需要增加一个机制能够具体对每一条发送出错的消息进行处理。

c)异步确认消息

实现的方式也比较简单,Producer在channel中注册监听器来对消息进行确认。核心代码就是一个:

按说监听只要注册一个就可以了,那为什么这里要注册两个呢?如果对照下RocketMQ的事务消息机制,这就很容易理解了。发送者在发送完消息后,就会执行第一个监听器callback1,然后等服务端发过来的反馈后,再执行第二个监听器callback2。

然后关于这个ConfirmCallback,这是个监听器接口,里面只有一个方法: void handle(long sequenceNumber, boolean multiple) throws IOException; 

这方法中的两个参数:

1、sequenceNumer:这个是一个唯一的序列号,代表一个唯一的消息。在RabbitMQ中,他的消息体只是一个二进制数组,并不像RocketMQ一样有一个封装的对象,所以默认消息是没有序列号的。而RabbitMQ提供了一个方法int sequenceNumber = channel.getNextPublishSeqNo());来生成一个全局递增的序列号。然后应用程序需要自己来将这个序列号与消息对应起来。没错!是的!需要客户端自己去做对应!

2、multiple:这个是一个Boolean型的参数。如果是false,就表示这一次只确认了当前一条消息。如果是true,就表示RabbitMQ这一次确认了一批消息,在sequenceNumber之前的所有消息都已经确认完成了。

对比下RocketMQ的事务消息机制,有没有觉得很熟悉,但是又很别扭?当然,考虑到这个对于RabbitMQ来说还是个新鲜玩意,所以有理由相信这个机制在未来会越来越完善。

2.SpringBoot集成

SpringBoot官方就集成了RabbitMQ,所以RabbitMQ与SpringBoot的集成是非常简单的。不过,SpringBoot集成RabbitMQ的方式是按照Spring的一套统一的MQ模型创建的,因此SpringBoot集成插件中对于生产者、消息、消费者等重要的对象模型,与RabbitMQ原生的各个组件有对应关系,但是并不完全相同。这一点需要在后续试验过程中加深理解。

2.1引入依赖

SpringBoot官方集成了RabbitMQ,只需要快速引入依赖包即可使用。RabbitMQ与SpringBoot集成的核心maven依赖就下面一个。

要特别注意下版本。我们这里采用的是SpringBoot的2.6.7版本的依赖发布包。不同版本下的配置方式会有变化。

然后所有的基础运行环境都在application.properties中进行配置。所有配置以spring.rabbitmq开头。通常按照示例进行一些基础的必要配置就可以跑了。关于详细的配置信息,可以参见RabbitProperties,源码中有各个字段说明。

如果需要更详细的配置资料,那就需要到官方的github仓库上去查了。但是国内访问github的这个速度,你是知道的。

2.2配置生产者

基础的运行环境参数以及生产者的一些默认属性配置都集中到了application.properties配置文件中。所有配置项都以spring.rabbitmq开头。

关于详细的配置信息,可以参见RabbitProperties类的源码,源码中有各个字段的简单说明。

如果需要更详细的配置资料,那就需要去官方的github仓库上去查了。github地址: https://github.com/spring-projects/spring-amqp 。但是国内访问github的速度,你知道的。

2.3声明队列

所有的exchange, queue, binding的配置,都需要以对象的方式声明。默认情况下,这些业务对象一经声明,应用就会自动到RabbitMQ上常见对应的业务对象。但是也是可以配置成绑定已有业务对象的。

业务对象的声明方式,具体请参见示例工程。

详细的属性声明,同样参见github仓库。

2.4使用RabbitmqTemplate对象发送消息

生产者的所有属性都已经在application.properties配置文件中进行配置。项目启动时,就会在Spring容器中初始化一个RabbitmqTemplate对象,然后所有的发送消息操作都通过这个对象来进行。

2.5使用@RabbitListener注解声明消费者

消费者都是通过@RabbitListener注解来声明。注解中包含了声明消费者队列时所需要的重点参数。对照原生API,这些参数就不难理解了。

但是当要消费Stream队列时,还是要重点注意他的三个必要的步骤:

1、channel必须设置basicQos属性。 channel对象可以在@RabbitListener声明的消费者方法中直接引用,Spring框架会进行注入。

2、正确声明Stream队列。 通过往Spring容器中注入Queue对象的方式声明队列。在Queue对象中传入声明Stream队列所需要的参数。

3、消费时需要指定offset。 可以通过注入Channel对象,使用原生API传入offset属性。

使用SpringBoot框架集成RabbitMQ后,开发过程可以得到很大的简化,所以使用过程并不难,对照一下示例就能很快上手。但是,需要理解一下的是,SpringBoot集成后的RabbitMQ中的很多概念,虽然都能跟原生API对应上,但是这些模型中间都是做了转换的,比如Message,就不是原生RabbitMQ中的消息了。使用SpringBoot框架,尤其需要加深对RabbitMQ原生API的理解,这样才能以不变应万变,深入理解各种看起来简单,但是其实坑很多的各种对象声明方式。

2.6关于Stream队列

在目前版本下,使用RabbitMQ的SpringBoot框架集成,可以正常声明Stream队列,往Stream队列发送消息,但是无法直接消费Stream队列了。

关于这个问题,还是需要从Stream队列的三个重点操作入手。SpringBoot框架集成RabbitMQ后,为了简化编程模型,就把channel,connection等这些关键对象给隐藏了,目前框架下,无法直接接入这些对象的注入过程,所以无法直接使用。

如果非要使用Stream队列,那么有两种方式,一种是使用原生API的方式,在SpringBoot框架下自行封装。另一种是使用RabbitMQ的Stream 插件。在服务端通过Strem插件打开TCP连接接口,并配合单独提供的Stream客户端使用。这种方式对应用端的影响太重了,并且并没有提供与SpringBoot框架的集成,还需要自行完善,因此选择使用的企业还比较少。

这里就不详细介绍使用方式了。关于Stream插件的使用和配置方式参见官方文档:https://www.rabbitmq.com/stream.html。配合Stream插件使用的客户端有Java和GO两个版本。其中Java版本客户端参见git仓库:https://github.com/rabbitmq/rabbitmq-stream-java-client 。

其实关于Stream队列,现在也不需要着急上手,只是把他当作一种特殊的队列类型,上手了解即可。因为一方面太新的技术,往往还需要小白鼠多多验证。另一方面,现在RabbitMQ多种队列并存的状态,在不久肯定会得到简化,到时候,应用层的使用方式也肯定会跟着变化。

推荐一篇博文 https://blog.csdn.net/roykingw/article/details/78404956 可以作为大家了解SpringBoot集成RabbitMQ的一个入门博文。

你可能感兴趣的:(RabbitMQ第二天)