RabbitMQ Return机制(十)

857 阅读4分钟

这是我参与更文挑战的第 28 天,活动详情查看: 更文挑战

日积月累,水滴石穿 😄

Return机制

上篇已经讲了发送方确认机制,已经知道 发送方确认机制是确认生产者是否成功发送消息到交换机。那交换机是否将消息发送到具体的队列那我们就不得而知了。如果想知道交换机是否成功将消息发送到了队列,就需要用到 return机制:监控交换机是否将消息发送到队列

在客户端 Channel 接口中提供的addReturnListener方法,可以添加 ReturnListener这个回调接口,这个ReturnListener接口包含一个方法:handleReturn,用来处理交换机发送消息到队列失败,则执行此方法。

原生api方式

//省略获取连接、获得 channel
String quequ = "queue-2";
String exchange = "exchange-2";
String key = "key-2";
//创建交换机
channel.exchangeDeclare(exchange, BuiltinExchangeType.TOPIC, true);
//创建队列
channel.queueDeclare(quequ, true, false, false, null);
//队列与交换机绑定
channel.queueBind(quequ, exchange, key);
//开启消息确认机制
channel.confirmSelect();
channel.addConfirmListener(new ConfirmListener() {
    //参数一:deliveryTag: 消息的编号
    //参数二:multiple:是否批量confirm true 是
    public void handleAck(long deliveryTag, boolean multiple) throws IOException {
        System.out.println("消息发送到交换机成功,deliveryTag: " + deliveryTag + ", multiple: " + multiple);
    }

    public void handleNack(long deliveryTag, boolean multiple) throws IOException {
        System.out.println("消息发送到交换机失败, deliveryTag: " + deliveryTag + ", multiple: " + multiple);
    }
});
//Return机制
channel.addReturnListener(new ReturnListener() {
    /*
     * 参数1:响应code
     * 参数2:响应文本
     * 参数3:交换机名称
     * 参数4:路由key
     * 参数5:消息的基本属性集
     * 参数6:消息内容
     */
    public void handleReturn(int replyCode, String replyText,
                             String exchange, String routingKey,
                             AMQP.BasicProperties properties, byte[] body) throws IOException {
        //此处便是执行Basic.Return之后回调的地方
        //如果交换机发送消息到队列失败,则执行此方法
        System.out.println("replyCode =" + replyCode);
        System.out.println("replyText =" + replyText);
        System.out.println("exchange =" + exchange);
        System.out.println("routingKey =" + routingKey);
        System.out.println("properties =" + properties);
        System.out.println("body =" + new String(body));
    }
});

/**
 * 第三个参数:mandatory:当 mandatory 参数设为 true 时,
 * 交换器无法根据自身的类型和路由键找到一个符合条件的队列时,
 * 那么 RabbitMQ 会调用 Basic.Return 命令将消息返回给生产者。
 * 当 mandatory参数设置为 false 时,出现上述情形,则消息直接被丢弃。
 */
String message = "发送路由key为 =" + key +"的消息";
channel.basicPublish(exchange, key,true, null,message.getBytes());
System.out.println("其他逻辑");

启动main方法,控制台打印如下: image.png 那我们将属性 key 的值修改为 key-2222,注释掉 创建交换机、创建队列、队列与交换机绑定的代码。启动main方法,控制台打印如下: image.png 根据日志可得出,消息发送到交换机成功,但并没有发送到指定队列,因为根据路由key 匹配不上对应的队列。

注:调用channel.basicPublish时,需要将mandatory参数设置为true

当然也可以测试一下 mandatory参数为false时的情况。 channel.basicPublish(exchange, key,false, null,message.getBytes()); image.png 可以看到结果,并没有执行 handleReturn回调函数。

Boot方式

在yml中配置开启Return机制

spring:
  application:
    name: info-config-boot
  rabbitmq:
    host: 47.105.*
    port: 5672
    virtual-host: /test-1
    username: *
    password: *
    # 开启 Return机制
    publisher-returns: true

编码

创建类并实现 ReturnCallback 接口

@Component
public class Return implements RabbitTemplate.ReturnCallback {

    Logger logger = LoggerFactory.getLogger(Return.class);

    @Autowired
    private RabbitTemplate rabbitTemplate;

    /**
     * 需要给ConfirmCallback赋值 不然不会走回调方法,默认是null
     */
    @PostConstruct
    public void init(){
        rabbitTemplate.setReturnCallback(this);
    }
    //处理交换机发送消息到队列失败,则执行此方法。
    @Override
    public void returnedMessage(Message message,
                                int replyCode, String replyText,
                                String exchange, String routingKey) {
        logger.info("交换机发送消息到队列失败=====》");
        logger.info("message = {}",new String(message.getBody()));
        logger.info("replyCode = {}",replyCode);
        logger.info("replyText = {}",replyText);
        logger.info("exchange = {}",exchange);
        logger.info("routingKey = {}",routingKey);
    }
}

提供对外调用方法

@Autowired
RabbitTemplate rabbitTemplate;

@GetMapping("/send")
public void send(String exchange,String key){
    CorrelationData correlation = new CorrelationData("设置:" + UUID.randomUUID().toString());
    rabbitTemplate.convertAndSend(exchange,key,"发送消息",correlation);
}

send方法新增了两个入参,动态控制交换机与路由key名称。exchange-1key-1是已经存在的。

调用接口:http://localhost:8080/send?exchange=exchange-1&key=key-1 。交换机和路由key都是存在的。 image.png 调用接口:http://localhost:8080/send?exchange=exchange-1&key=key-155 。交换机存在,路由key不存在。

image.png

画了一张勉勉强强的图。 草图一张

到这,一条消息我们才能知道真正发送到了指定的队列中。

  • 如你对本文有疑问或本文有错误之处,欢迎评论留言指出。如觉得本文对你有所帮助,欢迎点赞和关注。