前言
前段时间搞了个SpringBoot开发案例之整合mail发送服务,也是基于目前各项目平台的邮件发送功能做一个抽离和整合。
问题
以发送方为例,比如网易的反垃圾邮件政策,过多或者频率过快的发送都会被判定为垃圾邮件,即使发再多的邮件也无济于事。当然如果是自建邮件服务器,也是要考虑发送服务的并发能力。
以接收方邮件为例,比如腾讯邮箱就有类似说明:如果内容涉嫌大量群发,并且被多数用户投诉为垃圾邮件,也就通过不了收件方的"安检",如下图:
方案
为了解决以上实际场景中遇到的问题,使得其更像一个安全高效的邮件服务平台,我们尝试引入了任务队列对邮件发送进行流量削锋、间隔发送以及重复内容检测。
首先,我们先建一个队列MailQueue:
/**
* 邮件队列
* 创建者 科帮网
* 创建时间 2017年8月4日
*
*/
public class MailQueue {
//队列大小
static final int QUEUE_MAX_SIZE = 1000;
static BlockingQueue<Email> blockingQueue = new LinkedBlockingQueue<Email>(QUEUE_MAX_SIZE);
/**
* 私有的默认构造子,保证外界无法直接实例化
*/
private MailQueue(){};
/**
* 类级的内部类,也就是静态的成员式内部类,该内部类的实例与外部类的实例
* 没有绑定关系,而且只有被调用到才会装载,从而实现了延迟加载
*/
private static class SingletonHolder{
/**
* 静态初始化器,由JVM来保证线程安全
*/
private static MailQueue queue = new MailQueue();
}
//单例队列
public static MailQueue getMailQueue(){
return SingletonHolder.queue;
}
//生产入队
public void produce(Email mail) throws InterruptedException {
blockingQueue.put(mail);
}
//消费出队
public Email consume() throws InterruptedException {
return blockingQueue.take();
}
// 获取队列大小
public int size() {
return blockingQueue.size();
}
}
如文章开头图片所描述,这里我们还需要建一个消费线程池ConsumeMailQueue:
/**
* 消费队列
* 创建者 科帮网
* 创建时间 2017年8月4日
*/
@Component
public class ConsumeMailQueue {
private static final Logger logger = LoggerFactory.getLogger(ConsumeMailQueue.class);
@Autowired
IMailService mailService;
@PostConstruct
public void startThread() {
ExecutorService e = Executors.newFixedThreadPool(2);// 两个大小的固定线程池
e.submit(new PollMail(mailService));
e.submit(new PollMail(mailService));
}
class PollMail implements Runnable {
IMailService mailService;
public PollMail(IMailService mailService) {
this.mailService = mailService;
}
@Override
public void run() {
while (true) {
try {
Email mail = MailQueue.getMailQueue().consume();
if (mail != null) {
logger.info("剩余邮件总数:{}",MailQueue.getMailQueue().size());
//可以设置延时 以及重复校验等等操作
mailService.send(mail);
}
} catch (Exception e) {
e.printStackTrace();
}
}
}
}
@PreDestroy
public void stopThread() {
logger.info("destroy");
}
}
改造service:
部分接口:
/**
* 队列
* @Author 科帮网
* @param mail
* @throws Exception void
* @Date 2017年8月4日
* 更新日志
* 2017年8月4日 科帮网 首次创建
*
*/
public void sendQueue(Email mail) throws Exception;
部分实现:
@Override
public void sendQueue(Email mail) throws Exception {
MailQueue.getMailQueue().produce(mail);
}
队列说明
以上代码,大家可以看到我们有使用到了linkedblockingqueue来实现邮件发送队列。
LinkedBlockingQueue作为一个阻塞队列是线程安全的,同时具有先进先出等特性,是作为生产者消费者的首选。
LinkedBlockingQueue可以指定容量,也可以不指定,不指定的话,默认最大是Integer.MAX_VALUE。
其中主要用到put和take方法,put方法在队列满的时候会阻塞直到有队列成员被消费,take方法在队列空的时候会阻塞,直到有队列成员被放进来。
最后给大家补充一个非阻塞队列ConcurrentLinkedQueue,有兴趣的同学可以自行查阅资料。
分享总结
如果,你看到你写的代码是一坨屎的时候你就该去优化她了,好好爱护她,未来的你会为昨天的你而感到骄傲的。
其实,想表达的是,架构优化是无止境的,随着业务的增长以及平台的发展,我们会遇到各种各样的问题。
- 邮件服务挂了,队列还没消费完怎么办?
- 邮件服务挂了,我们是否应该做个高可用?
- 邮件服务爆了,我们是否应该做个负载均衡?
以上问题,你又会怎么解决呢?下一篇为大家带来高可用的邮件服务平台。
项目案例:码云
Java 面试宝典是大明哥全力打造的 Java 精品面试题,它是一份靠谱、强大、详细、经典的 Java 后端面试宝典。它不仅仅只是一道道面试题,而是一套完整的 Java 知识体系,一套你 Java 知识点的扫盲贴。
它的内容包括:
- 大厂真题:Java 面试宝典里面的题目都是最近几年的高频的大厂面试真题。
- 原创内容:Java 面试宝典内容全部都是大明哥原创,内容全面且通俗易懂,回答部分可以直接作为面试回答内容。
- 持续更新:一次购买,永久有效。大明哥会持续更新 3+ 年,累计更新 1000+,宝典会不断迭代更新,保证最新、最全面。
- 覆盖全面:本宝典累计更新 1000+,从 Java 入门到 Java 架构的高频面试题,实现 360° 全覆盖。
- 不止面试:内容包含面试题解析、内容详解、知识扩展,它不仅仅只是一份面试题,更是一套完整的 Java 知识体系。
- 宝典详情:https://www.yuque.com/chenssy/sike-java/xvlo920axlp7sf4k
- 宝典总览:https://www.yuque.com/chenssy/sike-java/yogsehzntzgp4ly1
- 宝典进展:https://www.yuque.com/chenssy/sike-java/en9ned7loo47z5aw
目前 Java 面试宝典累计更新 400+ 道,总字数 42w+。大明哥还在持续更新中,下图是大明哥在 2024-12 月份的更新情况:
想了解详情的小伙伴,扫描下面二维码加大明哥微信【daming091】咨询
同时,大明哥也整理一套目前市面最常见的热点面试题。微信搜[大明哥聊 Java]或扫描下方二维码关注大明哥的原创公众号[大明哥聊 Java] ,回复【面试题】 即可免费领取。