SpringBoot整合Rabbitmq与redis发送邮件

准备工作:

提示:使用任何邮箱都一样,这里使用qq邮箱进行测试,获取授权码的过程也大同小异。

  1. 登录邮箱找到设置
    SpringBoot整合Rabbitmq与redis发送邮件_第1张图片
  2. 开启POP3/SMTP服务
    SpringBoot整合Rabbitmq与redis发送邮件_第2张图片
  3. 获取邮箱code码
    SpringBoot整合Rabbitmq与redis发送邮件_第3张图片

项目准备:

1.引入rabbitmq,redis,mail依赖

<dependency>
     <groupId>org.springframework.boot</groupId>
     <artifactId>spring-boot-starteramqp</artifactId>
 </dependency>
 <dependency>
     <groupId>org.springframework.boot</groupId>
     <artifactId>spring-boot-starter-data-redis</artifactId>
 </dependency>
 <dependency>
     <groupId>org.springframework.boot</groupId>
     <artifactId>spring-boot-startermail</artifactId>
 </dependency>

2.配置文件(yml与properties均可)
注意:使用到的各种参数按照自己的实际情况填写即可,一定要开启rabbitmq的发布确认模式CORRELATED值是发布消息成功到交换器后会触发回调方法。

SpringBoot整合Rabbitmq与redis发送邮件_第4张图片SpringBoot整合Rabbitmq与redis发送邮件_第5张图片注意:我按照跟人的开发习惯把发送邮件的模块抽出来了,方便以后使用,下面是mail的配置。不同邮箱的端口是不一样的 感兴趣的可以去了解一下
SpringBoot整合Rabbitmq与redis发送邮件_第6张图片3.代码展示
3.1:配置rabbitmq,为了保证邮件百分百被发送开启手动确认

@Configuration
public class RabbitMqConfig {
    private static final Logger log = LoggerFactory.getLogger(AFRabbitMqConfig.class);
    @Autowired
    private MailSendLogService mailSendLogService;
    @Autowired
    private CachingConnectionFactory cachingConnectionFactory;
    @Bean
    Queue mainQuene(){
        return new Queue(MailConstants.MAIL_QUEUE_NAME,true);
    }
   
    @Bean
    DirectExchange mailExchange(){
        return new DirectExchange(MailConstants.MAIL_EXCHANGE_NAME, true, false);
    }

    @Bean
    Binding mailBind(){
        return BindingBuilder.bind(mainQuene()).to(mailExchange()).with(MailConstants.MAIL_ROUTING_KEY_NAME);
    }
  
    @Bean
    RabbitTemplate rabbitTemplate() {
        RabbitTemplate rabbitTemplate = new RabbitTemplate(cachingConnectionFactory);
        //邮件发送失败监控
        rabbitTemplate.setConfirmCallback((data, ack, cause) -> {
            String msgId = data.getId();
            if (ack) {
                log.info("邮件发送成功" + msgId);
           mailSendLogService.updateMailSendLogStatus(msgId, 1);
            } else {
                log.info("邮件发送失败" + msgId);
            }
        });
        //rabbit自身错误监控
        rabbitTemplate.setReturnCallback((msg, repCode, repText, exchange, routingkey) -> {
            log.info("Rabblimq自身原因邮件发送失败" + msg);
        });
        return rabbitTemplate;
    }
}

3.2:创建邮件发送日志表,记录发送情况,并创建实体类

CREATE TABLE `mail_send_log`  (
  `msgId` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL,
  `empId` int(11) DEFAULT NULL,
  `status` int(11) DEFAULT 0 COMMENT '0发送中,1发送成功,2发送失败',
  `routeKey` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL,
  `exchange` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL,
  `count` int(11) DEFAULT NULL COMMENT '重试次数',
  `tryTime` date DEFAULT NULL COMMENT '第一次重试时间',
  `createTime` date DEFAULT NULL,
  `updateTime` date DEFAULT NULL
)
public class MailSendLog {
    private String msgId;
    private Integer empId;
    //0 消息投递中   1 投递成功   2投递失败
    private Integer status;
    private String routeKey;
    private String exchange;
    private Integer count;
    private Date tryTime;
    private Date createTime;
    private Date updateTime;

    public String getMsgId() {
        return msgId;
    }

    public void setMsgId(String msgId) {
        this.msgId = msgId;
    }

    public Integer getEmpId() {
        return empId;
    }

    public void setEmpId(Integer empId) {
        this.empId = empId;
    }

    public Integer getStatus() {
        return status;
    }

    public void setStatus(Integer status) {
        this.status = status;
    }

    public String getRouteKey() {
        return routeKey;
    }

    public void setRouteKey(String routeKey) {
        this.routeKey = routeKey;
    }

    public String getExchange() {
        return exchange;
    }

    public void setExchange(String exchange) {
        this.exchange = exchange;
    }

    public Integer getCount() {
        return count;
    }

    public void setCount(Integer count) {
        this.count = count;
    }

    public Date getTryTime() {
        return tryTime;
    }

    public void setTryTime(Date tryTime) {
        this.tryTime = tryTime;
    }

    public Date getCreateTime() {
        return createTime;
    }

    public void setCreateTime(Date createTime) {
        this.createTime = createTime;
    }

    public Date getUpdateTime() {
        return updateTime;
    }

    public void setUpdateTime(Date updateTime) {
        this.updateTime = updateTime;
    }
}

3.3:生产者发送邮件

//开始发送邮件
            MailSendLog mailSendLog = new MailSendLog();
            mailSendLog.setEmpId(emp.getId());
            String msgId = UUID.randomUUID().toString().replaceAll("-", "");
            mailSendLog.setMsgId(msgId);
            mailSendLog.setCreateTime(new Date());
            mailSendLog.setExchange(MailConstants.MAIL_EXCHANGE_NAME);
            mailSendLog.setRouteKey(MailConstants.MAIL_ROUTING_KEY_NAME);
            //默认重试时间为一分钟以后
            mailSendLog.setTryTime(new Date(System.currentTimeMillis() + 1000 * 60));
            mailSendLogService.insert(mailSendLog);
            //发给rabbitmq  CorrelationData为唯一标识
            rabbitTemplate.convertAndSend(MailConstants.MAIL_EXCHANGE_NAME, MailConstants.MAIL_ROUTING_KEY_NAME
                    ,  new CorrelationData(msgId));

3.4:消费者进行邮件处理,发送还使用了thymleaf,比较简单,就是简单的内容填充,这里不再过多赘述

   @Autowired
    JavaMailSender javaMailSender;
    @Autowired
    MailProperties mailProperties;
    @Autowired
    TemplateEngine templateEngine;
    @Autowired
    StringRedisTemplate redisTemplate;

    @RabbitListener(queues = MailConstants.MAIL_QUEUE_NAME)
    public void handler(Message message, Channel channel) throws IOException {
        MessageHeaders headers = message.getHeaders();
        Long tag = (Long) headers.get(AmqpHeaders.DELIVERY_TAG);
        String msgId = (String) headers.get("spring_returned_message_correlation");
        //解决幂等性
        if (redisTemplate.opsForHash().entries("mail_log").containsKey(msgId)) {
            //redis 中包含该 key,说明该消息已经被消费过
            logger.info(msgId + ":消息已经被消费");
            channel.basicAck(tag, false);//确认消息已消费
            return;
        }
        //收到消息,发送邮件
        MimeMessage msg = javaMailSender.createMimeMessage();
        MimeMessageHelper helper = new MimeMessageHelper(msg);
        try {
            helper.setTo("收件人");
            helper.setFrom("发件人");
            helper.setSubject("测试主题");
            helper.setSentDate(new Date());
            Context context = new Context();
            context.setVariable("title", "标题");
            context.setVariable("content", "内容");
            //填充模板内容
            String mail = templateEngine.process("mail", context);
            helper.setText(mail, true);
            javaMailSender.send(msg);
            redisTemplate.opsForHash().put("mail_log", msgId, "mail_log");
            //已消费
            channel.basicAck(tag, false);
            logger.info(msgId + ":邮件发送成功");
        } catch (MessagingException e) {
            //回到队列里面  等待下一次消费
            channel.basicNack(tag, false, true);
            e.printStackTrace();
            logger.error("邮件发送失败:" + e.getMessage());
        }
    }

3.5:创建定时对失败发送进行轮循,根据业务需要书写cron表达式,规定轮循三次以后还失败的不再轮询。

 @Autowired
    MailSendLogService mailSendLogService;
    @Autowired
    RabbitTemplate rabbitTemplate;


    @Scheduled(cron = "*/5 * * * * ?")
    public void mailResendTask() {
        List<MailSendLog> logs = mailSendLogService.getMailSendLogsByStatus();
        if (logs == null || logs.size() == 0) {
            return;
        }
        logs.forEach(mailSendLog -> {
            if (mailSendLog.getCount() >= 3) {
                mailSendLogService.updateMailSendLogStatus(mailSendLog.getMsgId(), 2);//直接设置该条消息发送失败
            } else {
                mailSendLogService.updateCount(mailSendLog.getMsgId(), new Date());
           
                rabbitTemplate.convertAndSend(MailConstants.MAIL_EXCHANGE_NAME, MailConstants.MAIL_ROUTING_KEY_NAME, new CorrelationData(mailSendLog.getMsgId()));
            }
        });
    }
这只是简单的邮件发送展示,不是很全面,目的还是在于巩固对rabbitmq和redis的掌握。

你可能感兴趣的:(rabbitmq,spring,boot,redis)