RabbitMQ学习笔记 - 消费端注意点

参考:

消费者客户端通过推模式或拉模式来获取并消费消息,这里RabbitMQ消费端来说,还有以下一些点需要注意:

  • 消息分发
  • 消息顺序性

一、消息分发

当RabbitMQ拥有多个消费者时,队列收到消息将以轮循(round-robin)的方式发送给消费者。每条消息只会发送给订阅列表里的一个消费者。

默认情况下,如果有n个消费者,那么RabbitMQ会将第m条消息分发给第 m%n 个消息者,RabbitMQ不管消费者是否消费并已经确认了(Basic.Ack)了消息。

可能出现的问题:假如某些消费者因为某些原因很快处理完消息,而有些消费者来不及处理造成任务堆积,这样就会造成处理快的消息者进程空间,进而整体应用吞吐量降低。

可以使用channel.basicQos()方法,这个方法允许限制信道上的消费者所能保持的最大未确认消息的数量。例如,在订阅消费队列之前,消费端程序调用了channel.basicQos(5),之后订阅了某个队列进行消费。RabbitMQ会保存一个消费者的列表,每发送一条消息都会为对应的消费者计数,如果达到了所设定的上限,那么RabbitMQ就不会向这个消费者再发送任何消息,直到消费者确认了某条消息之后,RabbitMQ将相应的计数-1,之后消费者可以继续接收消息,直到再次达到计数上限。

三个重载方法如下:
参数说明:

  • prefetchCount:信道上的消费者所能保持的最大未确认消息的数量,0表示没有上限
  • prefetchSize:表示消费者所能接收未确认消息的总体大小的上限,单位:B,0表示没有上限
  • global:默认值false
    • true:信道上所有的消费者需要遵从prefetchCount的限定值
    • false:信道上新的消费者需要遵从prefetchCount的限定值
void basicQos(int prefetchCount) throws IOException;
void basicQos(int prefetchCount, boolean global) throws IOException;
void basicQos(int prefetchSize, int prefetchCount, boolean global) throws IOException;

示例:
对于同一个信道上的多个消费者来说,如果设置了prefetchCount,那么两者都会生效;如果设置了两次channel.basicQos(3, false)channel.basicQos(5, true),那么二者配置都会生效:每个消费者最多只能收到3个未确认消息,两个消费者能收到的未确认的消息个数之和的上限为5。

注:

  • 如果使用两种global的模式,会增加RabbitMQ的负载,因为RabbitMQ需要更多的资源来协调完成这些限制
  • 如果没有特殊需求,最好设置global为false。
Channel channel = connection.createChannel();
channel.basicQos(10, false);

String queueName1 = "direct.queue.test.qos1";
channel.basicConsume(queueName1, false, consumer);

String queueName2 = "direct.queue.test.qos2";
channel.basicConsume(queueName2, false, consumer);

二、消息顺序性

消费的顺序性:指消费者消费到的消息和生产者发送的消息的顺序是一致的。如不考虑重复消息,生产者发送消息为msg1、msg2、msg3,那么消费者也是按照msg1、msg2、msg3的顺序进行消费。

2.1 RabbitMQ如何保证顺序性

主要思路有两种:

  • 单线程消费来保证消息的顺序性
  • 对消息进行编号,消费者处理时根据编号判断顺序

问题分析:

如下图所示,假设 data1 和 data2 是有顺序的,必须 data1 先执行,data2 后执行;

  • 问题1:如果多个生产者发送消息时,就不能保证一定是data1先到达RabbitMQ
  • 问题2:如果有多个消费者消费消息,这两个数据被不同的消费者消费到了,那么也不能保证data1先被消费
    RabbitMQ消息顺序问题

解决方案:

  • 在不使用任何RabbitMQ的高级特性,也没有消息丢失、网络故障之类异常的情况发生,并且只有一个消费者,最好也只有一个生产者的情况下可以保证消息的顺序性(单生产者、单消费者)
  • 多个生产者、只有一个消费者,然后消费者内部用内存队列对消息按照某种规则进行排序后再处理(多个生产者、一个消费者)。注:比如在消息体内添加全局有序标识(类似 Sequence ID) 来实现。

单生产者、单消费者消费模式如下图所示:
RabbitMQ消费顺序解决方案

2.2 破坏消息顺序性的几种情形

一些常见的情形:

  • 如果生产者使用了事务机制,在发送消息之后遇到异常进行了事务回滚,那么需要重新补偿发送这条消息,如果补偿发送是在另一个线程实现的,那么消息在生产者这个源头就出现了错序。
  • 如果启用 publisher confirm(发送方确认机制)时,在发生超时、中断,或者是收到RabbitMQ的 Basic.Nack 命令时,那么同样需要补偿发送,结果与事务机制一样会错序。
  • 如果生产者发送的消息设置了不同的超时时间,井且也设置了死信队列,整体上来说相当于一个延迟队列,那么消费者在消费这个延迟队列的时候,消息的顺序必然不会和生产者发送消息的顺序一致。
  • 如果消息设置了优先级,那么消费者消费到的消息也不是顺序性的。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值