1、RabbitMQ消息确认机制-可靠抵达
前言:为了保证消息不丢失,可靠抵达,可以使用事务消息(客户端与broker建立连接在通道中进行收发消息,设置通道为事务模式,只有当发送成功,或者消费成功,mq给出响应等一连串反应成功之后才算成功),但是官方说性能会下降250倍,为了保证高并发,所以就引入了消息的确认机制
eg:publisher发送给broker,网络波动没发成功,发成功了broker没接收到,consumer消费一半网络波动导致消息没接收到等各种场景
保证可靠抵达就是保证消息可靠的发送到broker以及consumer可靠的消费这条消息
1.1可靠抵达-confirmCallback
spring.rabbitmq.publisher-confirm-type=correlated
①在创建connectFactory的时候设置PublisherConfirm(true)选项,开启confirmCallback
②CorrelationData:用来表示当前消息唯一性
③消息只要被broker接收到就会执行confirmCallback,如果是cluster模式,需要所有broker接受到才会调用confirmCallback
④被broker接收到只能标识message已经抵达服务器,并不能保证消息一定会投递到queue中,所以需要用到E->Q的确认机制(只要投递失败就会触发returnCallback方法)
1.2可靠抵达-returnCallback
spring.rabbitmq.publisher-returns=true
spring.rabbitmq.template.mandatory=true
①confirm模式只能保证消息抵达broker,不能保证消息准确投递到目标queue中,在有些业务场景下,我们需要保证消息一定要投递到目标queue中,此时就需要returnCallback退回模式
②这样如果未能投递到目标queue里将调用returnCallback,可以记录下详细要投递的数据,定期巡查或者自动纠错都需要这些数据
1.3可靠抵达-Ack消息确认机制
①消费者获取到消息,成功处理,可以恢复Ack给Broker
basic.ack用于肯定确认,broker将移除此消息
basic.nack用于否定确认,可以指定broker是否丢弃此消息,可以批量
basic.reject用于否定确认,可以指定broker是否丢弃此消息,但是不能批量
②默认消息被消费者收到,就会从broker的queue中移除
③queue无消费者,消息依然会被存贮,直到消息被消费者消费
④消费者收到消息,默认会自动ack,但是如果无法确认此消息是否被处理完成或者成功处理,可以开启手动ack模式
消息处理成功,ack(),接受下一个消息,此消息会被broker移除
消息处理失败,nack()/reject(),重新发送给其他人进行处理,或者容错处理后ack
消息一直没调用ack()/nack()方法,broker认为此消息正在被处理,不会投递给别人,此时客户端断开,消息不会被broker移除,会投递给别人
import org.springframework.amqp.core.ReturnedMessage;
import org.springframework.amqp.rabbit.connection.CorrelationData;
import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.amqp.support.converter.Jackson2JsonMessageConverter;
import org.springframework.amqp.support.converter.MessageConverter;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import javax.annotation.PostConstruct;
@Configuration
public class MyRabbitConfig {
@Autowired
private RabbitTemplate rabbitTemplate;
/**
* 使用JSON序列化机制,进行消息转换
*/
@Bean
public MessageConverter messageConverter() {
return new Jackson2JsonMessageConverter();
}
/**
* 定制RabbitTemplate 实现自己的消息确认机制
* 1、服务器收到消息就会回调
* 1.1 spring.rabbitmq.publisher-confirms=true
* 1.2 设置确认回调ConfirmCallback
* 2、消息正确抵达队列进行回调
* 3、消费端确认(保证每一个消息被正确消费,此时broker才可以删除这个消息)
* 3.1默认是自动确认的,只要消息接收到,客户端会自动确认,服务端就会移除这个消息
* 问题:收到一个消息处理成功后,宕机了,所有消息都会丢失,因为自动确认了,所以要手动确认
* 手动确认模式下,只要美誉哦明确告诉mq货物已被签收,相当于没有ack消息就一直是unacked状态,即使服务器宕机,消息也不会丢失,会重新变为read状态
* 3.2如何签收 channel.basicAck(deliveryTag, false);
*/
// MyRabbitConfig对象创建完成后,执行这个方法
@PostConstruct
public void initRabbitTemplate() {
// 设置确认回调
rabbitTemplate.setConfirmCallback(new RabbitTemplate.ConfirmCallback() {
/**
* @param correlationData 当前消息的唯一关联数据(消息的唯一id)
* @param ack 消息是否成功收到
* @param cause 失败的原因
*/
@Override
public void confirm(CorrelationData correlationData, boolean ack, String cause) {
System.out.println("confirm...correlationData[" + correlationData + "]==>ack[" + ack + "]==>cause[" + cause + "]");
}
});
// 设置消息抵达队列的确认回调
rabbitTemplate.setReturnsCallback(new RabbitTemplate.ReturnsCallback() {
/**
* 只要消息美誉哦投递给指定的队列就会触发回调,类似于失败回调
*/
@Override
public void returnedMessage(ReturnedMessage returnedMessage) {
System.out.println("投递失败的消息详细信息"+returnedMessage.getMessage());
System.out.println("回复的状态码"+returnedMessage.getReplyCode());
System.out.println("回复的文本内容"+returnedMessage.getReplyText());
System.out.println("当时这个消息发送给哪个交换机"+returnedMessage.getExchange());
System.out.println("当时这个消息用的哪个路由键"+returnedMessage.getRoutingKey());
System.out.println("fail message");
}
});
}
}