2024年RabbitMQ进阶学习(1),2024年最新原理竟然是这

img
img
img

既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,涵盖了95%以上大数据知识点,真正体系化!

由于文件比较多,这里只是将部分目录截图出来,全套包含大厂面经、学习笔记、源码讲义、实战项目、大纲路线、讲解视频,并且后续会持续更新

需要这份系统化资料的朋友,可以戳这里获取


我们利用命令停掉RabbitMQ服务:



docker stop mq


然后测试发送一条消息,会发现会每隔1秒重试1次,总共重试了3次。消息发送的超时重试机制配置成功了!



**注意**:当网络不稳定的时候,利用重试机制可以有效提高消息发送的成功率。不过SpringAMQP提供的重试机制是**阻塞式**的重试,也就是说多次重试等待的过程中,当前线程是被阻塞的。通俗来说在等待期间,代码卡在那里了,不会向下运行了。


        如果对于业务性能有要求,建议**禁用**重试机制。如果一定要使用,请合理配置等待时长和重试次数,当然也可以考虑使用异步线程来执行发送消息的代码。



### 1.2.生产者确认机制


相比于生产者重试机制,生产者确认机制侧重的是:消息发送时,失败了怎么办。


一般情况下,只要生产者与MQ之间的网路连接顺畅,基本不会出现发送消息丢失的情况,因此大多数情况下我们无需考虑这种问题。


不过,在少数情况下,也会出现消息发送到MQ之后丢失的现象,比如:


* MQ内部处理消息的进程发生了异常
* 生产者发送消息到达MQ后未找到`Exchange`
* 生产者发送消息到达MQ的`Exchange`后,未找到合适的`Queue`,因此无法路由



针对上述情况,RabbitMQ提供了生产者消息确认机制,包括`Publisher Confirm`和`Publisher Return`两种。在开启确认机制的情况下,当生产者发送消息给MQ后,MQ会根据消息处理的情况返回不同的**回执**。


具体如图所示:


![](https://img-blog.csdnimg.cn/direct/4cae180196a84b2c8979ad6625274ca3.png)


总结如下:


* 当消息投递到MQ,但是**路由失败**时,通过**Publisher Return**返回异常信息,同时返回ack的确认信息,代表投递成功。这种情况一般是RoutingKey填写失败,跟我MQ发送没有关系!
* 临时消息投递到了MQ,并且入队成功,返回ACK,告知投递成功
* 持久消息投递到了MQ,并且入队完成持久化(保存到磁盘)后,才会返回ACK ,告知投递成功
* 其它情况都会返回NACK,告知投递失败


比如说:消息投递到交换机,结果还没来得及持久化到磁盘,结果磁盘因为某种故障 (比如说磁盘满了);MQ内部出现异常 (比如说内存爆满)导致消息丢失。这些情况都会返回NACK。




其中`ack`和`nack`属于**Publisher Confirm**机制,`ack`是投递成功;`nack`是投递失败。而`return`则属于**Publisher Return**机制。


默认两种机制都是关闭状态,需要通过配置文件来开启。



### 1.3.实现生产者确认


        在我们上面分析的生产者确认机制里面:生产者发消息,MQ给我们回值,我们应该去接收这个值。这里就有两种方法:1.同步等待,发了消息就开始等待,等着MQ给我回值。2.采用异步回调的方式,生产者发了消息,就干别的事了。当MQ回值来了以后我再去处理就行了。


当然这种异步的方式会好一点!接下来我们就采用这种方式。


#### 1.3.1.开启生产者确认


在publisher模块的`application.yaml`中添加配置:



spring:
rabbitmq:
publisher-confirm-type: correlated # 开启publisher confirm机制,并设置confirm类型
publisher-returns: true # 开启publisher return机制,专门用来返回路由失败消息的


这里`publisher-confirm-type`有三种模式可选:


* `none`:关闭confirm机制
* `simple`:同步阻塞等待MQ的回执
* **`correlated`**:MQ异步回调返回回执



一般我们推荐使用**`correlated`,异步回调机制**。



#### 1.3.2.定义ReturnCallback (回调函数)


**每个`RabbitTemplate`只能配置一个`ReturnCallback`**,**因此我们可以在配置类中统一设置。我们在publisher模块定义一个配置类:**


![](https://img-blog.csdnimg.cn/direct/abd9793e7e054ab584f2111a5eb1a960.png)


内容如下:



package com.itheima.publisher.config;

import lombok.AllArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.amqp.core.ReturnedMessage;
import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.context.annotation.Configuration;

import javax.annotation.PostConstruct;

@Slf4j
@AllArgsConstructor
@Configuration
public class MqConfig {
private final RabbitTemplate rabbitTemplate;

@PostConstruct
public void init(){
    rabbitTemplate.setReturnsCallback(new RabbitTemplate.ReturnsCallback() {
        @Override
        public void returnedMessage(ReturnedMessage returned) {
            log.error("触发return callback,");
            log.debug("exchange: {}", returned.getExchange());
            log.debug("routingKey: {}", returned.getRoutingKey());
            log.debug("message: {}", returned.getMessage());
            log.debug("replyCode: {}", returned.getReplyCode());
            log.debug("replyText: {}", returned.getReplyText());
        }
    });
}

}


#### 1.3.3.定义ConfirmCallback


由于每个消息发送时的处理逻辑不一定相同,因此ConfirmCallback需要在每次发消息时定义。具体来说,是在调用RabbitTemplate中的convertAndSend方法时,多传递一个参数:


![](https://img-blog.csdnimg.cn/direct/0273a6af81fc483b81fe6c09c8101935.png)


这里的CorrelationData中包含两个核心的东西:


* `id`:消息的唯一标示,MQ对不同的消息的回执以此做判断,避免混淆
* `SettableListenableFuture`:回执结果的Future对象



将来MQ的回执就会通过这个`Future`来返回,我们可以提前给`CorrelationData`中的`Future`添加回调函数来处理消息回执:


![](https://img-blog.csdnimg.cn/direct/829d5bdb05ea418c82e58fd0f023c967.png)



我们新建一个测试,向系统自带的交换机发送消息,并且添加`ConfirmCallback`:



@Test
void testPublisherConfirm() {
// 1.创建CorrelationData
CorrelationData cd = new CorrelationData();
// 2.给Future添加ConfirmCallback
cd.getFuture().addCallback(new ListenableFutureCallback<CorrelationData.Confirm>() {
@Override
public void onFailure(Throwable ex) {
// 2.1.Future发生异常时的处理逻辑,基本不会触发
log.error(“send message fail”, ex);
}
@Override
public void onSuccess(CorrelationData.Confirm result) {
// 2.2.Future接收到回执的处理逻辑,参数中的result就是回执内容
if(result.isAck()){ // result.isAck(),boolean类型,true代表ack回执,false 代表 nack回执
log.debug(“发送消息成功,收到 ack!”);
}else{ // result.getReason(),String类型,返回nack时的异常描述
log.error(“发送消息失败,收到 nack, reason : {}”, result.getReason());
}
}
});
// 3.发送消息
rabbitTemplate.convertAndSend(“hmall.direct”, “q”, “hello”, cd);
}


**CollelationData对象**:这个对象里面有一个唯一ID(UUID)当前消息的一个标识。每次发消息都有一个CollelationData对象,因为每个消息都有自己的消息id。将来消息到MQ以后,MQ也能区分每个消息谁是谁。将来做回调的时候,每个消息的回调函数可能不同。


**注意**这个onFailure和onSuccess方法,是指回调有没有成功。不是指消息执行有没有成功。而onSuccess里根据ack还是nack,才能知道消息有没有发送成功!


执行结果如下:


![](https://img-blog.csdnimg.cn/direct/6f8d642983f542879e37d3394c3ad9d6.png)


可以看到,由于传递的`RoutingKey`是错误的,路由失败后,触发了`return callback`,同时也收到了ack。


当我们修改为正确的`RoutingKey`以后,就不会触发`return callback`了,只收到ack。


而如果连交换机都是错误的,则只会收到nack。



**注意**:


开启生产者确认比较消耗MQ性能,一般不建议开启。而且大家思考一下触发确认的几种情况:


* 路由失败:一般是因为RoutingKey错误导致,往往是编程导致
* 交换机名称错误:同样是编程错误导致
* MQ内部故障:这种需要处理,但概率往往较低。因此只有对消息可靠性要求非常高的业务才需要开启,而且仅仅需要开启ConfirmCallback处理nack就可以了。




## 2.MQ的可靠性


消息到达MQ以后,如果MQ不能及时保存,也会导致消息丢失,所以MQ的可靠性也非常重要。


### 2.1.数据持久化


为了提升性能,默认情况下MQ的数据都是在**内存存储**的临时数据,重启后就会消失。为了保证数据的可靠性,必须配置数据持久化,包括:


* 交换机持久化
* 队列持久化
* 消息持久化


我们以控制台界面为例来说明。


#### 2.1.1.交换机持久化


在控制台的`Exchanges`页面,添加交换机时可以配置交换机的`Durability`参数:


![](https://img-blog.csdnimg.cn/direct/e1cba1fbb793471c8e88de1b39cc4128.png)


设置为`Durable`就是持久化模式,`Transient`就是临时模式。



#### 2.1.2.队列持久化


在控制台的Queues页面,添加队列时,同样可以配置队列的`Durability`参数:


![](https://img-blog.csdnimg.cn/direct/9aa562471c0a48daa5651bb0b4c9339f.png)


除了持久化以外,你可以看到队列还有很多其它参数,有一些我们会在后期学习。



#### 2.1.3.消息持久化


在控制台发送消息的时候,可以添加很多参数,而消息的持久化是要配置一个`properties`:


![](https://img-blog.csdnimg.cn/direct/5937ddc8f84a4c2b9c55c1ca5d712cf3.png)



**说明**:在开启持久化机制以后,如果同时还开启了生产者确认,那么MQ会在消息持久化以后才发送ACK回执,进一步确保消息的可靠性。


不过出于性能考虑,为了减少IO次数,发送到MQ的消息并不是逐条持久化到数据库的,而是每隔一段时间批量持久化。一般间隔在100毫秒左右,这就会导致ACK有一定的延迟,因此建议生产者确认全部采用异步方式。



### 2.2.LazyQueue


在默认情况下,RabbitMQ会将接收到的信息保存在内存中以降低消息收发的延迟。但在某些特殊情况下,这会导致消息积压,比如:


* 消费者宕机或出现网络故障
* 消息发送量激增,超过了消费者处理速度
* 消费者处理业务发生阻塞



一旦出现消息堆积问题,RabbitMQ的内存占用就会越来越高,直到触发内存预警上限。此时RabbitMQ会将内存消息刷到磁盘上,这个行为成为`PageOut`. `PageOut`会耗费一段时间,并且会阻塞队列进程。因此在这个过程中RabbitMQ不会再处理新的消息,生产者的所有请求都会被阻塞。



为了解决这个问题,从RabbitMQ的3.6.0版本开始,就增加了Lazy Queues的模式,也就是惰性队列。惰性队列的特征如下:


* 接收到消息后直接存入磁盘而非内存
* 消费者要消费消息时才会从磁盘中读取并加载到内存(也就是懒加载)
* 支持数百万条的消息存储


![img](https://img-blog.csdnimg.cn/img_convert/3e22ab5196f05c24611a6c9742e88295.png)
![img](https://img-blog.csdnimg.cn/img_convert/bcb54c0b881049d977e8de0bef72b6b3.png)
![img](https://img-blog.csdnimg.cn/img_convert/8c8f18b0c119edf955f4361655abc010.png)

**既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,涵盖了95%以上大数据知识点,真正体系化!**

**由于文件比较多,这里只是将部分目录截图出来,全套包含大厂面经、学习笔记、源码讲义、实战项目、大纲路线、讲解视频,并且后续会持续更新**

**[需要这份系统化资料的朋友,可以戳这里获取](https://bbs.csdn.net/forums/4f45ff00ff254613a03fab5e56a57acb)**

(img-ZJCSKoHa-1715230860454)]
[外链图片转存中...(img-bxrNo2ml-1715230860455)]
[外链图片转存中...(img-9rQbxo1z-1715230860455)]

**既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,涵盖了95%以上大数据知识点,真正体系化!**

**由于文件比较多,这里只是将部分目录截图出来,全套包含大厂面经、学习笔记、源码讲义、实战项目、大纲路线、讲解视频,并且后续会持续更新**

**[需要这份系统化资料的朋友,可以戳这里获取](https://bbs.csdn.net/forums/4f45ff00ff254613a03fab5e56a57acb)**

  • 4
    点赞
  • 10
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
回答: RabbitMQ是一个消息队列中间件,它可以被看作是一个服务节点或者服务实例,通常也可以将其视为一台服务器。\[1\]在生产环境中,由于一些不明原因导致RabbitMQ重启,可能会导致消息投递失败和消息丢失。为了确保消息的可靠投递,可以采用事务机制或者使用发送方确认机制。事务机制可以解决消息发送方和RabbitMQ之间消息确认的问题,只有消息成功被RabbitMQ接收,事务才能提交成功,否则可以进行事务回滚和消息重发。然而,使用事务机制会对RabbitMQ的性能产生一定的影响。另一种方法是使用发送方确认机制,通过该机制可以实现消息的可靠投递,即发送方在消息被RabbitMQ确认接收之后才认为消息已经成功投递。\[2\]\[3\]这样可以在RabbitMQ集群不可用的情况下,对无法投递的消息进行处理和恢复。 #### 引用[.reference_title] - *1* [Rabbitmq进阶](https://blog.csdn.net/weixin_46634416/article/details/124755747)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v91^insert_down1,239^v3^insert_chatgpt"}} ] [.reference_item] - *2* [RabbitMQ进阶](https://blog.csdn.net/weixin_73198745/article/details/130910185)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v91^insert_down1,239^v3^insert_chatgpt"}} ] [.reference_item] - *3* [RabbitMQ入门进阶高级详解(内容有点多)](https://blog.csdn.net/sinat_16658263/article/details/124211232)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v91^insert_down1,239^v3^insert_chatgpt"}} ] [.reference_item] [ .reference_list ]

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值