2-RabbitMQ(2)
消息队列在进行消息的发接时,会面临很多问题,比如
● 消息的可靠性:如何保证消息至少被消费一次
● 延迟消息:消息的延迟投递
● 高可用问题:如何避免单点MQ故障导致不可用
● 消息堆积问题:消息的发送速率远大于消息的消费速率,造成消息在队列中堆积
1、消息的可靠性
消息在发送的过程中可能会造成丢失,
● 消息未送达交换机 消息未送达队列
/生产者确认机制 :给消息指定唯一ID,消息发送到MQ以后返回ack/nack
消息成功到交换机,返回ack
消息未到交换机,返回nack
消息到交换机但没到队列,返回ack,并返回失败的原因
● MQ接收到消息后宕机,消息丢失
/MQ持久化:RabbitMQ默认是非持久化的,SpringAMQP默认是持久化的
交换机持久化、队列持久化、消息持久化
SpringAMQP:是基于RabbitMQ封装的一套模板,并且利用SpringBoot对其实现了自动装配
● 消费者接收到消息后未来得及消费就宕机
/消费者确认机制
manual:手动回复ack给队列,在代码执行结束后在返回ack
auto:自动返回ack给队列:由Spring监测,当代码抛出异常时返回nack正常执行完毕时返回ack
yml配置:Spring:rabbitmq:listener:simple:acknowledge-mode:manual #手动返回ack
/失败重试机制
消费者出现异常,消息不断入队列重新发送,无限循环,队列的消息处理飙升
解决上面问题:开启本地重试
在yml进行配置:开启重试机制、初始等待时长、每次失败等待时长曾倍数增长、重试最大次数
1、在开启本地重试后,消费者出现异常没有成功接收到消息,这时消息不会在重新加入队列而是在消费者本地重试,重试次数达到设定的最大次数仍没有成功时,Spring返回ack,消息会被丢弃
2、在1中丢弃的消息,如果是重要的数据可能造成损失,给队列绑定一个存放失败消息的交换机,失败交换机后面在绑定一个队列用于存放失败的消息,后交由人工处理
2、延时消息
● 可以通过死信交换机来实现消息的延时发送
死信:被消费者决绝接收的消息、存放在队列中超时未消费的消息、队列已满在进来新的消息最开始入队列的消息会成为死信
死信交换机:与队列进行绑定,队列中有死信后,死信会存放到死信队列中,然后该死信交换机后面会绑定一个队列用于存放死信
用代码声明死信交换机:deadLetterExchange(“死信交换机名称”);
@Bean //声明一个普通队列与死信交换机的绑定关系
public Queue simpleQueue2(){
return QueueBuilder.durable("simple.queue")
.deadLetterExchange("死信交换机名称")
.builder();
}
@Bean //声明死信交换机
public DirectExchange dlQueue(){
return new DirectExchange("死信交换机名称",true,false);
}
我们在发送消息时会给消息设置一个超时时间,也可以在声明队列时给队列设置一个消息存放的超时时间,超过时间未消费的消息就会变为死信,通过死信交换机存放到死信队列中,通过这种方式,来实现消息的延时发送;
● 也可以通过声明DelayExchange交换机来实现延时消息 //使用延迟队列插件的方式
1、消息的监听
@RabbitListener(
bindings = @QueueBinding(value=@Queue(name="队列名",durable="true")), //durable:开启持久化
exchange = @Exchange(name="交换机名称",delayed=”true”), //delayed:开启delayed属性,就是用delayed属性来实现消息的延时
key = "bingdingKey的值"
)
2、消息的发送
@Autoworad
Private RabbitTemplate rabbitTemplate;
MessageBuilder.withBody("消息内容".getBytes(StandardCharsets.UTF_8))
.setHeader("x-delay",10000) //携带x-delay属性,指定延时时间
.builder();
rabbitlTemplate.convertAndSend("交换机名称","routingKey值","携带x-delay属性和延时时间的消息内容",唯一id)
3、惰性队列 Lazy Queues
当生产者发送消息的速度大于消费者接收消息的速度,就导致队列中消息堆积问题,直到队列中的消息存放达到上限后,消息成为死信
解决消息堆积的两种思路:
● 增加消费者数量,提高消费速度
● 扩大队列容积,提高消息堆积的上限
接收到消息后直接存放的磁盘而非内存,消费者要消费消息时才会从磁盘中读取并加载到内存,支持数百万条的消息存储
基于@Bean来声明惰性队列
@Bean
public Queue lazyQueue(){
return QueueBuilder
.durable("惰性队列名称")
.lazy() //表示该队列为惰性队列
.builder();
}
基于@RabbitListener注解来声明惰性队列