这是我参与更文挑战的第 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
方法,控制台打印如下:
那我们将属性 key 的值修改为 key-2222
,注释掉 创建交换机、创建队列、队列与交换机绑定的代码。启动main
方法,控制台打印如下:
根据日志可得出,消息发送到交换机成功,但并没有发送到指定队列,因为根据路由key 匹配不上对应的队列。
注:调用channel.basicPublish时,需要将mandatory参数设置为true
当然也可以测试一下 mandatory
参数为false
时的情况。
channel.basicPublish(exchange, key,false, null,message.getBytes());
可以看到结果,并没有执行 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-1
和 key-1
是已经存在的。
调用接口:http://localhost:8080/send?exchange=exchange-1&key=key-1 。交换机和路由key都是存在的。 调用接口:http://localhost:8080/send?exchange=exchange-1&key=key-155 。交换机存在,路由key不存在。
画了一张勉勉强强的图。
到这,一条消息我们才能知道真正发送到了指定的队列中。
- 如你对本文有疑问或本文有错误之处,欢迎评论留言指出。如觉得本文对你有所帮助,欢迎点赞和关注。