rabbitmq消费者---------消息的获取和ack确认

本文主要介绍了RabbitMQ消息的获取方式,包括不建议使用的拉取get和常用的推送consume模式。还阐述了消息的应答机制,有自动确认和手动确认,以及QoS预取模式。手动确认可避免消息丢失,QoS预取能限速和保证确认质量。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

消息的获取方式

1、拉取get---------------不建议使用

属于一种轮询模型,发送一次 get 请求,获得一个消息。如果此时 RabbitMQ 中没有消息,会获得一个表示空的回复。总的来说,这种方式性能比较
,很明显,每获得一条消息,都要和 RabbitMQ 进行网络通信发出请求。而且对 RabbitMQ 来说,RabbitMQ 无法进行任何优化,因为它永远不知道应用
程序何时会发出请求。

         while(true){
            //拉一条,自动确认的(rabbit 认为这条消息消费 -- 从队列中删除)
            GetResponse getResponse = channel.basicGet(queueName, false);
            if(null!=getResponse){
                System.out.println("received["
                        +getResponse.getEnvelope().getRoutingKey()+"]"
                        +new String(getResponse.getBody()));
            }
            //确认(自动、手动)
            channel.basicAck(0,true);
            Thread.sleep(1000);
        }

2、消息的推送consume-------------最常用的模式

属于一种推送模型。注册一个消费者后,RabbitMQ 会在消息可用时,自动将消息进行推送给消费者。

        //申明一个消费者
        final Consumer consumer  = new DefaultConsumer(channel){
            @Override
            public void handleDelivery(String s, Envelope envelope, AMQP.BasicProperties     basicProperties, byte[] bytes) throws IOException {
                String message = new String(bytes,"UTF-8");
                System.out.println("Received["+envelope.getRoutingKey()+"]"+message);
            }
        };
        //消息者正是开始在指定队列上消费。(queue-king)
        //TODO 这里第二个参数是自动确认参数,如果是true则是自动确认
        channel.basicConsume(queueName,true,consumer);

消息的应答

消费者收到的每一条消息都必须进行确认。消息确认后,RabbitMQ 才会从队列删除这条消息,RabbitMQ 不会为未确认的消息设置超时时
间,它判断此消息是否需要重新投递给消费者的唯一依据是消费该消息的消费者连接是否已经断开。这么设计的原因是 RabbitMQ 允许消费者消费一条消
息的时间可以很久很久。

自动确认

消费者在声明队列时,可以指定 autoAck 参数,当 autoAck=true 时,一旦消费者接收到了消息,就视为自动确认了消息。如果消费者在处理消息的过
程中,出了错,就没有什么办法重新处理这条消息,所以我们很多时候,需要在消息处理成功后,再确认消息,这就需要手动确认。

//TODO 这里第二个参数是:是否需要自动确认参数,如果是false则是手动确认,如果是true则是自动确认
        channel.basicConsume(queueName,true,consumer);

手动确认

当 autoAck=false 时,RabbitMQ 会等待消费者显式发回 ack 信号后才从内存(和磁盘,如果是持久化消息的话)中移去消息。否则,RabbitMQ 会在队列
中消息被消费后立即删除它。
采用消息确认机制后,只要令 autoAck=false,消费者就有足够的时间处理消息(任务),不用担心处理消息过程中消费者进程挂掉后消息丢失的问题,
因为 RabbitMQ 会一直持有消息直到消费者显式调用 basicAck 为止。
当 autoAck=false 时,对于 RabbitMQ 服务器端而言,队列中的消息分成了两部分:一部分是等待投递给消费者的消息;一部分是已经投递给消费者,
但是还没有收到消费者 ack 信号的消息。如果服务器端一直没有收到消费者的 ack 信号,并且消费此消息的消费者已经断开连接,则服务器端会安排该消
息重新进入队列,等待投递给下一个消费者(也可能还是原来的那个消费者)。

 /*声明了一个消费者*/
        final Consumer consumer = new DefaultConsumer(channel){
            @Override
            public void handleDelivery(String consumerTag,
                                       Envelope envelope,
                                       AMQP.BasicProperties properties,
                                       byte[] body) throws IOException {
                String message = new String(body, "UTF-8");
                System.out.println("Received["+envelope.getRoutingKey()
                        +"]"+message);
                //TODO 这里进行确认
                System.out.println("手动确认的tag:"+envelope.getDeliveryTag());
                channel.basicAck(envelope.getDeliveryTag(),false);
            }
        };
        /*消费者正式开始在指定队列上消费消息*/
        //TODO 这里第二个参数是自动确认参数,如果是false则是手动确认
        channel.basicConsume(queueName,false,consumer);


通过运行程序,启动两个消费者 A、B,都可以收到消息,但是其中有一个消费者 A 不会对消息进行确认,当把这个消费者 A 关闭后,消费者 B 又会
收到本来发送给消费者 A 的消息。所以我们一般使用手动确认的方法是,将消息的处理放在 try/catch 语句块中,成功处理了,就给 RabbitMQ 一个确认应
答,如果处理异常了,就在 catch 中,进行消息的拒绝,如何拒绝,参考消息的拒绝章节https://blog.csdn.net/nandao158/article/details/109801226

QoS 预取模式

在确认消息被接收之前,消费者可以预先要求接收一定数量的消息,在处理完一定数量的消息后,批量进行确认。如果消费者应用程序在确认消息
之前崩溃,则所有未确认的消息将被重新发送给其他消费者。所以这里存在着一定程度上的可靠性风险。
这种机制一方面可以实现限速(将消息暂存到 RabbitMQ 内存中)的作用,一方面可以保证消息确认质量(比如确认了但是处理有异常的情况)。
注意:消费确认模式必须是非自动 ACK 机制(这个是使用 baseQos 的前提条件,否则会 Qos 不生效),然后设置 basicQos 的值;另外,还可以基于
consume 和 channel 的粒度进行设置(global)。

生产者代码(主要是模拟生产者发送较多的代码)

//TODO 生产者发送非常多的数据
        //发送210条消息,其中第210条消息表示本批次消息的结束
        for(int i=0;i<210;i++){
            // 发送的消息
            String message = "Hello World_"+(i+1);
            if(i==209){ //最后一条
                message = "stop";
            }
            //参数1:exchange name
            //参数2:routing key
            channel.basicPublish(EXCHANGE_NAME, "error",
                    null, message.getBytes());
            System.out.println(" [x] Sent 'error':'"
                    + message + "'");
        }

QOS预取代码

//TODO 150条预取(150都取出来 150, 210-150  60  )
        channel.basicQos(150,true);
        /*消费者正式开始在指定队列上消费消息*/
        channel.basicConsume(queueName,false,consumer);

QOS预取代码-----------自定义批量确认

1、实现DefaultConsumer

package cn.enjoyedu.consumer_balance.qos;

import com.rabbitmq.client.AMQP;
import com.rabbitmq.client.Channel;
import com.rabbitmq.client.DefaultConsumer;
import com.rabbitmq.client.Envelope;

import java.io.IOException;

/**
 * 批量确认 -----消费者
 */
public class BatchAckConsumer extends DefaultConsumer {
    //计数,第多少条
    private  int meesageCount =0;
    public BatchAckConsumer(Channel channel) {
        super(channel);
        System.out.println("批量消费者启动了......");
    }

    @Override
    public void handleDelivery(String consumerTag,
                               Envelope envelope,
                               AMQP.BasicProperties properties,
                               byte[] body) throws IOException {
        //把消息体拉出来
        String message = new String(body,"UTF-8");

        System.out.println("批量消费者---Received["+envelope.getRoutingKey()
                +"]"+message);
        meesageCount++;
        //批量确认 50一批
        if(meesageCount %50 ==0){
           this.getChannel().basicAck(envelope.getDeliveryTag(),true);
            System.out.println("批量消息费进行消息的确认------------");
        }
        if(message.equals("stop")){ //如果是最后一条消息,则把剩余的消息都进行确认
            this.getChannel().basicAck(envelope.getDeliveryTag(),true);
            System.out.println("批量消费者进行最后业务消息的确认---------");
        }
    }
}

2、消费者的代码

        //TODO 150条预取(150都取出来 150, 210-150  60  )
        channel.basicQos(150,true);
        /*消费者正式开始在指定队列上消费消息*/
        //TODO 自定义消费者批量确认
        BatchAckConsumer batchAckConsumer = new BatchAckConsumer(channel);
        channel.basicConsume(queueName,false,batchAckConsumer);

 

RabbitMQ-C是一个用于与RabbitMQ服务器交互的C语言客户端库。如果你想通过RabbitMQ-C清空队列,可以按照以下步骤操作: 1. 首先,你需要连接到RabbitMQ服务器。创建一个`amqp_connection_t`连接,并开启会话(`amqp_session_start`)。 2. 确定你要清空的队列名,通常使用`amqp_queue_declare()`函数声明或获取队列信息。 3. 使用`amqp.basic.consume()`来建立一个消费者,这个消费者会在接收到消息时立即确认(`AMQP_AUTO_ack`),从而消费掉队列中的所有未确认消息。 4. 当所有的消息都被消费并确认后,你可以关闭消费者的回调(`amqp_cancel()`)。 5. 最后,你可以选择性地清除队列中的持久化消息。这需要发送一个`DELETE`命令给RabbitMQ,使用`amqp.queue.delete()`函数,但是请注意,这不会删除正在处理的消息,只是防止新的消息进入队列。 ```c // 示例代码片段: amqp_channel_t *channel = amqp_connection_create_channel(conn); amqp_queue_delete(channel, queue_name, AMQP_EXCL); // 删除队列(非生产环境) amqp_rpc_reply_t result; amqp_basic_consume_ok_t consume_ok; while (true) { amqp_basic_get_result_t get_result = amqp_basic_get(channel, queue_name, &message, &properties, NULL, &consumer_tag, &delivery_tag, &redelivered, &exchange, &routing_key, &result); if (get_result == AMQP_NO_MESSAGES) { break; // 如果没有更多消息,跳出循环 } amqp.basic_ack(channel, delivery_tag, true); // 立即确认消息 // 处理消息... } amqp_channel_close(channel); amqp_connection_close(conn); ```
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

寅灯

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值