【RabbitMQ问题踩坑】RabbitMQ设置手动ack后,消息队列有多条消息,只能消费一条,就不继续消费了,这是为什么 ?

现象:我发送5条消息到MQ队列中,同时,我在yml中设置的是需要在代码中手动确认,但是我把代码中的手动ack给关闭了,会出现什么情况?

yml中配置,配置需要在代码中手动去确认消费者消费消息成功,
在这里插入图片描述
正常情况 需要在处理完消费的业务逻辑之后,通过channel.basicAck(tag, false);;来给MQ反馈消息已经被消费,也就是手动ack
在这里插入图片描述
这种正常的情况表现为: 我发送成功在postman中
在这里插入图片描述
其实被消费了已经,所以ready的数目为0,但是MQ还在等待ack回应,我这里注释了,所以在unacked这里会多一个;
在这里插入图片描述
此时如果队列中有3条消息的话,也就是ready中存在3条消息,unacked中存在一条,也就是total为4条,那及时其他的消息我修改代码,重新项目了,把注释的手动ack给放开了,那MQ中的情况也不会变,因为如果在代码中设置了消费者需要需要手动ack的话,如果一但存在了未ack的消息【unacked】的消息,那ready中都不会被消费

spring:
  rabbitmq: 
    listener:  #这个在测试消费多个消息的时候,不能有下面这些配置,否则只能消费一个消息后就不继续消费了
      simple:
        acknowledge-mode: manual  #指定MQ消费者的确认模式是手动确认模式  这个在消费者者模块配置  设置手动确认(ack)
        prefetch: 1 #一次只能消费一条消息   这个在消费者者模块配置

有个文献中的解析如下:
Rabbit 中的 channel.basicAck 注释掉,只能消费掉一个消息吗?

在 RabbitMQ 中,当你使用手动消息确认(manual message acknowledgment)模式时, channel.basicAck 方法用于向 RabbitMQ 发送一个确认消息,告知 RabbitMQ 某个消息已经被成功处理。如果你注释掉 channel.basicAck调用,那么 RabbitMQ 将不会收到这个消息已经被处理的确认,并且会根据你的消费者配置来决定如何处理这个消息。

具体行为取决于你的消费者配置中的 autoAck 参数(在某些客户端库中可能称为 noAck或其他名称)
1.如果 autoAck 被设置为 true (也就是不配置 acknowledge-mode: manual,默认是自动ack )
当你从队列中获取一个消息时,RabbitMQ 会立即认为这个消息已经被处理并自动将其从队列中删除。在这种情况下,即使你注释掉 channel.basicAck,RabbitMQ也不会等待任何确认,并且会继续发送下一个消息给消费者。因此,你可以消费掉多个消息,但请注意,如果消费者在处理消息时失败并崩溃,这些消息将会丢失(因为 RabbitMQ 认为它们已经被处理了)。
2.如果 autoAck 被为手动的话:(yml中的 手动ack配置为手动 acknowledge-mode: manual):
当你从队列中获取一个消息时,RabbitMQ 会等待消费者发送一个确认消息(即 channel.basicAck)。如果消费者注释掉了这个确认调用,RabbitMQ将不会收到任何确认,并且会根据你设置的 basicQos 方法中的 prefetchcount 参数来决定是否继续发送下一个消息。

  • 如果 prefetch 大于 1,RabbitMQ 可能会继续发送下一个消息给消费者,但请注意,消费者最多只会同时处理 prefetchcount个未确认的消息,
  • 如果 prefetch设置为1(或未设置,因为默认可能是 0,表示无限制,但这不是推荐的做法)RabbitMQ 将只发送一个消息给消费者,并等待该消息的确认。在这种情况下,如果你注释掉了消费者将只能消费一个消息,并且不会收到下一个消息,直到你发送确认或关闭连channel.basicAck接。

总之,是否只能消费一个消息取决于你的消费者配置(特别是 autoAck和 prefetch )。但是,强烈建议在手动消息确认模式下使用 acknowledge-mode: manual和合理的 prefetch值,以确保消息在处理失败时不会丢失,并控制消费者同时处理的消息数量。

我遇到的问题现象:
我在yml中是添加着需要手动ack的配置的,
操作产生这个问题的过程:
第一次消息将手动ack代码注释后产生了一个nacked的消息,这样就出现了其实被消费成功了,但是未被回应的一条消息!!注意这个条件
在这里插入图片描述
但是他未被回应:
在这里插入图片描述

	之后我将手动ack加上,重启代码,并向MQ中写入3条新的消息,正常按照我们的理解的话,这三天总应该都被消费了吧,但是即使我现在加上了手动ack了,但是由于第一条未ack的消息存在,所以后面的消息也不回被消费:产生了堵塞。现象如下:

在这里插入图片描述

	遇到这种情况,通过队列purg  message也是无法去掉unacked中的消息的,只能去掉ready中的消息
	我们该怎么将nacked中的消息去掉呢 ?

**答案:
****在yml中注释掉手动ack的配置,
在这里插入图片描述

这样重启项目后,消息队列里积压的无论是ready或者unnack的消息都会被消费掉(对于ready未被消费的,被消费没问题,但是对于之前消费成功了但是未被ack的,不能轻易让他被消费掉,因为我这是在自己实验的过程中,在生产中可不能这样操作,容易导致生产事故,导致事务的重复消费,除非你在自己的消费者端添加着“幂等性校验”,也就是之前被消费成功的,未ack的不允许重复消费的校验,这总情况你可以拦住他,所以可以这样搞,否则的话,不行!特别注意这里)**

我说的方法的实验效果:
前提:0个ready的,1个unacked的,一个total的,注释配置并重启之后:
console中正常被拦住了,而且MQ队列中unacked的也没了 ,OK啦
在这里插入图片描述
实验结果:
在这里插入图片描述
在这里插入图片描述

  • 23
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
RabbitMQ 中,同一个消息只能被一个消费消费。如果多个消费者同时消费同一个队列中的消息,每个消息只会被其中一个消费消费。 如果需要多个消费者同时处理同一个消息,可以使用 RabbitMQ 的“发布/订阅”模式。在这种模式下,每个消费者都订阅一个自己的队列,并且这些队列都绑定到同一个交换机上。当一个生产者发送一条消息时,这条消息会被交换机广播到所有绑定的队列中,每个消费者都会收到一份拷贝。 以下是使用 Python Pika 库实现多个消费者同时处理同一个消息的示例代码: ```python import pika import time # 连接 RabbitMQ 服务器 connection = pika.BlockingConnection(pika.ConnectionParameters('localhost')) channel = connection.channel() # 创建一个名为 'my_exchange' 的交换机 channel.exchange_declare(exchange='my_exchange', exchange_type='fanout') # 创建一个名为 'my_queue' 的队列 channel.queue_declare(queue='my_queue') # 将 'my_queue' 队列绑定到 'my_exchange' 交换机 channel.queue_bind(queue='my_queue', exchange='my_exchange') # 定义回调函数用于处理收到的消息 def callback(ch, method, properties, body): print("Received %r" % body) time.sleep(1) # 模拟处理消息的耗时 print("Done") ch.basic_ack(delivery_tag=method.delivery_tag) # 设置每个消费者只处理一个消息 channel.basic_qos(prefetch_count=1) # 启动多个消费者同时消费 'my_queue' 队列中的消息 for i in range(3): # 创建一个名为 'consumer_queue_{i}' 的队列 queue_name = 'consumer_queue_%d' % i channel.queue_declare(queue=queue_name) # 将 'consumer_queue_{i}' 队列绑定到 'my_exchange' 交换机 channel.queue_bind(queue=queue_name, exchange='my_exchange') # 每个消费者只消费自己的队列中的消息 channel.basic_consume(queue=queue_name, on_message_callback=callback) print('Waiting for messages. To exit press CTRL+C') channel.start_consuming() ``` 在上面的示例代码中,我们创建了三个消费者,每个消费者都订阅一个自己的队列,并将这些队列绑定到同一个交换机 `my_exchange` 上。当一个生产者发送一条消息时,这条消息会被交换机广播到所有绑定的队列中,每个消费者都会收到一份拷贝。在每个消息被处理完成后,调用 `ch.basic_ack(delivery_tag=method.delivery_tag)` 告诉 RabbitMQ消息已经被成功处理了。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值