• 【JavaMail】JavaMail整合RabbitMq发送邮件案例


    前言

    Linux安装RabbitMQ:https://www.cnblogs.com/jxd283465/p/11975094.html

    SpringBoot整合RabbitMQ:https://www.cnblogs.com/jxd283465/p/11975136.html

    流程

    代码

     数据库表

    CREATE TABLE `msg_log` (
      `msg_id` varchar(255) NOT NULL DEFAULT '' COMMENT '消息唯一标识',
      `msg` text COMMENT '消息体, json格式化',
      `exchange` varchar(255) NOT NULL DEFAULT '' COMMENT '交换机',
      `routing_key` varchar(255) NOT NULL DEFAULT '' COMMENT '路由键',
      `status` int(11) NOT NULL DEFAULT '0' COMMENT '状态: 0投递中 1投递成功 2投递失败 3已消费',
      `try_count` int(11) NOT NULL DEFAULT '0' COMMENT '重试次数',
      `next_try_time` datetime DEFAULT NULL COMMENT '下一次重试时间',
      `create_time` datetime DEFAULT NULL COMMENT '创建时间',
      `update_time` datetime DEFAULT NULL COMMENT '更新时间',
      PRIMARY KEY (`msg_id`),
      UNIQUE KEY `unq_msg_id` (`msg_id`) USING BTREE
    ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='消息投递日志';

    邮件发送类

    package cc.mrbird.febs.common.utils;
    /**
     * 邮件发送工具类。
     * 以下邮件中的配置参数,请在实际环境中,根据需要采取合适的配置方式。
     * 发送邮件依赖 com.sun.mail(1.6.1) 包、javax.mail(1.5.0-b01) 包。
     * 如果使用 Idea 运行,请将这两个包(可以直接到Maven目录下面去找)添加到项目的 Libraries 里面(快捷键:Ctrl + Alt + Shift + S)
     *
     * @author Zebe
     */
    public class SendEmailUtil {
    
        /**
         * 发件人别名(可以为空)
         */
        private final static String fromAliasName = "***";
    
        /**
         * 登录用户名
         */
        private String ACCOUNT;
    
        /**
         * 登录密码
         */
        private String PASSWORD;
    
        /**
         * 邮件服务器地址
         */
        //QQ企业邮箱:smtp.exmail.qq.com
        //网易企业邮箱:smtphz.qiye.163.com
        private String HOST;
    
        /**
         * 发信端口
         */
        //QQ企业邮箱:465
        //网易企业邮箱:994
        private String PORT;
    
        /**
         * 发信协议
         */
        private final static String PROTOCOL = "ssl";
    
        /**
         * 收件人
         */
        private String to;
    
        /**
         * 收件人名称
         */
        private String toName;
    
        /**
         * 主题
         */
        private String subject;
    
        /**
         * 内容
         */
        private String content;
    
        /**
         * 附件列表(可以为空)
         */
        private List<String> attachFileList;
    
        /**
         * 构造器
         *
         * @param attachFileList 附件列表
         */
        public SendEmailUtil(MailTemplate mailTemplate, List<String> attachFileList) {
            this.to = mailTemplate.getTo();
            this.toName = mailTemplate.getToName();
            this.subject = mailTemplate.getSubject();
            this.content = mailTemplate.getContent();
            this.attachFileList = attachFileList;
            this.ACCOUNT = mailTemplate.getAccount();
            this.PASSWORD = mailTemplate.getPassword();
            switch (mailTemplate.getSendType()) {
                case "qq":
                    this.HOST = "smtp.exmail.qq.com";
                    this.PORT = "465";
                    break;
                case "163":
                    this.HOST = "smtp.ym.163.com";
                    this.PORT = "994";
                    break;
            }
        }
    
        /**
         * 认证信息
         */
        static class MyAuthenticator extends Authenticator {
    
            /**
             * 用户名
             */
            String username = null;
    
            /**
             * 密码
             */
            String password = null;
    
            /**
             * 构造器
             *
             * @param username 用户名
             * @param password 密码
             */
            public MyAuthenticator(String username, String password) {
                this.username = username;
                this.password = password;
            }
    
            @Override
            protected PasswordAuthentication getPasswordAuthentication() {
                return new PasswordAuthentication(username, password);
            }
        }
    
        /**
         * 发送邮件
         */
        public boolean send() {
            // 设置邮件属性
            Properties prop = new Properties();
            prop.setProperty("mail.transport.protocol", PROTOCOL);
            prop.setProperty("mail.smtp.host", HOST);
            prop.setProperty("mail.smtp.port", PORT);
            prop.setProperty("mail.smtp.auth", "true");
            MailSSLSocketFactory sslSocketFactory = null;
            try {
                sslSocketFactory = new MailSSLSocketFactory();
                sslSocketFactory.setTrustAllHosts(true);
            } catch (GeneralSecurityException e1) {
                e1.printStackTrace();
            }
            if (sslSocketFactory == null) {
                System.err.println("开启 MailSSLSocketFactory 失败");
            } else {
                prop.put("mail.smtp.ssl.enable", "true");
                prop.put("mail.smtp.ssl.socketFactory", sslSocketFactory);
                // 创建邮件会话(注意,如果要在一个进程中切换多个邮箱账号发信,应该用 Session.getInstance)
                Session session = Session.getDefaultInstance(prop, new MyAuthenticator(ACCOUNT, PASSWORD));
                // 开启调试模式(生产环境中请不要开启此项)
                session.setDebug(true);
                try {
                    MimeMessage mimeMessage = new MimeMessage(session);
                    // 设置发件人别名(如果未设置别名就默认为发件人邮箱)
                    mimeMessage.setFrom(new InternetAddress(ACCOUNT, fromAliasName));
                    // 设置主题和收件人、发信时间等信息
                    mimeMessage.addRecipient(Message.RecipientType.TO, new InternetAddress(to, toName));
                    mimeMessage.setSubject(subject);
                    mimeMessage.setSentDate(new Date());
                    // 如果有附件信息,则添加附件
                    if (!attachFileList.isEmpty()) {
                        Multipart multipart = new MimeMultipart();
                        MimeBodyPart body = new MimeBodyPart();
                        body.setContent(content, "text/html; charset=UTF-8");
                        multipart.addBodyPart(body);
                        // 添加所有附件(添加时判断文件是否存在)
                        for (String filePath : attachFileList) {
                            if (Files.exists(Paths.get(filePath))) {
                                MimeBodyPart tempBodyPart = new MimeBodyPart();
                                tempBodyPart.attachFile(filePath);
                                multipart.addBodyPart(tempBodyPart);
                            }
                        }
                        mimeMessage.setContent(multipart);
                    } else {
                        Multipart multipart = new MimeMultipart();
                        MimeBodyPart body = new MimeBodyPart();
                        body.setContent(content, "text/html; charset=UTF-8");
                        multipart.addBodyPart(body);
                        mimeMessage.setContent(multipart);
                        //mimeMessage.setText(content);
                    }
                    // 开始发信
                    mimeMessage.saveChanges();
                    Transport.send(mimeMessage);
                    return true;
                } catch (MessagingException | IOException e) {
                    e.printStackTrace();
                    return false;
                }
            }
            return false;
        }
    }

    邮件模板

    @Data
    @NoArgsConstructor
    public class MailTemplate implements Serializable {
    
        private String msgId;
        /**
         * 收件人
         */
        private String to;
    
        /**
         * 收件人名称
         */
        private String toName;
    
        /**
         * 主题
         */
        private String subject;
    
        /**
         * 内容
         */
        private String content;
    
        /**
         * 附件列表
         */
        private List<String> attachFileList;
    
        /**
         * 邮箱账号
         */
        private String account;
    
        /**
         * 邮箱密码
         */
        private String password;
    
        /**
         * 邮箱类型
         */
        private String sendType;
    
        /**
         * 构造器
         *
         * @param to      收件人
         * @param subject 主题
         * @param content 内容
         */
        public MailTemplate(String account, String password, String sendType, String to, String toName, String subject, String content) {
            this.account = account;
            this.password = password;
            this.sendType = sendType;
            this.to = to;
            this.toName = toName;
            this.subject = subject;
            this.content = content;
        }
    
        /**
         * 构造器
         *
         * @param to             收件人
         * @param subject        主题
         * @param content        内容
         * @param attachFileList 附件列表
         */
        public MailTemplate(String account, String password, String sendType, String to, String toName, String subject, String content, List<String> attachFileList) {
            this(account, password, sendType, to, toName, subject, content);
            this.attachFileList = attachFileList;
        }
    }

    rabbit mq配置类

    @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";
        public final static Integer MAIL_DELIVER_SUCCESS = 1;
        public final static Integer MAIL_DELIVER_FAIL = 2;
        public final static Integer MAIL_CONSUMED_SUCCESS = 3;
        public static boolean ENABLE_SCHEDULED = false;
        private final CachingConnectionFactory connectionFactory;
    
        @Autowired
        private IMsgLogService iMsgLogService;
    
        public RabbitConfig(CachingConnectionFactory connectionFactory) {
            this.connectionFactory = connectionFactory;
        }
    
        @Bean
        public RabbitTemplate rabbitTemplate() {
            RabbitTemplate rabbitTemplate = new RabbitTemplate(connectionFactory);
            rabbitTemplate.setMessageConverter(converter());
    
            // 消息是否成功发送到Exchange
            rabbitTemplate.setConfirmCallback((correlationData, ack, cause) -> {
                if (ack) {
                    log.info("消息成功发送到Exchange");
                    String msgId = correlationData.getId();
                    UpdateWrapper<MsgLog> updateWrapper = new UpdateWrapper<>();
                    updateWrapper.eq("msg_id",msgId);
                    MsgLog msgLog = new MsgLog();
                    msgLog.setStatus(MAIL_DELIVER_SUCCESS);
                    iMsgLogService.update(msgLog, updateWrapper);
                } 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;
        }
    
        @Bean
        public Jackson2JsonMessageConverter converter() {
            return new Jackson2JsonMessageConverter();
        }
    
        @Bean
        public Queue mailQueue() {
            return new Queue(MAIL_QUEUE_NAME, true);
        }
    
        @Bean
        public DirectExchange mailExchange() {
            return new DirectExchange(MAIL_EXCHANGE_NAME, true, false);
        }
    
        @Bean
        public Binding mailBinding() {
            return BindingBuilder.bind(mailQueue()).to(mailExchange()).with(MAIL_ROUTING_KEY_NAME);
        }
    
    }

    生产者

    @RestController
    @RequestMapping("mail")
    public class MailController {
    
        @Autowired
        private IMsgLogService iMsgLogService;
        @Autowired
        private RabbitTemplate rabbitTemplate;
      
    
        @PostMapping("/test")
        public void test(String account, String password, String sendType) {
            try {
                for (int j = 1; j <= 3; j++) {
                    for (int i = 1; i <= 15; i++) {
                        for (int num = 1; num <= 73; num++) {
                            // 设置发信参数
                            final String toName = "我是" + num + "号";
                            final String to = "test" + num + "@forexgwg.com";
                            String subject = num + " 第" + num + "次发送测试邮件标题";
                            final String content = "<p style='color:red'>这是邮件内容正文。</p></br>";
                            MailTemplate mailTemplate = new MailTemplate();
                            String msgId = UUID.randomUUID().toString();
                            mailTemplate.setMsgId(msgId);
                            mailTemplate.setAccount(account);
                            mailTemplate.setPassword(password);
                            mailTemplate.setSendType(sendType);
                            mailTemplate.setToName(toName);
                            mailTemplate.setTo(to);
                            mailTemplate.setSubject(subject);
                            mailTemplate.setContent(content);
                            mailTemplate.setAttachFileList(new ArrayList<>());
    
                            MsgLog msgLog = new MsgLog(msgId, JSON.toJSONString(mailTemplate), RabbitConfig.MAIL_EXCHANGE_NAME, RabbitConfig.MAIL_ROUTING_KEY_NAME, LocalDateTime.now());
                            iMsgLogService.save(msgLog);
                            CorrelationData correlationData = new CorrelationData(msgId);
                            Thread.sleep(1000);
                            rabbitTemplate.convertAndSend(RabbitConfig.MAIL_EXCHANGE_NAME, RabbitConfig.MAIL_ROUTING_KEY_NAME, JSON.toJSONString(mailTemplate), correlationData);// 发送消息
                        }
                    }
                }
                RabbitConfig.ENABLE_SCHEDULED = true;
            } catch (Exception e) {
                System.out.println("错误: " + e);
            }
        }
    }

    消费者

    @Component
    @Slf4j
    public class MailConsumer {
    
        @Autowired
        private IMsgLogService iMsgLogService;
    
        @RabbitListener(queues = RabbitConfig.MAIL_QUEUE_NAME)
        public void consume(Message message, Channel channel) throws IOException {
            String msg = new String(message.getBody());
            msg = msg.replaceAll("\\", "");
            msg = msg.substring(1, msg.length() - 1);
            MailTemplate mailTemplate = JSON.parseObject(msg, MailTemplate.class);
            log.info("收到消息: {}", mailTemplate.toString());
    
            String msgId = mailTemplate.getMsgId();
            QueryWrapper<MsgLog> queryWrapper = new QueryWrapper<>();
            queryWrapper.eq("msg_id", msgId);
            UpdateWrapper<MsgLog> updateWrapper = new UpdateWrapper<>();
            updateWrapper.eq("msg_id", msgId);
            MsgLog msgLog = iMsgLogService.getOne(queryWrapper);
    
            // 消费幂等性
            if (null == msgLog || msgLog.getStatus().equals(RabbitConfig.MAIL_CONSUMED_SUCCESS)) {
                log.info("重复消费, msgId: {}", msgId);
                return;
            }
    
            msgLog.setStatus(3);
            msgLog.setUpdateTime(LocalDateTime.now());
            iMsgLogService.update(msgLog, updateWrapper);
    
            MessageProperties properties = message.getMessageProperties();
            long tag = properties.getDeliveryTag();
    
            boolean success = new SendEmailUtil(mailTemplate, new ArrayList<>()).send();
            if (success) {
                msgLog.setStatus(RabbitConfig.MAIL_CONSUMED_SUCCESS);
                msgLog.setUpdateTime(LocalDateTime.now());
                iMsgLogService.update(msgLog, updateWrapper);
                log.info("消费成功!");
                channel.basicAck(tag, false);// 消费确认
            } else {
                channel.basicNack(tag, false, true);
            }
        }
    }

    重新发送

    @Component
    @Slf4j
    public class ResendMsg {
    
        @Autowired
        private IMsgLogService iMsgLogService;
    
        @Autowired
        private RabbitTemplate rabbitTemplate;
    
        // 最大投递次数
        private static final int MAX_TRY_COUNT = 3;
    
        /**
         * 每30s拉取投递失败的消息, 重新投递
         */
        @Scheduled(cron = "0 0/1 * * * ?")
        public void resend() {
            log.info("开始执行定时任务(重新投递消息)");
            UpdateWrapper<MsgLog> updateWrapper = new UpdateWrapper<>();
            QueryWrapper<MsgLog> queryWrapper = new QueryWrapper<>();
            queryWrapper.ne("status", RabbitConfig.MAIL_DELIVER_FAIL).ne("status", RabbitConfig.MAIL_CONSUMED_SUCCESS);
    
            List<MsgLog> msgLogs = iMsgLogService.list(queryWrapper);
            if (msgLogs.size() == 0){
                RabbitConfig.ENABLE_SCHEDULED = false;
            }
            msgLogs.forEach(msgLog -> {
                String msgId = msgLog.getMsgId();
                updateWrapper.eq("msg_id", msgId);
                if (msgLog.getTryCount() >= MAX_TRY_COUNT) {
                    msgLog.setStatus(RabbitConfig.MAIL_DELIVER_FAIL);
                    msgLog.setUpdateTime(LocalDateTime.now());
                    iMsgLogService.update(msgLog, updateWrapper);
                    log.info("超过最大重试次数, 消息投递失败, msgId: {}", msgId);
                } else {
                    msgLog.setTryCount(msgLog.getTryCount() + 1);
                    msgLog.setUpdateTime(LocalDateTime.now());
                    msgLog.setNextTryTime(LocalDateTime.now().plusSeconds(60));
                    iMsgLogService.update(msgLog, updateWrapper);// 投递次数+1
    
                    CorrelationData correlationData = new CorrelationData(msgId);
                    rabbitTemplate.convertAndSend(msgLog.getExchange(), msgLog.getRoutingKey(), msgLog.getMsg(), correlationData);// 重新投递
                    log.info("第 " + (msgLog.getTryCount() + 1) + " 次重新投递消息");
                }
            });
            log.info("定时任务执行结束(重新投递消息)");
        }
    
    }

     使用不同的兩個帳戶发送email时,第一个账户可以发送成功,但到第二个账户的时候就报出了501 mail from address must be same as authorization user的错误。

    Session session = Session.getDefaultInstance(props, auth);
    以上改成
    Session session = Session.getInstance(props, auth);
  • 相关阅读:
    【VS开发】CTimeSpan类
    【VS开发】CTimeSpan类
    【VS开发】【电子电路技术】PCI与PCIE主板插卡与插槽识别
    【VS开发】【电子电路技术】PCI与PCIE主板插卡与插槽识别
    【VS开发】PCIe体系结构的组成部件
    【VS开发】PCIe体系结构的组成部件
    【VS开发】【DSP开发】WinDriver简介(或介绍)
    转载 ------ 三次指数平滑法
    转载:二次指数平滑法求预测值的Java代码
    转载: 通过反射操作类的私有属性
  • 原文地址:https://www.cnblogs.com/jxd283465/p/11982076.html
Copyright © 2020-2023  润新知