Java Mail + 消息队列 高效率发送邮件

  本文主要是讲述JavaMail 和 消息队列的一些简单应用,这是第一次写博客,有很多不足的地方希望各位看客给出宝贵建议,另外本文写的不对的地方,请各位大神勿喷!!!

  很多人都会在各个系统里面遇到发送邮件的功能,这次开博写文章也是在整理过程中发现,团队中有个新来的小童鞋对发送邮件这块不是很熟,所以自己准备写一个简单的样例。

  这里加了一个消息队列主要是用于高并发的情况下,对邮件发送的控制,并可以对消息进行缓存,防止消息丢失。

  话不多说直接上代码:

  用maven对项目进行的管理,pom.xml

    <dependency>
           <groupId>javax.mail</groupId>
           <artifactId>mail</artifactId>
           <version>1.4.7</version>
    </dependency>

  邮件实体:

 

package com.test.utils.mail;

import java.io.Serializable;

/**
 *
 * @author
 *
 */
public class MailVo {

    /**
     * 注释内容
     */
    private static final long serialVersionUID = 1L;

    private String receiver;

    private String subject;

    private String content;

    public String getReceiver() {
        return receiver;
    }

    public void setReceiver(String receiver) {
        this.receiver = receiver;
    }

    public String getSubject() {
        return subject;
    }

    public void setSubject(String subject) {
        this.subject = subject;
    }

    public String getContent() {
        return content;
    }

    public void setContent(String content) {
        this.content = content;
    }

    public static long getSerialversionuid() {
        return serialVersionUID;
    }

}

  邮件基本配置config.properties

#mail smtp host
mail.smtp.host = smtp.exmail.qq.com
mail.smtp.port = 465
mail.smtp.username = test
mail.sender = [email protected]
mail.smtp.password = test

  很多人用的是多线程来做的这个地方,我考虑了下,我觉得用队列比较合适,因为如果有大量需要发送邮件的时候,新建太多的线程会对系统产生不必要的开销,虽然可以集合线程池的方式来做,当然如果是邮件业务比较多,多线程的并发会好点。

  我考虑了下,有时候邮件业务不是很多,所以,我给单线程加了个状态维护,在3分钟内没有任务,则销毁该线程,等待新的任务再重新创建新的线程。

package com.test.utils.mail;

import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.TimeUnit;

import org.apache.log4j.Logger;

/**
 * 邮件发送管理插件
 * 主要用于管理邮件队列和发送邮件
 * @author
 *
 */
public class SendMailManager{

    private static Logger logger = Logger.getLogger(SendMailManager.class);

    //缓存发送邮件的队列
    public static BlockingQueue<MailVo> queue = new ArrayBlockingQueue<MailVo>(2000);
    //发送邮件线程状态
    private static boolean isRunning = false;
    //超时时间间隔
    public static long TIMEOUT_INTERVAL = 3*60*1000;

    public static boolean sendMail(MailVo mail){
        boolean rsp = false;
        try {
            //阻塞3秒
            rsp = queue.offer(mail, 3, TimeUnit.SECONDS);

            if(!rsp){
                throw new Exception("服务器发送邮件繁忙,请稍后再发");
            }

            if(!isRunning){
                startup();
                isRunning = true;
            }

        } catch (Exception e) {
            logger.error("添加发送邮件队列出错", e);
        }
        return rsp;
    }

    public static void startup(){
        if(isRunning){
            return;
        }

        Thread th = new  Thread(){

            @Override
            public void run(){
                logger.debug("邮件发送消息线程启动");

                long timestamp = System.currentTimeMillis();
                while(isRunning)
                {
                    long timeout = timestamp + TIMEOUT_INTERVAL;
                    if(System.currentTimeMillis()>timeout)
                    {
                        //空跑一段时间(3分钟)后线程退出
                        break;
                    }

                    try
                    {
                        if(queue.size()==0)
                        {
                            Thread.sleep(1000);
                            continue;
                        }
                        timestamp = System.currentTimeMillis();
                        MailVo mail = queue.poll();
                        MailUtil.sendMail(mail);

                        //发送一个邮件休息2秒,防止发送过快,导致主油箱被锁定
                        Thread.sleep(2000);

                    } catch (Exception e)
                    {
                        logger.error("邮件推送线程出错",e);
                    }
                }
                isRunning = false;
                logger.debug("邮件发送线程停止。");
            }
        };

        th.start();
    }

}

  邮件发送工具:

package com.test.utils.mail;

import java.io.UnsupportedEncodingException;
import java.security.GeneralSecurityException;
import java.util.ArrayList;
import java.util.List;

import javax.mail.MessagingException;
import javax.mail.Session;
import javax.mail.Transport;
import javax.mail.internet.InternetAddress;
import javax.mail.internet.MimeMessage;
import javax.mail.internet.MimeUtility;

import org.apache.log4j.Logger;
import org.springframework.util.StringUtils;

import com.sun.mail.smtp.SMTPSendFailedException;
import com.sun.mail.util.MailSSLSocketFactory;
import com.test.constant.Config;
import com.test.utils.PropUtil;

/**
 * @描述:
 *
 * @author 作者 :
 *
 */
public class MailUtil {

    private static Logger logger = Logger.getLogger(MailUtil.class);

    public static boolean sendMail(MailVo mailVo){
        boolean flag = false;
        PropUtil pro = new PropUtil(Config.CONFIG_PATH);
        String smtpHost = "";
        String smtpport = "";
        String from = "";
        String password = "";
        String to = "";
        String subject = "";
        String messageText = "";
        try {
            smtpHost = pro.getValue(Config.MAIL_SMTP_HOST);
            smtpport = pro.getValue(Config.MAIL_SMTP_PORT);
            from = pro.getValue(Config.MAIL_SENDER);
            password = pro.getValue(Config.MAIL_SMTP_PASSWORD);
            to = mailVo.getReceiver();
            subject = mailVo.getSubject();
            messageText = mailVo.getContent();
            int sendFlag = sendMessage(smtpHost, smtpport, from, password, to, subject, messageText);
            flag = true;
            if(sendFlag == 200){
                logger.debug("邮件发送成功:发件人 = " + from + "; 收件人 = " + to + "; 主题  = " + subject + "; 内容  = " + messageText);
            }else{
                logger.debug("邮件发送失败:发件人 = " + from + "; 收件人 = " + to + "; 主题  = " + subject + "; 内容  = " + messageText);
            }
        } catch (Exception e) {
            logger.debug("发送邮件失败,失败内容:smtpHost = " + smtpHost);
            logger.debug("发送邮件失败,失败内容:smtpport = " + smtpport);
            logger.debug("发送邮件失败,失败内容:from = " + from);
            logger.debug("发送邮件失败,失败内容:password = " + password);
            logger.debug("发送邮件失败,失败内容:to = " + to);
            logger.debug("发送邮件失败,失败内容:smtpHost = " + smtpHost);
            logger.debug("发送邮件失败,失败内容:subject = " + subject);
            logger.debug("发送邮件失败,失败内容:messageText = " + messageText);
            logger.error("发送邮件失败", e);
        }
        return flag;
    }

    public static int sendMessage(String smtpHost,String smtpport,
            String from, String password, String to,
            String subject, String messageText){
        try {
            //1、配置邮件基本属性
            java.util.Properties props = new java.util.Properties();

            //2、判断是否需要开启SSL
            if(!StringUtils.isEmpty(smtpport) && smtpport.equals("465")){
                MailSSLSocketFactory sf;
                try {
                    sf = new MailSSLSocketFactory();
                    sf.setTrustAllHosts(true);
                    props.put("mail.smtp.ssl.enable", "true");
                    //props.put("mail.smtp.ssl.checkserveridentity", "true");
                    props.put("mail.smtp.ssl.socketFactory", sf);
                } catch (GeneralSecurityException e1) {
                    logger.error("开始SSL设置出错", e1);
                }
            }

            props.setProperty("mail.smtp.auth", "true");//指定是否需要SMTP验证
            props.setProperty("mail.smtp.host", smtpHost);//指定SMTP服务器
            props.setProperty("mail.smtp.port", smtpport);//指定SMTP服务器端口
            props.put("mail.transport.protocol", "smtp");
            Session mailSession = Session.getInstance(props);
            mailSession.setDebug(true);//是否在控制台显示debug信息

            //3、构建发送邮件的具体消息体
            logger.debug("Constructing message -  from=" + from + "  to=" + to);
            String nick = javax.mail.internet.MimeUtility.encodeText("邮件用户昵称,根据自己实际定义");
            //设置发送人
             InternetAddress fromAddress = new InternetAddress(nick+" <"+from+">");
            //设置接收人
            InternetAddress toAddress;
            toAddress = new InternetAddress(to);

            MimeMessage testMessage = new MimeMessage(mailSession);

            try {
                testMessage.setFrom(fromAddress);
            } catch (MessagingException e) {
                logger.error(e);
                return 535;
            }
            try {
                testMessage.addRecipient(javax.mail.Message.RecipientType.TO, toAddress);
            } catch (MessagingException e) {
                logger.error(e);
                return 534;
            }
            testMessage.setSentDate(new java.util.Date());
            testMessage.setSubject(MimeUtility.encodeText(subject,"gb2312","B"));
            //设置消息文本
            testMessage.setContent(messageText, "text/html;charset=gb2312");
            System.out.println("Message constructed");
            Transport transport;
            transport = mailSession.getTransport("smtp");

            //设置邮箱服务器     用户名    密码
            transport.connect(smtpHost, from, password);
            transport.sendMessage(testMessage, testMessage.getAllRecipients());

            transport.close();

            return 200;
        }catch(SMTPSendFailedException e){
            logger.error(e);
            return 550;
        }catch (MessagingException e) {
            logger.error(e);
            return 500;
        } catch (UnsupportedEncodingException e) {
            logger.error(e);
            return 501;//文本消息有错
        }
    }
}

  本文是用的腾讯企业邮箱作为测试的,但是发现在测试过程中,很容易造成:

javax.mail.AuthenticationFailedException: 535 Error: authentication failed, system busy

  用于登录失败,所以整理了下具体原因:

  1:邮箱有独立密码,非邮箱登录密码

  2:邮箱设置的客户端单独授权码
  进入邮箱,点击设置:

  

  

  额,第一次写文,话不多说,直接都是给的精华,希望初学者能理解到。

  另外代码封装不是很好,主要是为了测试,请在自己项目中重新封装下,以便于程序的扩展。

  

时间: 2024-11-04 21:23:58

Java Mail + 消息队列 高效率发送邮件的相关文章

java JMS消息队列

http://blog.csdn.net/shirdrn/article/details/6362792 http://haohaoxuexi.iteye.com/blog/1893038 http://afreon.blog.163.com/blog/static/223794094201431422654237/ http://www.cnblogs.com/huang0925/p/3558690.html ActiveMQ第二弹:使用Spring JMS与ActiveMQ通讯 本文章的完整

Java常用消息队列原理介绍及性能对比

消息队列使用场景 为什么会需要消息队列(MQ)? 解耦  在项目启动之初来预测将来项目会碰到什么需求,是极其困难的.消息系统在处理过程中间插入了一个隐含的.基于数据的接口层,两边的处理过程都要实现这一接口.这允许你独立的扩展或修改两边的处理过程,只要确保它们遵守同样的接口约束. 冗余  有些情况下,处理数据的过程会失败.除非数据被持久化,否则将造成丢失.消息队列把数据进行持久化直到它们已经被完全处理,通过这一方式规避了数据丢失风险.许多消息队列所采用的"插入-获取-删除"范式中,在把一

[Java] 分布式消息队列(MQ)

概述 场景 服务解耦 削峰填谷 异步化缓冲:最终一致性/柔性事务 MQ应用思考点 生产端可靠性投递 消费端幂等:消息只能消费一次 高可用.低延迟.可靠性 消息堆积能力 可扩展性 业界主流MQ ActiveMQ:适合传统需求,并发性差 RabbitMQ:扩展性差 RocketMQ:扩展性强 Kafka:扩展性强,并发性强,可靠性差 技术选型 性能.优缺点.业务场景 集群架构模式,分布式.可扩展.高可用.可维护性 综合成本,集群规模,人员成本 未来的方向.规划.思考 ActiveMQ 介绍 JMS(

Netty构建分布式消息队列实现原理浅析

在本人的上一篇博客文章:Netty构建分布式消息队列(AvatarMQ)设计指南之架构篇 中,重点向大家介绍了AvatarMQ主要构成模块以及目前存在的优缺点.最后以一个生产者.消费者传递消息的例子,具体演示了AvatarMQ所具备的基本消息路由功能.而本文的写作目的,是想从开发.设计的角度,简单的对如何使用Netty,构建分布式消息队列背后的技术细节.原理,进行一下简单的分析和说明. 首先,在一个企业级的架构应用中,究竟何时需引入消息队列呢?本人认为,最经常的情况,无非这几种:做业务解耦.事件

redis消息队列简单应用

参考 https://blog.yxccan.cn/blog/detail/3 一.什么是消息队列 是一个消息的链表,是一个异步处理的数据处理引擎. PS:可以理解为在redis的list列表中存放消息数据,然后按照排队方式先进先出(左进右出:右进左出) 二.可以使用的应用场景 主要应用一些延迟或异步操作的场景比如:发送邮件.发送短信.视频转码.图片转码.日志存储.导入数据等在发送邮件或者短信,我们不希望程序一直停留,等待发送成功才相应,而是异步进行处理,即:将待发送的邮件数据添加到消息队列中,

挨踢部落坐诊第四期:Java消息队列的应用场景和作用

挨踢部落是为核心开发者提供深度技术交流,解决开发需求,资源共享的服务社群.基于此社群,我们邀请了业界技术大咖对开发需求进行一对一突破,解除开发过程中的绊脚石.以最专业.最高效的答复为开发者解决开发难题. 消息队列 话题关键词:消息队列.索引.App.路由.接口 部落阵容:51CTO管理团队: 面向对象:移动开发者.IT运维.数据分析师 参与方式:加入51CTO开发者QQ交流群(群号370892523(已满).请加312724475),有任何技术问题,在群里提问,或发给群主小官. 活动详情: 重庆

基于java mail实现简单的QQ邮箱发送邮件

刚学习到java邮件相关的知识,先写下这篇博客,方便以后翻阅学习. -----------------------------第一步 开启SMTP服务 在 QQ 邮箱里的 设置->账户里开启 SMTP 服务 完成验证 获取授权码(后面代码实现时使用) -----------------------------第二步 环境配置 即下载第三方库 https://github.com/javaee/javamail/releases -----------------------------第三步 代

java mail发送邮件

import java.io.UnsupportedEncodingException;import java.util.Date;import java.util.Properties; import javax.mail.Authenticator;import javax.mail.Message;import javax.mail.MessagingException;import javax.mail.NoSuchProviderException;import javax.mail.

java mail发送邮件demo 代码

java mail发送邮件demo,引入mail.jar,运行测试发送ok[代码][Java]代码     01import java.util.Date;02import java.util.Properties;0304import javax.mail.Authenticator;05import javax.mail.Message;06import javax.mail.MessagingException;07import javax.mail.PasswordAuthenticat