RabbitMQ提供了两种方式来解决消息投递的可靠性,避免消息丢失或消息投递失败的情况
- confirm确认模式
- return退回模式
SpringBoot中需要开启响应配置才能使用,如果不设置消息投递失败后,默认丢弃消息,不会进行任何回调。
spring:
rabbitmq:
publisher-confirm-type: correlated
publisher-returns: true
rabbitTemplate.setMandatory(true);
mandatory:用于设置处理失败消息的方式,直接丢失还是返回给消息生产者
(1)当mandatory标志位设置为true时,如果exchange根据自身类型和消息routingKey无法找到一个合适的queue存储消息,那么broker会调用basic.return方法将消息返还给生产者
(2)当mandatory设置为false时,出现上述情况broker会直接将消息丢弃,回调方法也不会执行
RabbitMQ整个消息投递的过程:
producer→RabbitMQ broker→exchange→queue→consumer 【其中消息实际是存放在queue中】
- 消息从producer投递到exchange,会返回一个confirmCallback。无论是否投递成功,都会返回。当ack=true,表示成功,当ack=fase,表示失败
- 消息从exchange投递到queue,投递失败则会返回一个returnCallback
ConfirmCallback
设置confirm确认模式分为两步:
(1)配置中开启confirm确认模式
(2)在RabbitTemplate中定义ConfirmCallback的实现类【建议通过lambda表达式实现】
@FunctionalInterface
public interface ConfirmCallback {
/**
* @param correlationData 相关配置信息
* @param ack true代表ack【确认,接收成功】, false代表nack【未确认,接收失败】
* @param cause 失败原因
*/
void confirm(@Nullable CorrelationData correlationData, boolean ack, @Nullable String cause);
}
实际应用:RabbitTemplate必须设置为多例,才能如下使用
@Test
public void test() {
User user = new User();
user.setId(1);
user.setUsername("老大");
rabbitTemplate.setConfirmCallback((data, ack, cause) -> {
if (!ack) {
log.error("消息发送失败!" + cause + data.toString());
} else {
log.info("消息发送成功,消息ID:" + (data != null ? data.getId() : null));
}
});
rabbitTemplate.convertAndSend(RabbitConMQConfig.PURCHASE_DIRECT_EXCHANGE, RabbitConMQConfig.PURCHASE_ORDER_ROUTING_KEY, user,
new CorrelationData(String.valueOf(user.getId())))
}
CorrelationData对象,用于设置发送消息时需要投递的额外配置新,CorrelationData 对象内部只有一个 id 属性,用来表示当前消息唯一性。实际开发中CorrelationData id是与业务无关的,发送的id需要记录下来用于纠错和对账
ReturnCallback
设置return回退模式分为三步:
(1)配置中开启return回退模式:spring.rabbitmq.publisher-returns=true
(2)设置Exchange处理消息的模式:spring.rabbitmq.template.mandatory=true
- 消息没有路由到Queue,则丢弃消息【默认配置】
- 消息没有路由到Queue,则返回消息给消息生产者producer
(3)在RabbitTemplate中定义ReturnCallback的实现类【建议通过lambda表达式实现】,回调中可以接收到第(2)步中返回的消息,并进行处理
如果设置了spring.rabbitmq.publisher-returns=true,但未设置spring.rabbitmq.template.mandatory=true,则Exchange没有找到Queue就会丢弃掉消息, 而不会触发回调
@FunctionalInterface
public interface ReturnCallback {
/**
* @param 投递失败后Exchange返回给消息生产者的消息
* @param 返回的错误码
* @param 返回的错误信息
* @param 交换机名称
* @param 路由键
*/
void returnedMessage(Message message, int replyCode, String replyText, String exchange, String routingKey);
}
@Test
public void test() {
User user = new User();
user.setId(1);
user.setUsername("老大");
rabbitTemplate.setConfirmCallback((data, ack, cause) -> {
if (!ack) {
log.error("消息发送失败!" + cause + data.toString());
} else {
log.info("消息发送成功,消息ID:" + (data != null ? data.getId() : null));
}
});
rabbitTemplate.setReturnCallback((message, replyCode, replyText, exchange, routingKey) -> {
log.info("消息丢失:exchange({}),route({}),replyCode({}),replyText({}),message:{}", exchange, routingKey, replyCode, replyText, message);
});
rabbitTemplate.convertAndSend(RabbitConMQConfig.PURCHASE_DIRECT_EXCHANGE, RabbitConMQConfig.PURCHASE_ORDER_ROUTING_KEY, user, new CorrelationData(String.valueOf(user.getId())));
}
消息的可靠投递小结
确认模式:
- 设置ConnectionFactory的publisher-confirm-type=correlated,开启confirm确认模式
- 使用rabbitTemplate.setConfirmCallback设置回调函数,当消息发送到Exchange后回调confirm方法,在方法中判断ack,如果为true,则发送成功。如果为false,则发送失败
回退模式:
- 设置ConnectionFactory的publisher-returns=true,开启confirm确认模式
- 使用rabbitTemplate.setReturnCallback设置spring.rabbitmq.publisher-returns=true,当消息由Exchange路由到queue失败后,如果设置了 spring.rabbitmq.template.mandatory=true,则消息回回退给消息生产者producer,并执行回调函数