消息队列手动确认Ack

以RabbitMQ为例,默认情况下 RabbitMQ 是自动ACK机制,就意味着 MQ 会在消息发送完毕后,自动帮我们去ACK,然后删除消息的信息。
这样依赖就存在这样一个问题:
如果消费者处理消息需要较长时间,最好的做法是消费端处理完之后手动去确认。

1、配置文件:

  rabbitmq:
    host: ${yun.activity.rabbitmq.host}
    port: ${yun.activity.rabbitmq.port}
    username: ${yun.activity.rabbitmq.username}
    password: ${yun.activity.rabbitmq.password}
    virtual-host: ${yun.activity.rabbitmq.virtual-host}
    publisher-confirms: true
    publisher-returns: true
    template:
      mandatory: true
    listener:
      simple:
        acknowledge-mode: manual

通过acknowledge-mode: manual 设置为手动设置Ack模式。


2、消费者:

    @RabbitListener(queues = "activity-eleven-first-notify", containerFactory = "activityElevenCainerFactory")
    public void processNormalOrder(Message message, Channel channel) {
        try {
            dealInfo(message);
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            try {
                channel.basicAck(message.getMessageProperties().getDeliveryTag(), false);
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }

以上代码通过,channel.basicAck去手动回复消息处理状态

除了basicAck之外,RabbitMQ还提供了其他几个方法进行设置Ack,如下:

另外,消息的确认类型:

1)channel.basicAck(deliveryTag, multiple);
consumer处理成功后,通知broker删除队列中的消息,如果设置multiple=true,表示支持批量确认机制以减少网络流量。
例如:有值为5,6,7,8 deliveryTag的投递
如果此时channel.basicAck(8, true);则表示前面未确认的5,6,7投递也一起确认处理完毕。
如果此时channel.basicAck(8, false);则仅表示deliveryTag=8的消息已经成功处理。

2)channel.basicNack(deliveryTag, multiple, requeue);
consumer处理失败后,例如:有值为5,6,7,8 deliveryTag的投递。
如果channel.basicNack(8, true, true);表示deliveryTag=8之前未确认的消息都处理失败且将这些消息重新放回队列中。
如果channel.basicNack(8, true, false);表示deliveryTag=8之前未确认的消息都处理失败且将这些消息直接丢弃。
如果channel.basicNack(8, false, true);表示deliveryTag=8的消息处理失败且将该消息重新放回队列。
如果channel.basicNack(8, false, false);表示deliveryTag=8的消息处理失败且将该消息直接丢弃。

3)channel.basicReject(deliveryTag, requeue);
相比channel.basicNack,除了没有multiple批量确认机制之外,其他语义完全一样。
如果channel.basicReject(8, true);表示deliveryTag=8的消息处理失败且将该消息重新放回队列。
如果channel.basicReject(8, false);表示deliveryTag=8的消息处理失败且将该消息直接丢弃。

由此可见,手动Ack如果处理方式不对会发生一些问题。
1.没有及时ack,或者程序出现bug,所有的消息将被存在unacked中,消耗内存
如果忘记了ack,那么后果很严重。当Consumer退出时,Message会重新分发。然后RabbitMQ会占用越来越多的内存,由于 RabbitMQ会长时间运行,因此这个“内存泄漏”是致命的。
2.如果使用BasicNack,将消费失败的消息重新塞进队列的头部,则会造成死循环。
(解决basicNack造成的消息循环循环消费的办法是为队列设置“回退队列”,设置回退队列和阀值,如设置队列为q1,阀值为2,则在rollback两次后将消息转入q1)

综上,手动ack需要注意的是:
1.在消费者端一定要进行ack,或者是nack,可以放在try方法块的finally中执行
2.可以对消费者的异常状态进行捕捉,根据异常类型选择ack,或者nack抛弃消息,nack再次尝试
3.对于nack的再次尝试,是进入到队列头的,如果一直是失败的状态,将会造成阻塞。所以最好是专门投递到“死信队列”,
 

 

Python中使用RabbitMQ进行消息队列手动消费,通常是利用`pika`这个库来实现的。手动消费意味着消费者需要显式地确认消息已经被消费,这样RabbitMQ才会从队列中移除该消息。这种方式可以确保消息的可靠处理,即使在消费程序崩溃的情况下,也不会丢失消息。 以下是使用`pika`库进行手动消费的基本步骤: 1. 连接到RabbitMQ服务器:创建一个连接对象,连接到RabbitMQ服务器。 2. 创建频道(Channel):频道是RabbitMQ内部用于执行操作的通道,比如发送和接收消息。 3. 声明交换器和队列:告诉RabbitMQ你想要使用哪个交换器和队列,如果它们还不存在,RabbitMQ可以为你创建。 4. 绑定队列:将队列和交换器通过路由键绑定起来。 5. 接收消息:设置一个回调函数,当队列中有消息到达时,这个回调函数会被调用。 6. 确认消息:消费完消息后,需要发送确认信号给RabbitMQ,告诉它消息已被处理。确认信号需要在回调函数中执行。 示例代码片段(仅供参考,具体实现可能根据需要调整): ```python import pika # 创建连接 connection = pika.BlockingConnection(pika.ConnectionParameters('localhost')) channel = connection.channel() # 声明交换器和队列(如果不存在的话) channel.exchange_declare(exchange='your_exchange_name', exchange_type='direct') channel.queue_declare(queue='your_queue_name') # 绑定队列 channel.queue_bind(exchange='your_exchange_name', queue='your_queue_name', routing_key='your_routing_key') # 定义消费回调函数 def on_message_callback(ch, method, properties, body): print(f"Received message: {body}") # 手动确认消息已被处理 ch.basic_ack(delivery_tag=method.delivery_tag) # 开始消费 channel.basic_consume( queue='your_queue_name', on_message_callback=on_message_callback, auto_ack=False # 关闭自动确认,启用手动确认 ) print(' [*] Waiting for messages. To exit press CTRL+C') channel.start_consuming() ``` 在上面的代码中,`auto_ack=False` 表示使用手动确认模式。在 `on_message_callback` 函数中,`ch.basic_ack(delivery_tag=method.delivery_tag)` 是发送确认信号的关键步骤。
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值