RabbitMq 配置 消费者和 ReturnCallback 和 ConfirmCallback

在rabbitMq中 设置2种消息确认模式

1:自动确认 :RabbitMq种默认的确认模式 自动确认模式下,当 Broker (消息队列服务器实体)成功发送消息给消费者后就会立即把此消息从队列中删除,而不用 等待消费者回送确认消息。

2:手动确认。而在手动确认模式下,当 Broker (消息队列服务器实体) 发送消息给消费者后并不会立即把 此消息删除, 而是要等收到消费者回送的确认消息后才会删除,因此当消费者收到消息已经会发送一个ACK命令指消息确认给发送者,如果消费者因为意外崩溃而没有 发送 ACK 指令, 那么 Broker (消息队列服务器实体) 就会把该消息转发给其他消费者(如果此时没有其他消费者,则 Broker(消息队列服务器实体) 会缓存此消息, 直到有新的消费者注册)。

在RabbitMq 配置手动模式 

    /**
     *  配置消息监听者
     * @param connectionFactory
     * @return
            */
    @Bean
    public SimpleMessageListenerContainer messageContainer(ConnectionFactory connectionFactory) {
        SimpleMessageListenerContainer container = new SimpleMessageListenerContainer(connectionFactory);
        //同时监听多个队列
        container.setQueues(directRabbitMqQueue());
        //设置当前的消费者数量
        container.setConcurrentConsumers(1);
        //设置当前的消费者数量上限
        container.setMaxConcurrentConsumers(5);
        //设置是否重回队列
        container.setDefaultRequeueRejected(true);
        //设置手动签收 --》 这里指定确认方式 Auto为自动  MANUAL为手动
        container.setAcknowledgeMode(AcknowledgeMode.MANUAL);
        //设置监听外露
        container.setExposeListenerChannel(true);
        //设置消息监听 
        container.setMessageListener(fooMessageListener());
        return container;
    }

确定手动模式确认后,开始编写消费者和消息回调 ReturnCallback 和  ConfirmCallback

ConfirmCallback:消息发送成功的回调当消息进入Exchange交换器时就进入回调,不管是否进入队列。默认是关闭的需要手动开启在配置ConnectionFactory连接对象时配置

// 确认开启ConfirmCallback回调
connectionFactory.setPublisherReturns(true);

ReturnCallback:当消息进入Exchange交换器时就进入回调,但是未进入队列时回调。这里有二种状态要注意当配置

Mandatory:

为true时,消息通过交换器无法匹配到队列会返回给生产者 并触发MessageReturn

为false时,匹配不到会直接被丢弃

// rabbitMq新版本开启 ReturnCallback
connectionFactory.setPublisherConfirmType(CachingConnectionFactory.ConfirmType.CORRELATED);

配置 ReturnCallback 和  ConfirmCallback

/**
 * 消息发送成功的回调
 * 需要开启
 * # 开启发送确认
 * publisher-confirms: true
 * @author hw
 * @date 2019-07-22 15:44
 **/
@Slf4j
@Component
public class ConfirmReturn implements ConfirmCallback {

    /**
     * 发布者确认的回调。
     *
     * @param correlationData 回调的相关数据。
     * @param ack             ack为真,nack为假
     * @param cause           一个可选的原因,用于nack,如果可用,否则为空。
     */
    @Override
    public void confirm(CorrelationData correlationData, boolean ack, String cause) {
        if (ack) {
            log.info("消息成功发送到exchange");
        } else {
            log.error("消息发送exchange失败:" + cause);
        }
    }
}

实现ConfirmCallback 接口,重写confirm方法即可。

/**
 * 发生异常时的消息返回提醒
 * 需要开启
 * # 开启发送失败退回
 * publisher-returns: true
 * @author hw
 * @date 2019-07-22 15:45
 **/
@Slf4j
@Component
public class MessageReturn implements ReturnCallback {
    @Override
    public void returnedMessage(Message message, int replyCode, String replyText, String exchange, String routingKey) {
        log.info("返回消息回调:{} 应答代码:{} 回复文本:{} 交换器:{} 路由键:{}", message, replyCode, replyText, exchange, routingKey);
    }
}

ReturnCallback同理

创好 ReturnCallback 和  ConfirmCallback之后还不能直接使用需要将2个实现类配置到 RabbitTemplate对象中

        /**
         * Mandatory为true时,消息通过交换器无法匹配到队列会返回给生产者 并触发MessageReturn
         *          为false时,匹配不到会直接被丢弃
         */
        rabbitTemplate.setMandatory(true);
        rabbitTemplate.setReturnCallback(messageReturn);

        rabbitTemplate.setConfirmCallback(confirmReturn);

配置完成之后我们来自定义一个消费者来实现一下 是否和我们配置的相同

/**
 * 消费者
 * @author hw
 */
@Slf4j
@Service

public class ReceiverMessage {

    public ReceiverMessage() {
        log.info("ReceiverMessage正在初始化");
    }
    @RabbitListener(queues = "directRabbitMqQueue",ackMode = "MANUAL")
    public void processHandler(String msg, Channel channel,Message message) throws IOException {

        try {
            log.info("ReceiverMessage 收到消息:{}", msg);

            //TODO 具体业务
            //告诉服务器收到这条消息 无需再发了 否则消息服务器以为这条消息没处理掉 后续还会在发
            channel.basicAck(message.getMessageProperties().getDeliveryTag(), false);
        }  catch (Exception e) {
            log.info("回调的相关数据:{} ack:{} cause:{} ");
            if (message.getMessageProperties().getRedelivered()) {

                log.error("消息已重复处理失败,拒绝再次接收...");
                // 拒绝消息
                channel.basicReject(message.getMessageProperties().getDeliveryTag(), false);
            } else {

                log.error("消息即将再次返回队列处理...");

                channel.basicNack(message.getMessageProperties().getDeliveryTag(), false, true);
                throw new RuntimeException("自定义");
            }
        }
    }
}

创建一个测试类来跑一下看看

    @Test
    public void convertAndSend() {
        MaMallPojo maMallPojo = new MaMallPojo();
        maMallPojo.setId("1");
        maMallPojo.setMall("1072484187@qq.com");
        maMallPojo.setUserName("hewei");

        /**
         * 发送消息
         */
        CorrelationData correlationId = new CorrelationData(UUID.randomUUID().toString());
        rabbitTemplate.convertAndSend(rabbitmqDirectExchange,rabbitmqDirectRoutingKey,maMallPojo,correlationId);
    }
}

  rabbitmqDirectExchange和rabbitmqDirectRoutingKey是我自定义的exchange 和 routingKey2者是相互绑定的

: ReceiverMessage 收到消息:{"id":"1","userName":"hewei","mall":"1072484187@qq.com"}       
: 消息成功发送到exchange

当exchange 和 routingKey相绑定时,消息通过exchange 和 routingKey进入相对应的队列中则只会触发ConfirmCallback 不会触发ReturnCallback

在次实验一下 exchange 和 routingKey不相互绑定时

: 返回消息回调:(Body:'{"id":"1","userName":"hewei","mall":"1072484187@qq.com"}' MessageProperties [headers={spring_returned_message_correlation=4df9223a-ba36-4226-bb2b-18d4d1da8be8, __TypeId__=com.system.mq.发送邮件.MaMallPojo}, contentType=application/json, contentEncoding=UTF-8, contentLength=0, receivedDeliveryMode=PERSISTENT, priority=0, deliveryTag=0]) 应答代码:312 回复文本:NO_ROUTE 交换器:directRabbitMqExchange1 路由键:directRabbitMqRoutingKey
: 消息成功发送到exchange

因为 exchange 和 routingKey不相互绑定所以消息无法进入队列,消费者自然也收不到消息。当时exchange是真正存在的所以消息还是会进入 exchange所以还是会触发ConfirmCallback 并且因为无法找到对应的队列所以也会触发ReturnCallback。

 

自此RabbitMq手动配置模式完成。

  • 5
    点赞
  • 45
    收藏
    觉得还不错? 一键收藏
  • 11
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值