SpringBoot開發案例之整合mail隊列篇
前言
前段時間搞了個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,有興趣的同學可以自行查閱資料。
分享總結
如果,你看到你寫的代碼是一坨屎的時候你就該去優化她了,好好愛護她,未來的你會為昨天的你而感到驕傲的。
其實,想表達的是,架構優化是無止境的,隨著業務的增長以及平台的發展,我們會遇到各種各樣的問題。
- 郵件服務掛了,隊列還沒消費完怎麼辦?
- 郵件服務掛了,我們是否應該做個高可用?
- 郵件服務爆了,我們是否應該做個負載均衡?
以上問題,你又會怎麼解決呢?下一篇為大家帶來高可用的郵件服務平台。
項目案例:碼雲
作者: 小柒
出處: https://blog.52itstyle.com
分享是快樂的,也見證了個人成長曆程。文章大多都是工作經驗總結以及平時學習積累,基於自身認知不足之處在所難免,也請大家指正,共同進步。
本文版權歸作者和雲棲社區所有,歡迎轉載,但未經作者同意必須保留此段聲明,且在文章頁麵明顯位置給出, 如有問題, 可郵件(345849402@qq.com)谘詢。
最後更新:2017-08-13 22:29:20