使用队列邮箱发送

@Async  异步调用,方法不跑完可以接着跑别的方法,本质通过另起线程池
RateLimiter sendRateLimiter = RateLimiter.create(1.33) 控制每秒最多1.33次,即每分钟80次,可用来限制自身调用其他接口TPS
sendRateLimiter.acquire() //获取该许可证。一旦获取到许可证,不需要再释放许可证
//发送内容放入queue中,启动任务后单跑一个发送线程


@Service("mailService")
public class MailServiceImpl implements MailService {

    private static final Logger LOG = LoggerFactory.getLogger(MailServiceImpl.class);

    @Resource
    private MailProperties mailProperties;

    private Thread sendThread;

    private BlockingQueue<MailDTO> sendQueue;

    @PostConstruct
    public void startThread() {
        initSendQueue();
        sendThread = new Thread(new SendConsumer());
        sendThread.start();
        LOG.info(String.format("邮件发送线程已启动[%s]", sendThread.getName()));
    }

    // 追加到发送队列队尾超时时间(单位:秒)
    private final long OFFER_SEND_QUEUE_TIMEOUT = 6;

    public void offerSendQueue(MailDTO mailDTO) {
        initSendQueue();
        try {
            // 收件人超过30位,拆成多封。
            if (mailDTO.getTo().length <= 30) {
                boolean insertFlag = sendQueue.offer(mailDTO, OFFER_SEND_QUEUE_TIMEOUT, TimeUnit.SECONDS);
                LOG.info(String.format("[%s]插入邮件发送队列[%s]!", JSONObject.toJSONString(mailDTO), insertFlag));
            } else {
                List<MailDTO> mailList = splitMail(mailDTO);
                for (MailDTO tempMail : mailList) {
                    boolean insertFlag = sendQueue.offer(tempMail, OFFER_SEND_QUEUE_TIMEOUT, TimeUnit.SECONDS);
                    LOG.info(String.format("[%s]插入邮件发送队列[%s]!", JSONObject.toJSONString(tempMail), insertFlag));
                }
            }
        } catch (InterruptedException e) {
            LOG.error("发送邮件插入阻塞队列发生中断异常:", e);
        }

    }

    /**
     * 初始化邮件发送阻塞队列
     */
    private void initSendQueue() {
        if (null != sendQueue) {
            return;
        }
        synchronized (this) {
            if (null == sendQueue) {
                sendQueue = new LinkedBlockingQueue<>();
            }
        }
    }

    /**
     * 从阻塞队列中获取队首元素,发送到邮件中继MRU(Mail Relay Unit)
     */
    class SendConsumer implements Runnable {

        private RateLimiter sendRateLimiter = RateLimiter.create(1.33);

        @Override
        public void run() {
            try {
               //如果时长要求不高也可改为定时,下面这个写法如果在FULL GC的时候会发送中断信号
                while (!Thread.interrupted()) {
                    // 控制发送速率,当前设置为每分钟接近80封。
                    sendRateLimiter.acquire();
                    // 从发送队列中获取待发送邮件。
                    MailDTO mailDTO = sendQueue.take();
                    LOG.info(String.format("邮件发送线程[%s]:[%s]开始发送!", Thread.currentThread().getName(),
                            JSONObject.toJSONString(mailDTO)));
                    sendToMRU(mailDTO);
                }
                LOG.warn(String.format("邮件发送线程已停止[%s]", Thread.currentThread().getName()));
            } catch (InterruptedException e) {
                LOG.error("发送邮件阻塞队列消费者发生中断异常:", e);
            }
        }
    }

    private void sendToMRU(MailDTO mailDTO) {
        String defaultFrom = mailProperties.getDefaultMailFrom();
        String smtpHost = mailProperties.getSmtpHost();
        String smtpPort = mailProperties.getSmtpPort();
        LOG.info("邮件发送线程defaultFrom=" + defaultFrom + "smtpHost" + smtpHost + "smtpPort" + smtpPort);
        // 如果发件人为null,则设置为默认发件人
        String from = mailDTO.getFrom();
        if (null == from || "".equals(from)) {
            from = defaultFrom;
            mailDTO.setFrom(defaultFrom);
        }

        try {
            Properties props = getProperties();
            Session mailSession = Session.getDefaultInstance(props);
            MimeMessage msg = new MimeMessage(mailSession);
            // 发件人
            msg.addFrom(InternetAddress.parse(mailDTO.getFrom()));
            // 收件人列表
            InternetAddress[] addresses = parseAddresses(mailDTO.getTo());
            msg.setRecipients(Message.RecipientType.TO, addresses);

            // 抄送人邮箱
            if (null != mailDTO.getCc() && mailDTO.getCc().length > 0) {
                addresses = parseAddresses(mailDTO.getCc());
                if (addresses.length > 0) {
                    msg.setRecipients(Message.RecipientType.CC, addresses);
                }
            }

            // 暗送人邮箱
            if (mailDTO.getBcc() != null && mailDTO.getBcc().length > 0) {
                addresses = parseAddresses(mailDTO.getBcc());
                if (addresses.length > 0) {
                    msg.setRecipients(Message.RecipientType.BCC, addresses);
                }
            }

            // 发送时间
            if (msg.getSentDate() == null) {
                msg.setSentDate(new Date());
            }

            String encoding = "UTF-8";

            // 主题
            msg.setSubject(mailDTO.getSubject(), encoding);

            // 内容
            BodyPart bpContent = new MimeBodyPart();
            bpContent.setContent(mailDTO.getContent(), "text/html; charset=" + encoding);
            Multipart mp = new MimeMultipart();
            mp.addBodyPart(bpContent);
            msg.setContent(mp);

            Transport.send(msg);
        } catch (Exception e) {
            LOG.error("邮件发送失败,请与管理员联系!", e);
        }
    }


    private Properties getProperties(){
        Properties props = new Properties();
        String smtpHost = mailProperties.getSmtpHost();
        String smtpPort = mailProperties.getSmtpPort();
        props.put("mail.transport.protocol", "smtp"); // 发件协议
        props.put("mail.smtp.host", smtpHost); // 邮件服务器地址
        props.put("mail.smtp.port", smtpPort); // 端口
        return props;
    }

    /**
     * 字符串数组转换为 InternetAddress型数组
     */
    private InternetAddress[] parseAddresses(String[] to) throws AddressException {
        List<InternetAddress> list = new ArrayList<>();
        if (null == to) {
            return (InternetAddress[]) list.toArray(new InternetAddress[list.size()]);
        }

        for (int i = 0; i < to.length; i++) {
            String address = to[i];
            if (null != address && !"".equals(address)) {
                InternetAddress[] addresses = InternetAddress.parse(address);
                list.addAll(Arrays.asList(addresses));
            }

        }
        return (InternetAddress[]) list.toArray(new InternetAddress[list.size()]);
    }

    private List<MailDTO> splitMail(MailDTO mailDTO) {
        String[] to = mailDTO.getTo();
        List<MailDTO> mailList = new ArrayList<>();
        int start = 0;
        int end = 30;
        while (end <= to.length) {
            String[] tempTo = Arrays.copyOfRange(to, start, end);
            MailDTO tempMail = copyNotToField(mailDTO);
            tempMail.setTo(tempTo);
            mailList.add(tempMail);
            start += 30;
            end += 30;
        }

        if (start < to.length && end > to.length) {
            MailDTO tempMail = copyNotToField(mailDTO);
            String[] tempTo = Arrays.copyOfRange(to, start, to.length);
            tempMail.setTo(tempTo);
            mailList.add(tempMail);
        }
        return mailList;
    }

    private MailDTO copyNotToField(MailDTO origin) {
        MailDTO targetMail = new MailDTO();
        targetMail.setSubject(origin.getSubject());
        targetMail.setContent(origin.getContent());
        targetMail.setFrom(origin.getFrom());
        targetMail.setSentDate(origin.getSentDate());
        targetMail.setBcc(origin.getBcc());
        targetMail.setCc(origin.getCc());
        return targetMail;
    }

}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值