rabbitmq专题03-RabbitMq的发送端和消费端的ack确认

本文详细介绍了RabbitMQ的消息确认机制,包括发送端和消费端的确认模式。在发送端,通过设置确认类型和回调服务确保消息发送到交换机和队列的可靠性。消费端则有NONE、MANUAL和AUTO三种确认模式,MANUAL模式下可以通过手动ack/nack控制消息的消费状态,防止重复消费或异常处理。同时提供了实战代码示例进行说明。
摘要由CSDN通过智能技术生成
1. 消息确认机制
1.1发送端的确认

是否发送到交换机的确认,是否发送到队列的确认。
自定义RabbitmqConfig-ConnectionFactory的方式:

/**
 * 自定义RabbitmqConfig
 */
@Configuration
public class RabbitmqConfig {

    @Bean("connectionFactory")
    public ConnectionFactory connectionFactory() {
        CachingConnectionFactory connectionFactory = new CachingConnectionFactory("ip", 5672);
        connectionFactory.setVirtualHost("/");
        connectionFactory.setUsername("user_admin");
        connectionFactory.setPassword("user_admin");
        //此设置开启发送到交换机和队列的回调。确保发送端消息ack回调是否执行
        connectionFactory.setPublisherConfirmType(CachingConnectionFactory.ConfirmType.CORRELATED);
        return connectionFactory;
    }
    @Bean("rabbitTemplate")
    public RabbitTemplate rabbitTemplate(ConnectionFactory connectionFactory) {
        RabbitTemplate rabbitTemplate = new RabbitTemplate(connectionFactory);
        rabbitTemplate.setMessageConverter(new Jackson2JsonMessageConverter());
        return rabbitTemplate;
    }
    @Bean("directExchange")
    public DirectExchange defaultExchange() {
        return new DirectExchange("testExchange");
    }
    @Bean("queue")
    public Queue queue() {
        return new Queue("jllqueue", true);
    }
    @Bean("binding")
    public Binding binding() {
        return BindingBuilder.bind(queue()).to(defaultExchange()).with("testRoutingKey");
    }
}

/**
 * @Description 确保是否发送到交换机,可以配合配置属性 spring.rabbitmq.publisher-confirm-type 使用。如果配置文件不定义,代码    *中connectionFactory.setPublisherConfirmType(CachingConnectionFactory.ConfirmType.CORRELATED)   *rabbitTemplate.setConfirmCallback(confirmCallbackService) 必须定义。
 * @Author: jinglonglong
 * @Date:2021-6-29
 **/
@Component
public class ConfirmCallbackService implements RabbitTemplate.ConfirmCallback   {
    @Override
    public void confirm(CorrelationData correlationData, boolean ack, String cause) {
        System.out.println("### ConfirmCallbackService-ack:"+ack);
        if(!ack){
            System.out.println("消息发送失败,cause:"+cause);
        } else {
            System.out.println(" 消息发送成功:"+ JSON.toJSONString(correlationData));
        }

    }
}

/**
 * @Description 确保交换机中消息是否路由到队列,可以配合配置属性:spring.rabbitmq.publisher-returns使用。如果配置文件不定义,*下面的代码中rabbitTemplate.setMandatory(true)  rabbitTemplate.setReturnsCallback(returnCallbackService)必须定义。
 * @Author: jinglonglong
 * @Date:2021-6-29
 **/
@Component
public class ReturnCallbackService implements RabbitTemplate.ReturnsCallback {
    @Override
    public void returnedMessage(ReturnedMessage returned) {
        System.out.println("### ReturnCallbackService-消息发送失败:"+ JSON.toJSONString(returned));
    }
}
/**
 * @Description 发送消息
 * @Author: jinglonglong
 * @Date:2021-6-29
 **/
@Component
public class RabbitMqSend {
    @Autowired
    private RabbitTemplate rabbitTemplate;
    @Autowired
    private ConfirmCallbackService confirmCallbackService;
    @Autowired
    private ReturnCallbackService returnCallbackService;

    public void sendMessage(String msg,String routingKey,String exchange){
        //发送消息时设置强制标志; 仅当提供了returnCallback时才适用。
        rabbitTemplate.setMandatory(true);
        //确保消息是否发送到交换机,成功与失败都会触发
        rabbitTemplate.setConfirmCallback(confirmCallbackService);
        //确保消息是否发送到队列,成功发送不触发,失败触发
        rabbitTemplate.setReturnsCallback(returnCallbackService);
        rabbitTemplate.convertAndSend(exchange,routingKey,msg);
    }
}

ConfirmCallbackService 和 ReturnCallbackService是回调逻辑,分别是消息发送到交换机和消息发送到队列的回调逻辑类。

如果不自定义RabbitmqConfig-ConnectionFactory,用下面配置替换connectionFactory.setPublisherConfirmType(CachingConnectionFactory.ConfirmType.CORRELATED);,可以在配置文件中加配置:

spring.rabbitmq.publisher-confirm-type=CORRELATED
spring.rabbitmq.publisher-returns=true
1.2消费端的确认

消费端确认有三种模式:NONE,MANUAL,AUTO。

NONE:不管消费端消息是否正常消费或者有消费时是否发生异常,都会被确认为消费成功,队列都会删除。

MANUAL:手动确认,是否ack是需要配合消费端的代码 channel.basicAck() 来确认的,如果没有此代码,那么消息一直处于 unacked 状态,一旦消费端重启,或断开连接,那么unacked的消息会重新进入 ready状态,可能会发生重复消费。如果消费成功,通过channel.basicAck确认,队列中会删除该消息。如果消费失败,需要channel.basicNack确保消息重新进入ready状态或者进入死信队列。

AUTO:消费端如果正常消费,则消息自动确认。如果消费异常,并且抛出异常,则会进入unacked 状态,然后重新进入ready状态,重复消费,直到消费成功,有可能发生死循环。如果加了try catch,即使消费者执行异常,则认为消费正常,队列中的消息会自动删除。

以下为实战代码:

@Configuration
public class RabbitmqConfig {

    @Bean
    public ConnectionFactory connectionFactory() {
        CachingConnectionFactory connectionFactory = new CachingConnectionFactory("ip", 5672);
        connectionFactory.setVirtualHost("/");
        connectionFactory.setUsername("user_admin");
        connectionFactory.setPassword("user_admin");
        return connectionFactory;
    }
    //定义direct类型的交换机监听容器
    @Bean
    public DirectRabbitListenerContainerFactory directRabbitListenerContainerFactory(ConnectionFactory connectionFactory){
        DirectRabbitListenerContainerFactory directRabbitListenerContainerFactory = new DirectRabbitListenerContainerFactory();
        directRabbitListenerContainerFactory.setConnectionFactory(connectionFactory);
        //设置确认模式:NONE,MANUAL,AUTO。当前设置为手动模式
        directRabbitListenerContainerFactory.setAcknowledgeMode(AcknowledgeMode.MANUAL);
        return directRabbitListenerContainerFactory;
    }
}

	//------以下消费者方式取其一即可,为了方便解释,全列出来-------
	/**
	 * 方式一:不论异常与否,均通过channel.basicAck来确认消息已消费
	 * 消费业务
	 */
    @RabbitListener(queues = "jllqueue",containerFactory = "directRabbitListenerContainerFactory")
    public void consumer(Message message, Channel channel) throws IOException {
        try {
            System.out.println("### 执行业务 ### "+message);
        } catch (Exception e) {
            e.printStackTrace();
            channel.basicNack(message.getMessageProperties().getDeliveryTag(),false,false);
        }
        //手动确认,设置手动确认后,没有下面代码,那么队列中Unacked状态的数据一直存在,直到消费端服务断开时,这些数据会重新进入到Ready状态中重新消费,这样会导致重复消费
        channel.basicAck(message.getMessageProperties().getDeliveryTag(),false);
    }

/**
     * 方式二:消费正常,通过channel.basicAck确认;消费异常,通过channel.basicReject让消息重返队列或放弃消息
     * @param message
     * @param channel
     * @throws IOException
     */
    @RabbitListener(queues = "jllqueue",containerFactory = "directRabbitListenerContainerFactory")
    public void consumer2(Message message, Channel channel) throws IOException {
        System.out.println(message);
        try {
            int i = 10/0;
        } catch (Exception e) {
            e.printStackTrace();
            System.out.println("记录消费失败的消息");
            //第二个参数为true时,让消息重回队列进行消费,容易出现死循环。第二个参数为false,则放弃消息。
            channel.basicReject(message.getMessageProperties().getDeliveryTag(),false);
            return;
        }
        //正常消费,确认已消费消息
        channel.basicAck(message.getMessageProperties().getDeliveryTag(),false);
    }
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

荆茗Scaler

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

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

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

打赏作者

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

抵扣说明:

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

余额充值