1. 消息可靠性投递
概念:
在使用 RabbitMQ 的时候,作为消息发送方希望杜绝任何消息丢失或者投递失败场景。RabbitMQ 为我们提供了两种方式用来控制消息的投递可靠性模式。
消息投递步骤:
生产者---->交换机------>队列中。
为了保证消息的可靠性投递,提供了两种方式
- confirm:确认模式
- return:退回模式
2. 确认模式和退回模式
需要先开启确认模式:
spring:
#rabbitMQ的ip
rabbitmq:
host: 192.168.213.188
#开启rabbitMQ的生产方确认模式
publisher-confirm-type: correlated
# 开启发布者退回模式
publisher-returns: true
/**
* 保证发送方到交换机的可靠性。
* 1.开启confirm模式,publisher-confirm-type: correlated
* 2.设置rabbitTemplate的确认回调函数。如果消息到达交换机则返回true,如果消息没有到达交换机则返回一个false
*/
rabbitTemplate.setConfirmCallback(new RabbitTemplate.ConfirmCallback() {
@Override
public void confirm(CorrelationData correlationData, boolean b, String s) {
if(b){//消息没有到达交换机 根据业务需求。
System.out.println("消息到达交换机");
}
}
});
/**
* 退回模式:
* 1. 开启退回模式。
* 2. 设置RabbitTemplate的退回回调函数。
*/
rabbitTemplate.setReturnsCallback(new RabbitTemplate.ReturnsCallback() {
@Override
public void returnedMessage(ReturnedMessage returnedMessage) {
//只要交换机到队列失败时才会触发该方法。 可以继续发送也可以取消相应的业务功能。
System.out.println("消息从交换机到队列失败"+returnedMessage.getReplyText());
}
});
3. 消费者的手动确认模式
设置了手动确认方式,则需要在业务处理成功后,调用channel.basicAck(),手动签收,如果出现异常,则调用channel.basicNack()方法,让其自动重新发送消息。
spring:
rabbitmq:
host: 192.168.213.188
listener:
simple:
#表示手动确认,none表示手动确认模式
acknowledge-mode: manual
# 设置每次最大消费的个数。
prefetch: 100```
```java
try {
System.out.println("处理业务逻辑");
//消费端手动确认消息
// long deliveryTag, 表示的标识。
// boolean multiple:是否允许多确认
channel.basicAck(deliveryTag,true); //从队列中删除该消息。
}catch (Exception e){
//(long deliveryTag, boolean multiple, boolean requeue: 是否让队列再次发送该消息。
channel.basicNack(deliveryTag,true,true);
}
4. 想要保证消息的可靠性必须保证以下四点
- 保证消息从发送者到交换机的可靠性: 使用Confirm确认机制。
- 保证消息从交换机到队列的可靠性; 使用return回退机制。
- 消息在队列中的可靠性。 设置队列和消息的持久化。
- 保证消息从队列到消费者的可靠性。 使用消费端的手动确认机制。
5. 设置消息和队列的过期时间(TTL)
//设置消息的过期时间,该消息必须在队列的头部时才会被移除
MessagePostProcessor messagePostProcessor = new MessagePostProcessor() {
@Override
public Message postProcessMessage(Message message) throws AmqpException {
message.getMessageProperties().setExpiration("20000");
return message;
}
};
//String exchange, String routingKey, Object message, MessagePostProcessor messagePostProcessor
rabbitTemplate.convertAndSend("myexchange", "", "daiji"+i, messagePostProcessor);
//设置队列的过期时间,队列的过期时间:从一个消息进入队列开始计算
//创建队列
@Bean(value = "queue")
public Queue queue(){
Queue queue= QueueBuilder.durable(queue_name).withArgument("x-message-ttl",20000).build();
return queue;
}
6. 死信队列
什么是死信队列
死信队列,英文缩写:DLX 。Dead Letter Exchange(死信交换机),当消息成为Dead message后,可以被重新发送到另一个交换机,这个交换机就是DLX。
消息成为死信消息的三种情况
- 队列消息长度到达限制;
- 消费者拒接消费消息,basicNack/basicReject,并且不把消息重新放入原目标队列,requeue=false;
- 原队列存在消息过期设置,消息到达超时时间未被消费;
队列绑定死信交换机
设置队列的参数
- x-dead-letter-exchange:绑定指定的死信交换机
- x-dead-letter-routing-key:绑定指定的路由key
7. 延迟队列
使用RabbitMQ创建延迟队列:利用死信队列+设置消息队列的过期时间;
实现:设置消息队列的消息过期时间,将消息队列绑定死信交换机,消费者监听死信交换机的死信队列。
8. 消息的幂等性
1. 什么是消息的幂等性
如果消息重试多次,消费者端对该重复消息消费多次与消费一次的结果是相同的,并且多次消费没有对系统产生副作用,那么我们就称这个过程是消息幂等的。
2. 如何保证消息的幂等性
可以通过redis来保证
- 消费者获取到消息之后先去redis中查询是否已经存在该消息
- 如果不存在就正常消费消息,消费完之后,将消息存入redis
- 如果存在就证明消息被消费过,直接丢弃。
9. 使用代码创建RabbitMQ
@Configuration
public class ProductCfg {
private static final String DEAD_EXCHANGE = "dead_direct_exchange";
private static final String EXCHANGE = "direct_exchange";
private static final String QUEUE = "direct_queue";
private static final String DEAD_QUEUE = "dead_direct_queue";
@Bean//创建交换机
public Exchange exchange(){
return ExchangeBuilder.directExchange(EXCHANGE).build();
}
@Bean//创建死信交换机
public Exchange deadExchange(){
return ExchangeBuilder.directExchange(DEAD_EXCHANGE).build();
}
@Bean//创建队列,绑定死信交换机
public Queue queue(){
return QueueBuilder.durable(QUEUE)
.withArgument("x-message-ttl", 20000)
.withArgument("x-max-length", 10)
.withArgument("x-dead-letter-exchange", DEAD_EXCHANGE)
.withArgument("x-dead-letter-routing-key", "error")
.build();
}
@Bean//创建死信队列
public Queue deadQueue(){
return QueueBuilder.durable(DEAD_QUEUE).build();
}
@Bean//普通队列与普通交换机绑定
public Binding binding(){
return BindingBuilder.bind(queue()).to(exchange()).with("error").noargs();
}
@Bean//死信队列绑定死信交换机
public Binding deadBinding(){
return BindingBuilder.bind(deadQueue()).to(deadExchange()).with("error").noargs();
}
}