RabbitMQ(三)——延迟队列的第二种实现方式(插件)、消息发送可靠性(两种思路、开启事务、发送方确认机制)、自带的重试机制(Spring自带、业务重试——入职发送邮件、消费失败重试、幂等性处理)

4 篇文章 2 订阅
3 篇文章 0 订阅

RabbitMQ(三)——延迟队列的第二种实现方式(插件)、消息发送可靠性(两种思路、开启事务、发送方确认机制)、自带的重试机制(Spring自带、业务重试——发送失败重试、消费失败重试+幂等性处理)

一、延迟队列的第二种实现方式(插件)

第一种方式在上篇博文中。

接下来讲第二种方式。

1、使用插件

使用插件的方式主要就是在消息头中设置消息的延迟时间。

a、安装和使用

第二种方式就是使用插件:rabbitmq_delayed_message_exchange

首先我们需要下载 rabbitmq_delayed_message_exchange 插件,这是一个 GitHub 上的开源项目,我
们直接下载即可:

https://github.com/rabbitmq/rabbitmq-delayed-message-exchange/releases

选择适合自己的版本,我这里选择最新的 3.9.0 版。
下载完成后在命令行执行如下命令将下载文件拷贝到 Docker 容器中去:

docker cp ./rabbitmq_delayed_message_exchange-3.9.0.ez some-rabbit:/plugins

这里第一个参数是宿主机上的文件地址,第二个参数是拷贝到容器的位置。

接下来再执行如下命令进入到 RabbitMQ 容器中:

docker exec -it some-rabbit /bin/bash

进入到容器之后,执行如下命令启用插件:

rabbitmq-plugins enable rabbitmq_delayed_message_exchange

启用成功之后,还可以通过如下命令查看所有安装的插件,看看是否有我们刚刚安装过的插件,如下:

rabbitmq-plugins list

在这里插入图片描述
有 E* 的就是安装的了

b、前期准备

在这里插入图片描述
在这里插入图片描述

在这里插入图片描述

c、代码实现

在这里插入图片描述
在这里插入图片描述

消费者:
在这里插入图片描述
接口:
在这里插入图片描述
效果:
在这里插入图片描述
可以看到,刚好 5 秒。

二、消息发送可靠性

1、两种消费思路

RabbitMQ 的消息消费,整体上来说有两种不同的思路:

  • 推(push):MQ主动将消息推送给消费者,这种方式需要消费者设置一个缓冲区去缓存消息,对于消费者而言,内存中总是有一堆需要处理的消息,所以这种方式的效率比较高,这也是目前大多数应用采用的消费方式。
  • 拉(pull):消费者主动从 MQ 拉取消息,这种方式效率并不是很高,不过有的时候如果服务端需要批量拉取消息,倒是可以采用这种方式。

先看推:
在这里插入图片描述
当监听的队列中有消息时,就会触发该方法。

再来看拉(pull):
在这里插入图片描述
调用 receiveAndConvert 方法,方法参数为队列名称,方法执行完成后,会从 MQ 上拉取一条消息下来,如果该方法返回值为 null,表示该队列上没有消息了。receiveAndConvert 方法有一个重载方法,可以在重载方法中传入一个等待超时时间,例如 3 秒。此时,假设队列中没有消息了,则receiveAndConvert 方法会阻塞 3 秒,3 秒内如果队列中有了新消息就返回,3 秒后如果队列中还是没有新消息,就返回 null,这个等待超时时间要是不设置的话,默认为 0。

这是消息两种不同的消费模式。

如果需要从消息队列中持续获得消息,就可以使用推模式;如果只是单纯的消费一条消息,则使用拉模式即可。切忌将拉模式放到一个死循环中,变相的订阅消息,这会严重影响 RabbitMQ 的性能。

2、 确保消费成功的两种思路

对于消息消费成功,其实官方提供了相关的机制。

为了保证消息能够可靠的到达消息消费者,RabbitMQ 中提供了消息消费确认机制。当消费者去消费消息的时候,可以通过指定 autoAck 参数来表示消息消费的确认方式:

  • 当 autoAck 为 false 的时候,此时即使消费者已经收到消息了,RabbitMQ也不会立马将消息移除,而是等待消费者显式的回复确认信号后,才会将消息打上删除标记,然后再删除。
  • 当 autoAck 为 true的时候,此时MQ就会自动把发送出去的消息设置为确认,然后将消息移除(从内存或者磁盘中),即使这些消息并没有到达消费者。

来看一张图:
在这里插入图片描述如上图所示,在 RabbitMQ 的 web 管理页面:

  • Ready 表示待消费的消息数量。
  • Unacked 表示已经发送给消费者但是还没收到消费者 ack 的消息数量。

当我们将 autoAck 设置为 false 的时候,对于 RabbitMQ 而言,消费分成了两个部分:

  • 待消费的消息
  • 已经投递给消费者,但是还没有被消费者确认的消息

当设置 autoAck 为 false 的时候,消费者就变得非常从容了,它将有足够的时间去处理这条消息,当消息正常处理完成后,再手动 ack,此时 RabbitMQ 才会认为这条消息消费成功了。如果RabbitMQ 一直没有收到客户端的反馈,并且此时客户端也已经断开连接了,那么 RabbitMQ 就会将刚刚的消息重新放回队列中,等待下一次被消费。

综上所述,确保消息被成功消费,无非就是手动 Ack 或者自动 Ack,无他。当然,无论这两种中的哪一种,最终都有可能导致消息被重复消费,所以一般来说我们还需要在处理消息时,解决幂等性问题。

总而言之:只要确保消息到达队列了就算消息发送成功了。因为队列之后的事情不归我们管,是消费者的事情。

如何确保消息成功到达 RabbitMQ?RabbitMQ 给出了两种方案:

  1. 开启事务机制
  2. 发送方确认机制

这是两种不同的方案,不可以同时开启,只能选择其中之一,如果两者同时开启,则会报如下错误
在这里插入图片描述

3、开启事务机制(了解即可)

首先需要配置一个事务管理器, 发消息的时候配置一个事务就行。如果这个代码里面出错的话,就会回滚。通过加事务的方式确保这个消息发送成功。

a、代码实现

在这里插入图片描述

消息发送:
在这里插入图片描述
接口:
在这里插入图片描述
在消息发送那一块,通过加注解的方式开启事务,可以自行尝试故意出错,如果出错了这个消息会自动回滚。

消息发送使用了 @Transactional 注解,跟使用 mysql 的时候一样,因为 Spring 已经把事务的用法统一起来了。规则都是一样的。

b、为什么不选择事务方式

为什么事务模式了解就行呢?因为开启事务后会多了四个步骤:
在这里插入图片描述

在这里插入图片描述

4、发送方确认机制

再次强调,这种方式跟上面的事务方式不能共存,同时开启会报错:
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

a、代码实现

首先是 config 配置文件,先去掉事务管理:
在这里插入图片描述
其他都不变,再加点东西:
在这里插入图片描述在这里插入图片描述
发送消息:
在这里插入图片描述
到这里代码就搞完了。

可以自行测试 交换机出问题或者队列出问题。比如直接注释掉交换机就行(在 send 方法里注释)。

5、自带的重试机制

失败重试分两种情况,一种是压根没找到 MQ 导致的失败重试(一般是没启动 MQ,或者网络问题)。
另一种是找到 MQ 了,但是消息发送失败了。

a、自带重试机制(Spring 自带,与 MQ 无关)

不仅是 MQ,其他地方也可使用。

在这里插入图片描述

b、业务重试

在这里插入图片描述
在这里插入图片描述
具体的业务重试看下一章!

三、业务重试

1、RabbitMQ 员工入职发送邮件

这里拿了一个 SpringBoot 管理系统的的项目拆分之后,再加了一个发送消息的 SpringBoot 项目,两个项目一个发一个收消息来测试。

这里先只搞一部分需求,需求是员工成功入职了就模拟发送一个邮件,发送到员工的邮箱中去,发送内容:欢迎入职。这里只是模拟这个功能,所以邮件展示在控制台中。接着就是每次发送邮件都会有日志记录这个邮件的信息。

数据库 mapper 层的代码在最后的章节中有展示,前面先展示 MQ 业务相关的代码。 mapper 层没有什么特别的,就是普通的数据库 增删改查之类的。

a、实体类和前置准备

发送邮件项目的注册文件:
在这里插入图片描述

这里仅展示部分重要的实体类:

员工类序列化:
在这里插入图片描述

邮件类序列化:
在这里插入图片描述

发生邮件日志类:
在这里插入图片描述

b、模拟添加员工接口:

模拟员工入职添加员工的接口:
在这里插入图片描述

c、发送消息

这里说句题外话,如果这里 MQ 有事务,数据库也有事务,那么开启事务的注解是指定哪个事务呢?

一般来说是不可能两个一起存在,只能存在一个,如果真的有两个,可以通过属性 transactionManager = “xx” 来指定事务管理器。默认情况下,开启事务的注解只对数据库事务有效。
在这里插入图片描述
在这里插入图片描述

d、启动类添加注解

开启定时任务:
在这里插入图片描述

e、重复发送邮件,一定次数后设为失败

在这里插入图片描述

f、service 的 config

在这里插入图片描述
在这里插入图片描述

g、消费邮件项目配置文件

在这里插入图片描述

h、消费方的 config

在这里插入图片描述

i、消费邮件

在这里插入图片描述
到此这一部分的代码就结束了。

如果需要测试,就正常的登录后然后访问前面那个接口即可。一个是看数据库的日志,一个是看控制台是否有输出入职的相关信息。

2、消费失败重试

上面的代码还不完整,就比如消费的时候,还没发邮件,或者正在构建邮件的时候,突然有异常,比如这样:

跟上面那样正常测试,此时会看到消费者那边正在无限循环的报错;其实消息已经发出去了,发给消费者,但是消费者还没告诉 MQ 已经消费成功了;造成这样的原因有两点:

1、消费者的的项目的配置文件多写了两行:
在这里插入图片描述

这里需要注释掉。

此时再次运行,还是会一直报错。

2、消费者里边已经默认开启事务了。
在这里插入图片描述

如果失败,会自动的进行重试,所以会不停的一直打印错误信息。如果抛异常,这个消息会重新回到 MQ 里面等待消费者过来消费。这里可以配置,手动告诉 MQ 是否消费成功:
在这里插入图片描述

这个时候再次重试,虽然一开始会报错,但是只会报一次错误,不会像之前那样一直的重复报错。因为已经切换成手动,需要我们自己去手动的告诉 MQ 消费成功还是失败。

a、完善代码,手动告诉 MQ 消费成功或失败

basicAck:确认消息消费成功。
basicNack:确认消息消费失败。

一般是用上面这两个就够了。

消费者:
在这里插入图片描述

3、消息的幂等性处理

注意:这个跟 redis 那个不同,redis 处理的是接口幂等性,这个是消息幂等性。

但是上面的代码还不够,可能会存在消息发送重复,所以这里消费的时候还需要作幂等性处理:
解决思路是:如果发过来的消息已经处理过了,就不去管;如果还没处理,再去管。一般都是结合 redis 来做。 看下图:
在这里插入图片描述

a、前期准备

添加 redis 依赖,这里一开始是应该添加了。
还有消费者的配置文件:
在这里插入图片描述

b、幂等性处理代码

这里直接截图部分代码,红色框框内为新增代码,剩余的跟上面一样(注意看注释,很多重要的点):
在这里插入图片描述
到这里代码就完整了。

c、测试

如果要测试,这里这么设置:

MailSendLogScheduled 类:
在这里插入图片描述
发送多一条重复的消息进行测试。

4、mapper 层代码展示

想了又想,还是决定贴出 mapper 层的代码供日后查看:

a、EmployeeMapper

在这里插入图片描述
在这里插入图片描述

b、MailSendLogMapper

在这里插入图片描述
在这里插入图片描述

  • 2
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值