RibbitMQ进阶学习笔记
本文内容基本全部来自《RabbitMQ实战》,代码是java。
消息何去何从
mandatory参数
mandatory
是channel.basicPublish
方法中的一个参数。当mandatory
参数为true
时,且交换器无法根据自身的类型和路由键找到一个符合条件的队列时,RabbitMQ
会调用Basic.Return
命令将消息返回给生产者。当mandatory
参数为false
时,且交换器找不到对应队列时,消息直接被丢弃。
注:可以通过 channel.addReturnListener 来监听未被路由到队列的消息
immediate参数
简单的说,当immediate
参数为true
时,不光要路由到队列并且队列要存在消费者,才能被正常投递消息,否则就会调用Basic.Return
命令将消息返回给生产者。
注:RabbitMQ3.0版本开始去掉了immediate参数的支持。immediate参数会影响镜像队列的性能,增加了代码复杂性,建议采用TTL和DLX的方法替代。
备份交换器
英文名Alternate Exchange
。生产者在发送消息的时候如果不设置mandatory
参数,那么消息在未被路由的情况下,且没有添加channel.addReturnListener
时,消息将会丢弃。为了防止出现这种情况就可以使用备份交换器,在申明交换器时指定alternate-exchange
参数,参数值就是备份交换机的名字。
使用备份交换器注意点:
- 如果设置的备份交换器不存在,客户端和RabbitMQ服务端都不会有异常出现,此时消息会丢失。
- 如果备份交换器没有绑定任何队列,客户端和RabbitMQ服务端都不会有异常出现,此时消息会丢失。
- 如果备份交换器没匹配到任何队列,客户端和RabbitMQ服务端都不会有异常出现,此时消息会丢失。
- 如果备份交换器和
mandatory
参数一起使用,那么mandatory
参数无效。
过期时间(TTL)
TTL,Time to Live的检查,即过期时间。RabbitMQ可以对消息和队列设置TTL。
设置消息的TTL
- 通过队列的属性
x-message-ttl
设置,队列中所有消息都有相同的过期时间。单位:毫秒
- 对消息本身进行单独设置(在
channel.basicPublich
方法中加入expiration
参数),每条消息的TTL可以不同。单位:毫秒
对于第一种设置队列TTL属性的方法,一旦消息过期,就会从队列中抹去。而在第二种方法中,消息过期,也不会马上冲队列中抹去,每条消息是否过期是在即将投递到消费者之前判定的,这点和redis
中key过期机制相似
注:两种方法一起使用,以较小的TTL为准。如果不设置TTL,则表示此消息不会过期;如果将TTL设置为0,则表示除非此时可以直接将消息投递到消费者,否则该消息会丢弃
设置队列的x-expires
通过channel.queueDeclare
方法,设置x-expires
为1000,则表示该队列如果在1秒钟之内未使用则会被删除。
死信队列
DLX,全称为Dead-Letter-Exchange,中文名死信交换器。当消息在一个队列中编程死信dead message
之后,它能被重新发送到另一个交换器中,这个交换器就是DLX,绑定DLX的队列就称之为死信队列。
消息变成死信一般是由于以下几种情况:
- 消息被拒绝(
Basic.Reject
/Basic.Nack
),并且设置request
参数为false
; - 消息过期;
- 队列达到最大长度;
DLX其实就是一个普通的交换器,通过在channel.queueDeclare
方法中设置x-dead-letter-exchange
参数来为这个队列添加DLX。设置x-dead-letter-routing-key
的值表示死信
进入DLX时用的路由键。
延迟队列
在AMQP协议中,或者RabbitMQ本身没有直接延迟队列的功能,但是可以通过前面所介绍的DLX和TTL模拟出延迟队列的功能。
例:普通队列A
关联了一个死信队列B
,代码中只消费队列B
中的消息;现在往队列A
中发布一个过期时间为10秒的消息,10秒后就可以从队列B
中消费此消息。这样就模拟出了延迟队列的功能。
优先级队列
顾名思义,高优先级的队列有高的优先权,优先级高的消息优先被消费。队列优先级设置x-max-priority
,消息优先级设置priority
,值均在0-255之间。(超过按队列的最大优先级计算?) RabbitMQ3.5才有此功能。
RPC实现
RPC,全称Remote Procedure Call,中文名远程过程调用。它是一种通过网络从远程计算机上请求服务,而不需要了解底层网络的技术。
RabbitMQ中的实现原理:
- 客户端监听
队列A
- 客户端发起一个调用,消息进入
队列B
;消息携带了两个重要属性,回调队列A
和消息的标识
,回调队列A
也就是客户端监听的队列A
- 服务端监听
队列B
,收到调用信息后,服务端进行相应的cpu计算或io计算,然后将结果存入队列A
(含消息的标识
) - 客户端从
队列A
中取出消息并根据消息的标识
就可以知道此消息是哪一个调用的结果。
持久化
RabbitMQ的持久化分为:交换器的持久化、队列的持久化和消息的持久化。
如果把不设置交换器持久化(durable
参数),RabbitMQ服务重启之后,相关的交换器元数据会丢失,消息并不会。
如果把不设置队列持久化(durable
参数),RabbitMQ服务重启之后,相关的队列元数据会丢失,消息也会丢失,消息是存在队列中的。
队列持久化并不能保证消息的不丢失,要确保消息不丢失,需要设置为持久化(设置BasicProperties
中的deliveryMode
属性为2)。
生产者确认
RabbitMQ确保消息正确的到达服务器有两种方式:
- 通过事务机制
- 通过 publisher confirm 机制
注:这两种方法互斥,也就是不能同时使用这两种方式,否则会抛异常。消息正确的到达服务器后,如果消息不能路由到队列,那么消息会丢失。
事务机制
事务机制的相关方法有三个,channel.txSelect
将当前的信道设置成事务模式,channel.txCommit
用于提交事务,channel.txRollback
用于事务回滚。
publisher confirm 机制
确认机制主要有:同步confirm、批量confirm和异步confirm(推荐
)
通过设置channel.confirmSelect()
将信道channel
设置成publisher confirm
模式,发送一条消息后,调用channel.waitForConfirms
便是同步confirm
;
发送多条消息后,再调用channel.waitForConfirms
进行confirm就是批量confirm
。相比同步confirm
,批量confirm
极大的提高了效率,但是如果出现Basic.Nack
或超时的情况,就需要将这一批次的消息全部重发,重发必然会导致消息重复,并且当消息经常丢失时,批量confirm
的性能是不升反降的。
(注:如果RabbitMQ因自身内部错误导致消息丢失,就会发送一条Basic.Nack
命令)
异步confirm
同样也得先调用channel.confirmSelect()
,然后调用channel.addConfirmListener
注册消息回调接口ConfirmListener
,这个接口包含两个方法:handleAck
和handleNack
,分别处理RabbitMQ回传的Basic.Ack(成功)
和Basic.Nack(失败)
。这两个方法都包含deliveryTag
参数(消息的唯一标识
)。我们需要为每个信道维护一个unconfirm
的消息集合,发消息时往集合里添加消息,成功时删除消息,失败时重发消息。
消费端要点介绍
channel
对象的void basicReject(long deliveryTag, boolean requeue)
方法用来告诉RabbitMQ拒绝这个消息;如果requeue=true
则会将这条消息重新存入队列。
void basicNack(long deliveryTag, boolean multiple , boolean requeue)
表示批量拒绝消息。muliple=true
时表示拒绝delivertTag编号之前
所有未被当前消费者确认的消息.
basicRecover
用来请求RabbitMQ重新发送还未被确认的消息。如果方法参数requeue=true
,则表示消息重新入队,这样将可能分配给不同消费者。如果requeue=false
则分配给当前消费者。如果不设置这个参数默认为true
当RabbitMQ队列拥有多个消费者时,队列收到的消息将以轮询round-robin
的分发方式发送给消费者。每条消息只会发送给订阅列表里的一个消费者。RabbitMQ不管消费者是否已经消费完消息。这显然不合理,可以通过channel.basicQos
方法设置获取消息的数量。方法中prefetchCount
表示信道上未被确认的消息数量。例:将此致设为2
,如果已存在2
条未确认的消息,则RabbitMQ不会向这个消费者发消息,等消费者确认了某条消息后,消费者又可以接受消息了。global
表示该设置是否对信道上的所有消费者有效。例:global=true,prefetchCount=2
表示信道上的所有消费者未确认的消息总共是2
条,global=false,prefetchCount=2
表示信道上的每一个消费者未确认的消息是2
条。prefetchSize
不知道什么意思,测试的时候如果此值不为0
就会抛异常!网上查资料说:prefetchSize
表示最多传输的内容的大小的限制,0为不限制,RabbitMQ没有实现此功能。
本文来自博客园,作者:寻己Tenleft,转载请注明原文链接:https://www.cnblogs.com/tenleft/articles/11547509.html