利用SpringBoot+rabbitmq 实现邮件异步发送,保证100%投递成功

在之前的文章中,我们详细介绍了 SpringBoot 整合 mail 实现各类邮件的自动推送服务。

但是这类服务通常不稳定,当出现网络异常的时候,会导致邮件推送失败。

本篇文章将介绍另一种高可靠的服务架构,实现邮件 100% 被投递成功。类似的短信自动发送等服务也大体相同。

一、先来一张流程图

本文内容主要围绕这个流程图展开,利用 RabbitMQ 消息队列来实现邮件 100% 被投递,内容涵盖了 RabbitMQ 很多知识点,如:

  • 生产者和消费者模型
  • 消息发送确认机制
  • 消费确认机制
  • 消息的重新投递

二、实现思路

  • 1.准备一台 Linux 服务器,并安装 RabbitMQ
  • 2.开放 QQ 邮箱或者其它邮箱授权码,用于发送邮件
  • 3.创建邮件发送项目并编写代码
  • 4.发送邮件测试
  • 5.消息发送失败处理

三、环境准备

获取邮箱授权码的目的,主要是为了通过代码进行发送邮件,例如 QQ 邮箱授权码获取方式,如下图:

点击【开启】按钮,然后发送短信,即可获取授权码,该授权码就是配置文件spring.mail.password需要的密码!

四、项目介绍

  • springboot版本:2.1.5.RELEASE
  • RabbitMQ版本:3.6.5
  • SendMailUtil:发送邮件工具类
  • ProduceServiceImpl:生产者,发送消息
  • ConsumerMailService:消费者,消费消息,发送邮件

五、代码实现

5.1、创建项目

在 IDEA 下创建一个名称为smail的 Springboot 项目,pom文件中加入amqpmail

<dependencies><!--spring boot核心--><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter</artifactId></dependency><!--spring boot 测试--><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-test</artifactId><scope>test</scope></dependency><!--springmvc web--><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-web</artifactId></dependency><!--开发环境调试--><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-devtools</artifactId><optional>true</optional></dependency><!--mail 支持--><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-mail</artifactId></dependency><!--amqp 支持--><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-amqp</artifactId></dependency><!-- commons-lang3 --><dependency><groupId>org.apache.commons</groupId><artifactId>commons-lang3</artifactId><version>3.4</version></dependency><!--lombok--><dependency><groupId>org.projectlombok</groupId><artifactId>lombok</artifactId><version>1.16.10</version></dependency>
</dependencies>
5.2、配置rabbitMQ、mail

application.properties文件中,配置amqpmail

#rabbitmq
spring.rabbitmq.host=192.168.0.103
spring.rabbitmq.port=5672
spring.rabbitmq.username=guest
spring.rabbitmq.password=guest
# 开启confirms回调 P -> Exchange
spring.rabbitmq.publisher-confirms=true
# 开启returnedMessage回调 Exchange -> Queue
spring.rabbitmq.publisher-returns=true
# 设置手动确认(ack) Queue -> C
spring.rabbitmq.listener.simple.acknowledge-mode=manual
spring.rabbitmq.listener.simple.prefetch=100# mail
spring.mail.default-encoding=UTF-8
spring.mail.host=smtp.qq.com
spring.mail.username=xxxx@qq.com
spring.mail.password=获取的邮箱授权码
spring.mail.from=xxxx@qq.com
spring.mail.properties.mail.smtp.auth=true
spring.mail.properties.mail.smtp.starttls.enable=true
spring.mail.properties.mail.smtp.starttls.required=true

其中,spring.mail.password第四步中获取的授权码,同时usernamefrom要一致!

5.3、RabbitConfig配置类
@Configuration
@Slf4j
public class RabbitConfig {// 发送邮件public static final String MAIL_QUEUE_NAME = "mail.queue";public static final String MAIL_EXCHANGE_NAME = "mail.exchange";public static final String MAIL_ROUTING_KEY_NAME = "mail.routing.key";@Autowiredprivate CachingConnectionFactory connectionFactory;@Beanpublic RabbitTemplate rabbitTemplate() {RabbitTemplate rabbitTemplate = new RabbitTemplate(connectionFactory);rabbitTemplate.setMessageConverter(converter());// 消息是否成功发送到ExchangerabbitTemplate.setConfirmCallback((correlationData, ack, cause) -> {if (ack) {log.info("消息成功发送到Exchange");} else {log.info("消息发送到Exchange失败, {}, cause: {}", correlationData, cause);}});// 触发setReturnCallback回调必须设置mandatory=true, 否则Exchange没有找到Queue就会丢弃掉消息, 而不会触发回调rabbitTemplate.setMandatory(true);// 消息是否从Exchange路由到Queue, 注意: 这是一个失败回调, 只有消息从Exchange路由到Queue失败才会回调这个方法rabbitTemplate.setReturnCallback((message, replyCode, replyText, exchange, routingKey) -> {log.info("消息从Exchange路由到Queue失败: exchange: {}, route: {}, replyCode: {}, replyText: {}, message: {}", exchange, routingKey, replyCode, replyText, message);});return rabbitTemplate;}@Beanpublic Jackson2JsonMessageConverter converter() {return new Jackson2JsonMessageConverter();}@Beanpublic Queue mailQueue() {return new Queue(MAIL_QUEUE_NAME, true);}@Beanpublic DirectExchange mailExchange() {return new DirectExchange(MAIL_EXCHANGE_NAME, true, false);}@Beanpublic Binding mailBinding() {return BindingBuilder.bind(mailQueue()).to(mailExchange()).with(MAIL_ROUTING_KEY_NAME);}
}
5.4、Mail 邮件实体类
@Getter
@Setter
@NoArgsConstructor
@AllArgsConstructor
public class Mail {// 目标邮箱private String to;// 标题private String title;// 正文private String content;// 消息IDprivate String msgId;
}
5.5、SendMailUtil邮件发送类
@Component
@Slf4j
public class SendMailUtil {@Value("${spring.mail.from}")private String from;@Autowiredprivate JavaMailSender mailSender;/*** 发送简单邮件** @param mail*/public boolean send(Mail mail) {String to = mail.getTo();// 目标邮箱String title = mail.getTitle();// 邮件标题String content = mail.getContent();// 邮件正文SimpleMailMessage message = new SimpleMailMessage();message.setFrom(from);message.setTo(to);message.setSubject(title);message.setText(content);try {mailSender.send(message);log.info("邮件发送成功");return true;} catch (MailException e) {log.error("邮件发送失败, to: {}, title: {}", to, title, e);return false;}}
}
5.6、ProduceServiceImpl 生产者类
@Service
public class ProduceServiceImpl implements ProduceService {@Autowiredprivate RabbitTemplate rabbitTemplate;@Overridepublic boolean send(Mail mail) {//创建uuidString msgId = UUID.randomUUID().toString().replaceAll("-", "");mail.setMsgId(msgId);//发送消息到rabbitMQCorrelationData correlationData = new CorrelationData(msgId);rabbitTemplate.convertAndSend(RabbitConfig.MAIL_EXCHANGE_NAME, RabbitConfig.MAIL_ROUTING_KEY_NAME, MessageHelper.objToMsg(mail), correlationData);return true;}
}
5.7、ConsumerMailService 消费者类
@Component
@Slf4j
public class ConsumerMailService {@Autowiredprivate SendMailUtil sendMailUtil;@RabbitListener(queues = RabbitConfig.MAIL_QUEUE_NAME)public void consume(Message message, Channel channel) throws IOException {//将消息转化为对象String str = new String(message.getBody());Mail mail = JsonUtil.strToObj(str, Mail.class);log.info("收到消息: {}", mail.toString());MessageProperties properties = message.getMessageProperties();long tag = properties.getDeliveryTag();boolean success = sendMailUtil.send(mail);if (success) {channel.basicAck(tag, false);// 消费确认} else {channel.basicNack(tag, false, true);}}
}
5.8、TestController 控制层类
@RestController
@RequestMapping("/test")
@Slf4j
public class TestController {@Autowiredprivate ProduceService testService;@PostMapping("send")public boolean sendMail(Mail mail) {return testService.send(mail);}
}

六、测试服务

启动 SpringBoot 服务之后,用 postman 模拟请求接口。

查看控制台信息。

查询接受者邮件信息。

邮件发送成功!

七、小结

本文主要是通过发送邮件这个业务案例,来讲解 Springboot 与 rabbitMQ 技术的整合和使用!

使用了 rabbitMQ 的手动确认模式,当开启了之后,必须手动调用 ack 或者 nack 方法,否则消息会一直存储在 rabbitMQ 服务器中。

项目源代码地址:spring-boot-example-smail

写到最后

不会有人刷到这里还想白嫖吧?点赞对我真的非常重要!在线求赞。加个关注我会非常感激!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://xiahunao.cn/news/3225593.html

如若内容造成侵权/违法违规/事实不符,请联系瞎胡闹网进行投诉反馈,一经查实,立即删除!

相关文章

为什么要安装HTTPS证书?

安装HTTPS证书对于确保网站数据的安全性、增强用户信任度、提升品牌形象和优化搜索引擎排名至关重要。在互联网时代&#xff0c;信息传输的安全性和隐私保护已成为公众和企业最为关注的问题之一。HTTPS证书的引入&#xff0c;正是为了解决这些问题&#xff0c;为网站和用户提供…

git为文件添加可执行权限

查看文件权限 git ls-files --stage .\SecretFinder.py100644 表示文件的所有者有读取和写入权限 添加可执行权限 git update-index --chmod x .\SecretFinder.py再次查看文件权限 git ls-files --stage .\SecretFinder.py100755 表示文件的所有者有读取、写入和执行权限

第十六章 ValidationPipe验证post请求参数

在此之前我们用到的请求都是get请求&#xff0c;接下来我们使用post 请求 并接收参数&#xff0c;通过 Body 装饰器来取注意&#xff1a;post请求带参数 我们通过游览器路径是直接请求不了的 需要使用postman 来发 post 请求postman 下载网站 https://www.postman.com/download…

3D问界—ZBrush最新版本中的旋转Local按钮哪儿去哪儿?

问题提出&#xff1a;ZBrush最新版本中的Local按钮哪儿去哪儿&#xff1f; 目前我使用的是Zbrush2024.0.1版本&#xff0c;但是当我想要取消锁定局部旋转的时候死活找不到local功能。 1. 注意&#xff1a;Local功能挪到这里了 2. 关于local功能的使用&#xff08;Local Trans…

Science|N型半导体水凝胶(柔性半导体器件/柔性健康监测/导电水凝胶/柔性电子)

2024年5月2日,北京大学雷霆(Ting Lei)课题组在《Science》上发布了一篇题为“N-type semiconducting hydrogel”的论文。论文内容如下: 一、 摘要 水凝胶是一类具有可调机械性能、多样生化功能和良好离子导电性的生物界面材料,但由于缺乏半导体特性,使得水凝胶在电子学中…

基于ARM Cortex-M3单片机研发的国产指纹芯片 - P1032BF1

智能指纹锁的核心部件&#xff1a;主板、离合器、指纹采集器、密码技术、微处理器&#xff08;CPU&#xff09;、智能应急钥匙。作为指纹锁来说&#xff0c;重要的应该是指纹芯片。指纹锁是通过电子部件及机械部件的精密组合而生产出的安全产品。指纹锁的本质无非是安全、便捷、…

职升网:考取专科学历的途径包括以下这些!

高考统招&#xff1a; 每年6月举行的全国统一高考&#xff0c;是获得专科学历的传统途径。 考生需参加由教育部组织的统一考试&#xff0c;按照分数由高到低依次录取。 适合高中毕业生或具有同等学历的学生。 自学考试&#xff1a; 又称自考&#xff0c;是一种没有入学考试…

首个开源、原生多模态生成大模型:变色龙Anole一键生成 「煎鸡蛋」图文菜谱

首个开源、原生多模态生成大模型&#xff1a;变色龙Anole一键生成 「煎鸡蛋」图文菜谱 始智AI wisemodel 2024年07月09日 17:30 北京 首个开源、原生多模态生成大模型&#xff1a;变色龙Anole一键生成 「煎鸡蛋」图文菜谱 始智AI wisemodel.cn社区将打造成huggingface之外最…

【uni-app+Vue3】 API请求封装:让接口调用更便捷

前言&#xff1a;uni-app是一款基于Vue.js框架的跨平台开发工具&#xff0c;可以将代码编译成H5、小程序、App等不同平台的应用。在进行uni-app开发时&#xff0c;网络请求是必不可少的环节。为了方便开发&#xff0c;我们可以封装一些网络请求方法&#xff0c;以便在多个页面中…

Flink ui 本地flink ui 报错 {“errors“:[“Not found: /“]}

在学习flink 的过程中&#xff0c;伊始的flink 版本是1.17.2 报题目的错误 &#xff0c;百思不得其解&#xff0c;尝试更替了1.19.1 然后就成功了 &#xff0c;期间未做任何的修改 。 ui 默认地址 &#xff1a; http://localhost:8081 pom 文件 如下 <?xml version&qu…

华为HCIP Datacom H12-821 卷33

1.判断题 缺省情况下&#xff0c;华为AR路由器的VRRP运行在抢占模式下 A、对 B、错 正确答案&#xff1a; A 解析&#xff1a; 无 2.判断题 一个Route-Policy下可以有多个节点&#xff0c;不同的节点号用节点号标识&#xff0c;不同节点之间的关系是"或"的关…

【Python大语言模型系列】Windows环境下部署Chatglm2-6B-int4大语言模型(完整教程)

这是我的第319篇原创文章。 一、引言 电脑配置 &#xff1a; python版本要求&#xff1a;3.8torch版本&#xff1a;2.0.1cuda&#xff1a;11.7windows系统&#xff1a;Windows 10 显卡&#xff1a;6G以上GPU 二、实现过程 2.1 下载chatglm2-6b的项目源码 上chatglm2-6B的官…

程序员学CFA——经济学(六)

经济学&#xff08;六&#xff09; 国际贸易与资本流动国际贸易相关术语开放/封闭经济自由贸易/贸易保护贸易比价国内生产总值与国民生产总值 国际贸易的利弊分析益处弊端 从贸易中获益&#xff1a;比较优势比较优势和绝对优势比较优势的来源 贸易限制和贸易保护施行贸易保护政…

Python基础小知识问答系列-遍历嵌套列表

1. 问题&#xff1a; 如何只使用一次for循环&#xff0c;遍历2层嵌套的列表&#xff1f; 2. 解决方法&#xff1a; 使用yield from语句的递归生成器&#xff0c;解决多层嵌套列表的遍历问题。 示例&#xff1a; from collections.abc import Iterablelist_b ["hong kong…

pcie 基础

1. 传输速率与带宽的关系 当我们谈论PCIe总线标准的传输速率时&#xff0c;我们使用GT/s&#xff08;Giga Transfers per second&#xff0c;千兆传输/秒&#xff09;来衡量&#xff0c;而不是Gbps&#xff08;Giga Bits Per Second&#xff0c;千兆位/秒&#xff09;。这是因为…

linux服务器信息获取(宝塔)工具

功能介绍 SSH连接到远程服务器&#xff1a; 用户可以输入目标服务器的IP地址、用户名、密码以及SSH端口&#xff08;默认22&#xff09;。 工具会尝试连接到远程服务器&#xff0c;并在连接失败时显示错误信息。 运行命令并返回输出&#xff1a; 工具可以在远程服务器上运…

万界星空科技日化行业MES解决方案

日化行业MES&#xff08;制造执行系统&#xff09;解决方案是针对日化行业特点而设计的一套全面的生产管理系统&#xff0c;旨在提高生产效率、优化资源配置、加强质量控制&#xff0c;并推动企业的数字化转型。以下是对日化行业MES解决方案的详细阐述&#xff1a; 一、MES解决…

CodeNavi 中代码表达式的节点和节点属性

本文分享自华为云社区《CodeNavi 中代码表达式的节点和节点属性》。作者&#xff1a;Uncle_Tom 1. 前期回顾 《寻找适合编写静态分析规则的语言》 根据代码检查中的一些痛点&#xff0c;提出了希望寻找一种适合编写静态分析规则的语言。 可以满足用户对代码检查不断增加的各种需…

【源码+文档+调试讲解】沙县小吃点餐系统

摘 要 随着社会的发展&#xff0c;社会的各行各业都在利用信息化时代的优势。计算机的优势和普及使得各种信息系统的开发成为必需。 沙县小吃点餐系统&#xff0c;主要的模块包括实现管理员&#xff1b;个人中心、用户管理、小吃信息管理、门店信息管理、预约信息管理、系统管…

Python数据分析-2023-2024 NBA 球员统计数据分析

一、研究背景 近年来&#xff0c;NBA&#xff08;美国国家篮球协会&#xff09;已经成为全球最受关注的篮球联赛之一。随着比赛的日益激烈和球员表现的多样化&#xff0c;分析NBA球员的表现数据变得越来越重要。现代数据分析技术的进步使得我们能够更加详细地研究和理解球员的…