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 = test@qq.com
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:郵箱設置的客戶端單獨授權碼
  進入郵箱,點擊設置:

  

  

  額,第一次寫文,話不多說,直接都是給的精華,希望初學者能理解到。

  另外代碼封裝不是很好,主要是為了測試,請在自己項目中重新封裝下,以便於程序的擴展。

 

  

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM