SpringBoot开发案例之整合mail队列篇

科帮网邮件队列.png

前言

前段时间搞了个SpringBoot开发案例之整合mail发送服务,也是基于目前各项目平台的邮件发送功能做一个抽离和整合。

问题

以发送方为例,比如网易的反垃圾邮件政策,过多或者频率过快的发送都会被判定为垃圾邮件,即使发再多的邮件也无济于事。当然如果是自建邮件服务器,也是要考虑发送服务的并发能力。

以接收方邮件为例,比如腾讯邮箱就有类似说明:如果内容涉嫌大量群发,并且被多数用户投诉为垃圾邮件,也就通过不了收件方的"安检",如下图:

maileror.png

方案

为了解决以上实际场景中遇到的问题,使得其更像一个安全高效的邮件服务平台,我们尝试引入了任务队列对邮件发送进行流量削锋、间隔发送以及重复内容检测。

首先,我们先建一个队列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方法在队列空的时候会阻塞,直到有队列成员被放进来。

qj6Yi4M_png.png

最后给大家补充一个非阻塞队列ConcurrentLinkedQueue,有兴趣的同学可以自行查阅资料。

##分享总结

如果,你看到你写的代码是一坨屎的时候你就该去优化她了,好好爱护她,未来的你会为昨天的你而感到骄傲的。

其实,想表达的是,架构优化是无止境的,随着业务的增长以及平台的发展,我们会遇到各种各样的问题。

  • 邮件服务挂了,队列还没消费完怎么办?
  • 邮件服务挂了,我们是否应该做个高可用?
  • 邮件服务爆了,我们是否应该做个负载均衡?

以上问题,你又会怎么解决呢?下一篇为大家带来高可用的邮件服务平台。

项目案例:码云

原创声明,本文系作者授权云+社区发表,未经许可,不得转载。

如有侵权,请联系 yunjia_community@tencent.com 删除。

发表于

我来说两句

0 条评论
登录 后参与评论

相关文章

来自专栏北京马哥教育

某次压测时物理内存被用光 Tomcat 被 Kernel kill 掉的案例

? 背景描述 某项目结构图如下(前端交互式体验及对象存储为主,Redis 及 rds 负载较小没有画出): ? web1 和 web2 是两个 Apache,...

33270
来自专栏進无尽的文章

多线程-概述及底层实现机制浅析

在打算写这篇多线层底层实现机制的时候,突然发现自己对于计算机竟然懂得这么表面,对于CPU的工作原理都不完全清楚,于是又转头查看了一些CPU相关的资料。也不敢钻的...

20110
来自专栏云计算

ElasticMQ 0.7.0:长轮询,使用Akka和Spray的非阻塞实现

原文地址:https://dzone.com/articles/elasticmq-070-long-polling-non

22860
来自专栏程序员宝库

教你在不使用框架的情况下也能写出现代化 PHP 代码

我为你们准备了一个富有挑战性的事情。接下来你们将以无框架的方式开启一个项目之旅。 首先声明, 这篇并非又臭又长的反框架裹脚布文章。也不是推销非原创(https:...

36750
来自专栏美团技术团队

Android动态日志系统Holmes

背景 美团点评公司是全球领先的一站式生活服务平台,为6亿多消费者和超过450万优质商户提供连接线上线下的电子商务网络。美团点评的业务覆盖了超过200个丰富品类和...

703100
来自专栏hanlp学习笔记

菜鸟如何使用Hanlp

boss给了个做分词的任务,最开始想用的是结巴分词and正则表达式。后来发现结果并不好,需要一遍一遍筛选【第一个标准筛选出80%的数据,然后制定第二个标准,继续...

13130
来自专栏IT笔记

SpringBoot开发案例之整合mail队列篇

? 科帮网邮件队列.png 前言 前段时间搞了个SpringBoot开发案例之整合mail发送服务,也是基于目前各项目平台的邮件发送功能做一个抽离和整合。 问...

74670
来自专栏木东居士的专栏

从0写一个爬虫,爬取500w好友关系数据

72960
来自专栏生信宝典

测序文章数据上传找哪里

在我们发表高通量测序文章之前通常要上传测序数据到GEO数据库,现总结流程如下。 注册账户、填写MetaSheet 在NCBI GEO官网注册一个账号,然后登陆。...

23760
来自专栏Golang语言社区

Go 语言编写的缓存及缓存过滤库:groupcache

groupcache 是使用 Go 语言编写的缓存及缓存过滤库,作为 memcached 许多场景下的替代版本。 对比原始 memcached ? 首先,gr...

26940

扫码关注云+社区

领取腾讯云代金券