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

浙公网安备 33010602011771号