经验首页 前端设计 程序设计 Java相关 移动开发 数据库/运维 软件/图像 大数据/云计算 其他经验
当前位置:技术经验 » 数据库/运维 » RabbitMQ » 查看文章
利用SpringBoot+rabbitmq 实现邮件异步发送,保证100%投递成功
来源:cnblogs  作者:潘志的研发笔记  时间:2024/7/11 14:06:41  对本文有异议

在之前的文章中,我们详细介绍了 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

  1. <dependencies>
  2. <!--spring boot核心-->
  3. <dependency>
  4. <groupId>org.springframework.boot</groupId>
  5. <artifactId>spring-boot-starter</artifactId>
  6. </dependency>
  7. <!--spring boot 测试-->
  8. <dependency>
  9. <groupId>org.springframework.boot</groupId>
  10. <artifactId>spring-boot-starter-test</artifactId>
  11. <scope>test</scope>
  12. </dependency>
  13. <!--springmvc web-->
  14. <dependency>
  15. <groupId>org.springframework.boot</groupId>
  16. <artifactId>spring-boot-starter-web</artifactId>
  17. </dependency>
  18. <!--开发环境调试-->
  19. <dependency>
  20. <groupId>org.springframework.boot</groupId>
  21. <artifactId>spring-boot-devtools</artifactId>
  22. <optional>true</optional>
  23. </dependency>
  24. <!--mail 支持-->
  25. <dependency>
  26. <groupId>org.springframework.boot</groupId>
  27. <artifactId>spring-boot-starter-mail</artifactId>
  28. </dependency>
  29. <!--amqp 支持-->
  30. <dependency>
  31. <groupId>org.springframework.boot</groupId>
  32. <artifactId>spring-boot-starter-amqp</artifactId>
  33. </dependency>
  34. <!-- commons-lang3 -->
  35. <dependency>
  36. <groupId>org.apache.commons</groupId>
  37. <artifactId>commons-lang3</artifactId>
  38. <version>3.4</version>
  39. </dependency>
  40. <!--lombok-->
  41. <dependency>
  42. <groupId>org.projectlombok</groupId>
  43. <artifactId>lombok</artifactId>
  44. <version>1.16.10</version>
  45. </dependency>
  46. </dependencies>

5.2、配置rabbitMQ、mail

application.properties文件中,配置amqpmail

  1. #rabbitmq
  2. spring.rabbitmq.host=192.168.0.103
  3. spring.rabbitmq.port=5672
  4. spring.rabbitmq.username=guest
  5. spring.rabbitmq.password=guest
  6. # 开启confirms回调 P -> Exchange
  7. spring.rabbitmq.publisher-confirms=true
  8. # 开启returnedMessage回调 Exchange -> Queue
  9. spring.rabbitmq.publisher-returns=true
  10. # 设置手动确认(ack) Queue -> C
  11. spring.rabbitmq.listener.simple.acknowledge-mode=manual
  12. spring.rabbitmq.listener.simple.prefetch=100
  13. # mail
  14. spring.mail.default-encoding=UTF-8
  15. spring.mail.host=smtp.qq.com
  16. spring.mail.username=xxxx@qq.com
  17. spring.mail.password=获取的邮箱授权码
  18. spring.mail.from=xxxx@qq.com
  19. spring.mail.properties.mail.smtp.auth=true
  20. spring.mail.properties.mail.smtp.starttls.enable=true
  21. spring.mail.properties.mail.smtp.starttls.required=true

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

5.3、RabbitConfig配置类

  1. @Configuration
  2. @Slf4j
  3. public class RabbitConfig {
  4. // 发送邮件
  5. public static final String MAIL_QUEUE_NAME = "mail.queue";
  6. public static final String MAIL_EXCHANGE_NAME = "mail.exchange";
  7. public static final String MAIL_ROUTING_KEY_NAME = "mail.routing.key";
  8. @Autowired
  9. private CachingConnectionFactory connectionFactory;
  10. @Bean
  11. public RabbitTemplate rabbitTemplate() {
  12. RabbitTemplate rabbitTemplate = new RabbitTemplate(connectionFactory);
  13. rabbitTemplate.setMessageConverter(converter());
  14. // 消息是否成功发送到Exchange
  15. rabbitTemplate.setConfirmCallback((correlationData, ack, cause) -> {
  16. if (ack) {
  17. log.info("消息成功发送到Exchange");
  18. } else {
  19. log.info("消息发送到Exchange失败, {}, cause: {}", correlationData, cause);
  20. }
  21. });
  22. // 触发setReturnCallback回调必须设置mandatory=true, 否则Exchange没有找到Queue就会丢弃掉消息, 而不会触发回调
  23. rabbitTemplate.setMandatory(true);
  24. // 消息是否从Exchange路由到Queue, 注意: 这是一个失败回调, 只有消息从Exchange路由到Queue失败才会回调这个方法
  25. rabbitTemplate.setReturnCallback((message, replyCode, replyText, exchange, routingKey) -> {
  26. log.info("消息从Exchange路由到Queue失败: exchange: {}, route: {}, replyCode: {}, replyText: {}, message: {}", exchange, routingKey, replyCode, replyText, message);
  27. });
  28. return rabbitTemplate;
  29. }
  30. @Bean
  31. public Jackson2JsonMessageConverter converter() {
  32. return new Jackson2JsonMessageConverter();
  33. }
  34. @Bean
  35. public Queue mailQueue() {
  36. return new Queue(MAIL_QUEUE_NAME, true);
  37. }
  38. @Bean
  39. public DirectExchange mailExchange() {
  40. return new DirectExchange(MAIL_EXCHANGE_NAME, true, false);
  41. }
  42. @Bean
  43. public Binding mailBinding() {
  44. return BindingBuilder.bind(mailQueue()).to(mailExchange()).with(MAIL_ROUTING_KEY_NAME);
  45. }
  46. }

5.4、Mail 邮件实体类

  1. @Getter
  2. @Setter
  3. @NoArgsConstructor
  4. @AllArgsConstructor
  5. public class Mail {
  6. // 目标邮箱
  7. private String to;
  8. // 标题
  9. private String title;
  10. // 正文
  11. private String content;
  12. // 消息ID
  13. private String msgId;
  14. }

5.5、SendMailUtil邮件发送类

  1. @Component
  2. @Slf4j
  3. public class SendMailUtil {
  4. @Value("${spring.mail.from}")
  5. private String from;
  6. @Autowired
  7. private JavaMailSender mailSender;
  8. /**
  9. * 发送简单邮件
  10. *
  11. * @param mail
  12. */
  13. public boolean send(Mail mail) {
  14. String to = mail.getTo();// 目标邮箱
  15. String title = mail.getTitle();// 邮件标题
  16. String content = mail.getContent();// 邮件正文
  17. SimpleMailMessage message = new SimpleMailMessage();
  18. message.setFrom(from);
  19. message.setTo(to);
  20. message.setSubject(title);
  21. message.setText(content);
  22. try {
  23. mailSender.send(message);
  24. log.info("邮件发送成功");
  25. return true;
  26. } catch (MailException e) {
  27. log.error("邮件发送失败, to: {}, title: {}", to, title, e);
  28. return false;
  29. }
  30. }
  31. }

5.6、ProduceServiceImpl 生产者类

  1. @Service
  2. public class ProduceServiceImpl implements ProduceService {
  3. @Autowired
  4. private RabbitTemplate rabbitTemplate;
  5. @Override
  6. public boolean send(Mail mail) {
  7. //创建uuid
  8. String msgId = UUID.randomUUID().toString().replaceAll("-", "");
  9. mail.setMsgId(msgId);
  10. //发送消息到rabbitMQ
  11. CorrelationData correlationData = new CorrelationData(msgId);
  12. rabbitTemplate.convertAndSend(RabbitConfig.MAIL_EXCHANGE_NAME, RabbitConfig.MAIL_ROUTING_KEY_NAME, MessageHelper.objToMsg(mail), correlationData);
  13. return true;
  14. }
  15. }

5.7、ConsumerMailService 消费者类

  1. @Component
  2. @Slf4j
  3. public class ConsumerMailService {
  4. @Autowired
  5. private SendMailUtil sendMailUtil;
  6. @RabbitListener(queues = RabbitConfig.MAIL_QUEUE_NAME)
  7. public void consume(Message message, Channel channel) throws IOException {
  8. //将消息转化为对象
  9. String str = new String(message.getBody());
  10. Mail mail = JsonUtil.strToObj(str, Mail.class);
  11. log.info("收到消息: {}", mail.toString());
  12. MessageProperties properties = message.getMessageProperties();
  13. long tag = properties.getDeliveryTag();
  14. boolean success = sendMailUtil.send(mail);
  15. if (success) {
  16. channel.basicAck(tag, false);// 消费确认
  17. } else {
  18. channel.basicNack(tag, false, true);
  19. }
  20. }
  21. }

5.8、TestController 控制层类

  1. @RestController
  2. @RequestMapping("/test")
  3. @Slf4j
  4. public class TestController {
  5. @Autowired
  6. private ProduceService testService;
  7. @PostMapping("send")
  8. public boolean sendMail(Mail mail) {
  9. return testService.send(mail);
  10. }
  11. }

六、测试服务

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

查看控制台信息。

查询接受者邮件信息。

邮件发送成功!

七、小结

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

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

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

原文链接:https://www.cnblogs.com/dxflqm/p/18293317

 友情链接:直通硅谷  点职佳  北美留学生论坛

本站QQ群:前端 618073944 | Java 606181507 | Python 626812652 | C/C++ 612253063 | 微信 634508462 | 苹果 692586424 | C#/.net 182808419 | PHP 305140648 | 运维 608723728

W3xue 的所有内容仅供测试,对任何法律问题及风险不承担任何责任。通过使用本站内容随之而来的风险与本站无关。
关于我们  |  意见建议  |  捐助我们  |  报错有奖  |  广告合作、友情链接(目前9元/月)请联系QQ:27243702 沸活量
皖ICP备17017327号-2 皖公网安备34020702000426号