0


利用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

文件中加入

amqp

mail

<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

文件中,配置

amqp

mail

#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
[email protected]
spring.mail.password=获取的邮箱授权码
[email protected]
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

第四步中获取的授权码,同时

username

from

要一致!

5.3、RabbitConfig配置类
@Configuration@Slf4jpublicclassRabbitConfig{// 发送邮件publicstaticfinalStringMAIL_QUEUE_NAME="mail.queue";publicstaticfinalStringMAIL_EXCHANGE_NAME="mail.exchange";publicstaticfinalStringMAIL_ROUTING_KEY_NAME="mail.routing.key";@AutowiredprivateCachingConnectionFactory connectionFactory;@BeanpublicRabbitTemplaterabbitTemplate(){RabbitTemplate rabbitTemplate =newRabbitTemplate(connectionFactory);
        rabbitTemplate.setMessageConverter(converter());// 消息是否成功发送到Exchange
        rabbitTemplate.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;}@BeanpublicJackson2JsonMessageConverterconverter(){returnnewJackson2JsonMessageConverter();}@BeanpublicQueuemailQueue(){returnnewQueue(MAIL_QUEUE_NAME,true);}@BeanpublicDirectExchangemailExchange(){returnnewDirectExchange(MAIL_EXCHANGE_NAME,true,false);}@BeanpublicBindingmailBinding(){returnBindingBuilder.bind(mailQueue()).to(mailExchange()).with(MAIL_ROUTING_KEY_NAME);}}
5.4、Mail 邮件实体类
@Getter@Setter@NoArgsConstructor@AllArgsConstructorpublicclassMail{// 目标邮箱privateStringto;// 标题privateString title;// 正文privateString content;// 消息IDprivateString msgId;}
5.5、SendMailUtil邮件发送类
@Component@Slf4jpublicclassSendMailUtil{@Value("${spring.mail.from}")privateString from;@AutowiredprivateJavaMailSender mailSender;/**
     * 发送简单邮件
     *
     * @param mail
     */publicbooleansend(Mail mail){Stringto= mail.getTo();// 目标邮箱String title = mail.getTitle();// 邮件标题String content = mail.getContent();// 邮件正文SimpleMailMessage message =newSimpleMailMessage();
        message.setFrom(from);
        message.setTo(to);
        message.setSubject(title);
        message.setText(content);try{
            mailSender.send(message);
            log.info("邮件发送成功");returntrue;}catch(MailException e){
            log.error("邮件发送失败, to: {}, title: {}",to, title, e);returnfalse;}}}
5.6、ProduceServiceImpl 生产者类
@ServicepublicclassProduceServiceImplimplementsProduceService{@AutowiredprivateRabbitTemplate rabbitTemplate;@Overridepublicbooleansend(Mail mail){//创建uuidString msgId =UUID.randomUUID().toString().replaceAll("-","");
        mail.setMsgId(msgId);//发送消息到rabbitMQCorrelationData correlationData =newCorrelationData(msgId);
        rabbitTemplate.convertAndSend(RabbitConfig.MAIL_EXCHANGE_NAME,RabbitConfig.MAIL_ROUTING_KEY_NAME,MessageHelper.objToMsg(mail), correlationData);returntrue;}}
5.7、ConsumerMailService 消费者类
@Component@Slf4jpublicclassConsumerMailService{@AutowiredprivateSendMailUtil sendMailUtil;@RabbitListener(queues =RabbitConfig.MAIL_QUEUE_NAME)publicvoidconsume(Message message,Channel channel)throwsIOException{//将消息转化为对象String str =newString(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")@Slf4jpublicclassTestController{@AutowiredprivateProduceService testService;@PostMapping("send")publicbooleansendMail(Mail mail){return testService.send(mail);}}

六、测试服务

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

查看控制台信息。

查询接受者邮件信息。

邮件发送成功!

七、小结

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

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

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

写到最后

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


本文转载自: https://blog.csdn.net/dxflqm_pz/article/details/140315086
版权归原作者 志哥聊技术 所有, 如有侵权,请联系我们删除。

“利用SpringBoot+rabbitmq 实现邮件异步发送,保证100%投递成功”的评论:

还没有评论