本篇文章将介绍另一种高可用的服务架构,以便实现邮件 100% 被投递成功。类似的短信推送等服务,实现逻辑也大体类似。
01、先来一张流程图
图片
本文内容主要围绕这个流程图展开,利用 RabbitMQ 消息队列来实现邮件 100% 被投递,内容涵盖了 RabbitMQ 很多知识点,如:
- 生产者和消费者模型
- 消息发送机制
- 消费确认机制
- 消息的重新投递
- 消息消费失败的处理方案
02、实现思路
- 1.准备一台电脑,并安装 RabbitMQ 服务
- 2.开放 QQ 邮箱或者其它邮箱授权码,用于发送邮件
- 3.创建邮件发送项目并编写代码
- 4.发送邮件测试
- 5.消息消费失败的处理介绍
03、环境准备
3.1、安装 RabbitMQ 服务
安装 RabbitMQ 服务,这一步比较简单,可以访问下面的官方地址,下载软件包并依次按照步骤进行安装即可。
安装成功之后,登陆 RabbitMQ 控制台,可以看到类似于如下界面。
图片
3.1.1、创建交换器
点击“Exchanges”菜单,进入“交换器”管理界面。
图片
进入之后,点击最下方“Add a new exchange”按钮,创建一个类型为topic,名称叫mail.exchange的交换器,并提交。
图片
3.1.2、创建消息队列
接着,点击“Queues”菜单,进入消息队列管理界面。
图片
同样的,点击最下方“Add a new queue”按钮,创建一个名称叫mq.mail.ack的消息队列,并提交。
图片
保存之后,在列表中可以看到刚刚创建的消息队列,然后点击进入详情。
图片
在详情中,将当前消息队列与上文创建的交换器进行绑定,便于后续通过交换器来发送消息到队列,操作如下。
图片
对于topic类型的交换器,通常不直接与消息队列进行交互,而是通过一个路由键,将消息路由到目标消息队列,这样设计的目的是让消息投递更加灵活。路由键,可以简单理解为类似于路由器,对数据进行路由分发处理。
3.2、配置邮箱发送服务器
为了实现邮件自动发送功能,我们还需要准备一个邮箱发送服务器,这一步在之前的文章中已经详细的介绍过,在此,我们简单的再介绍一下。
以 QQ 邮箱为例,登陆进去之后,在设置里面开启 POP3/SMTP 服务,并获取授权码记录下来。
图片
图片
该授权码,就是下文配置文件中spring.mail.password需要的密码!
04、方案实践
4.1、构建项目
在 IDEA 下创建一个名称为smail的 Spring Boot 项目,pom文件中加入amqp和mail相关依赖包,示例如下:
4.2、添加相关配置
在application.properties中添加 rabbitmq、邮箱相关配置,示例如下:
其中,spring.mail.username和spring.mail.password指的就是上文中创建的邮箱账号和授权码,将其配置进去即可。
4.3、编写 RabbitMQ 配置类
编写一个 RabbitMQ 配置类,用于监听消息的发送情况,示例如下。
4.4、编写生产者服务
在 Spring Boot 中,我们可以利用RabbitTemplate工具,将数据通过交换器发送到目标消息队列,示例如下。
4.5、编写消费者服务
在 Spring Boot 中,我们可以利用@RabbitListener注解,监听指定的消息队列,如果队列中有消息会第一时间收到回调,示例如下。
4.6、编写邮件发送服务
正如之前的文章中所介绍的,在 Spring Boot 中,我们可以利用JavaMailSender工具来实现邮件的自动推送,示例如下。
4.7、编写 controller 接口
接着,编写一个 controller 接口,将邮件发送服务暴露出去,示例如下:
4.8、服务测试
最后,启动 SpringBoot 服务,用 postman 来测试一下。
图片
查看控制台信息。
图片
查询接受者邮件信息。
图片
可以清楚的看到,邮件发送成功!
当大批量的发送邮件,也不用担心,因为整个邮件的发送都是异步的,不会阻塞主流程的运行。
05、消费失败的处理方案
虽然以上的方案非常可靠,可以保证发出的消息 100% 被消费,但是其实也有弊端。
试想一下,按照上面的处理逻辑,假设其中有一条消息,因为某种原因一直发送失败,会出现什么样的情况?
此时,这条消息会重新返回队列,然后一直重试,会导致其它的消息可能会无法被消费。
针对这种情况,最简单粗暴的办法就是,当重试失败之后将消息丢弃,不会阻碍其它的消息被正常处理,不过会丢失数据。
那么如何正确的处理消息消费失败的问题呢?
可以借助数据库来记录消费失败的数据,针对系统无法成功处理的消息,人工进行干预。
实践过程如下!
5.1、创建一张消息日志表
首先,在数据库中创建一张消息日志表,用于跟踪消息数据的状态,示例如下:
5.2、改写生产者逻辑
在生产者服务类中,先将消息数据写入数据库,再向 rabbitMQ 服务中发消息,示例如下:
5.3、改写消费者逻辑
在消费者服务类中,收到消息之后,不管处理成功还是失败,都只会修改数据库中的消息状态,并且消息处理失败时,不再重新返回队列。
因为此处采用自动确认模式,因此还需要修改application.properties中的配置参数,内容如下:
5.4、编写定时任务对失败消息进行补偿投递
当消息消费失败时,会自动记录到数据库。
实际上,不可能每条数据都需要我们进行干预,有的可能重试一次就好了,因此可以编写一个定时任务,将消费失败的数据筛选出来,重新放入到消息队列中,只有当消费次数达到设置的最大值,此时进入人工干预阶段,可以节省不少的工作。
示例如下:
最后别忘了,在Application类上添加@EnableScheduling,以便让定时调度生效,示例如下:
利用定时任务,对投递失败的消息进行补偿投递,基本可以保证消息 100% 消费成功!
06、小结
本文主要以实现邮件自动推送这个业务场景为例,通过 Springboot 整合 rabbitMQ 技术来实现高可用的效果。
当然,解决这个业务需求的技术方案还有很多,例如 Springboot 整合 rocketMQ 也可以实现这个效果,不管怎么变,底层的实现思路基本都一样。
希望本篇的知识总结,对大家有所帮助。
最后,代码都经过自测,想要获取项目源码的同学,可以点击如下地址获取。
示例代码地址: