1. 前言html
互聯網發展到如今,郵件服務已經成爲互聯網企業中必備功能之一,應用場景很是普遍,比較常見的有:用戶註冊、忘記密碼、監控提醒、企業營銷等。前端
大多數互聯網企業都會將郵件發送抽取爲一個獨立的微服務,對外提供接口來支持各類類型的郵件發送。程序員
中國的第一封電子郵件spring
1987 年 9 月 14 日中國第一封電子郵件是由「德國互聯網之父」維納·措恩與王運豐在當時的兵器工業部下屬單位—計算機應用技術研究所(簡稱 ICA)發往德國卡爾斯魯厄大學的,其內容爲德文和英文雙語,第一段大意以下:數組
原文:「 Across the Great Wall we can reach every corner in the world. 」瀏覽器
中文大意:「 越過長城,咱們能夠到達世界的每個角落。 」性能優化
這是中國經過北京與德國卡爾斯魯厄大學之間的網絡鏈接,發出的第一封電子郵件。如今看這封郵件內容,頗具深意!服務器
2. 郵件協議網絡
發送郵件的本質是將一我的的信息傳輸給另一我的,那麼如何傳輸就須要商量好標準,這些標準就是協議。最初只有兩個協議:架構
· SMTP 協議
SMTP 的全稱是 「Simple Mail Transfer Protocol」,即簡單郵件傳輸協議。它是一組用於從源地址到目的地址傳輸郵件的規範,經過它來控制郵件的中轉方式。它的一個重要特色是它可以在傳送中接力傳送郵件,即郵件能夠經過不一樣網絡上的主機接力式傳送。
SMTP 認證,簡單地說就是要求必須在提供了帳戶名和密碼以後才能夠登陸 SMTP 服務器,這就使得那些垃圾郵件的散播者無可乘之機。增長 SMTP 認證的目的是爲了使用戶避免受到垃圾郵件的侵擾。SMTP主要負責底層的郵件系統如何將郵件從一臺機器傳至另一臺機器。
· POP3 協議
POP3 是 Post Office Protocol 3 的簡稱,即郵局協議的第3個版本,它規定怎樣將我的計算機鏈接到 Internet 的郵件服務器和下載電子郵件的電子協議。
它是因特網電子郵件的第一個離線協議標準,POP3 容許用戶從服務器上把郵件存儲到本地主機(即本身的計算機)上,同時刪除保存在郵件服務器上的郵件。
POP 協議支持「離線」郵件處理。其具體過程是:郵件發送到服務器上,電子郵件客戶端調用郵件客戶機程序以鏈接服務器,並下載全部未閱讀的電子郵件。
這種離線訪問模式是一種存儲轉發服務,將郵件從郵件服務器端送到我的終端機器上,通常是 PC 機或 MAC。
一旦郵件發送到 PC 機或 MAC上,郵件服務器上的郵件將會被刪除。但目前的 POP3 郵件服務器大均可以「只下載郵件,服務器端並不刪除」,也就是改進的 POP3 協議。
SMTP 和 POP3 是最初的兩個協議,隨着郵件的不斷髮展後來又增長了兩個協議:
· IMAP 協議
全稱 Internet Mail Access Protocol(交互式郵件存取協議),IMAP 是斯坦福大學在 1986 年開發的研發的一種郵件獲取協議,即交互式郵件存取協議,它是跟 POP3 相似郵件訪問標準協議之一。
不一樣的是,開啓了 IMAP 後,在電子郵件客戶端收取的郵件仍然保留在服務器上,同時在客戶端上的操做都會反饋到服務器上,如:刪除郵件,標記已讀等,服務器上的郵件也會作相應的動做。
因此不管從瀏覽器登陸郵箱或者客戶端軟件登陸郵箱,看到的郵件以及狀態都是一致的。
IMAP 的一個與 POP3 的區別是:IMAP 它只下載郵件的主題,並非把全部的郵件內容都下載下來,而是你郵箱當中還保留着郵件的副本,沒有把你原郵箱中的郵件刪除,你用郵件客戶軟件閱讀郵件時才下載郵件的內容。
較好支持這兩種協議的郵件客戶端有:Foxmail、Outlook 等。
· Mime 協議
因爲 SMTP 這個協議開始是基於純 ASCⅡ文本的,在二進制文件上處理得並很差。後來開發了用來編碼二進制文件的標準,如 MIME,以使其經過 SMTP 來傳輸。
今天,大多數 SMTP 服務器都支持 8 位 MIME 擴展,它使二進制文件的傳輸變得幾乎和純文本同樣簡單。
用一張圖來看發送郵件過程當中的協議使用:
實線表明 neo@126.com 發送郵件給 itclub@aa.com;
虛線表明 itclub@aa.com 發送郵件給 neo@126.com
3. 郵件發送流程
發信人在用戶代理上編輯郵件,並寫清楚收件人的郵箱地址;
用戶代理根據發信人編輯的信息,生成一封符合郵件格式的郵件;
用戶代理把郵件發送到發信人的郵件服務器上,郵件服務器上面有一個緩衝隊列,發送到郵件服務器上面的郵件都會加入到緩衝隊列中,等待郵件服務器上的 SMTP 客戶端進行發送;
發信人的郵件服務器使用 SMTP 協議把這封郵件發送到收件人的郵件服務器上
收件人的郵件服務器收到郵件後,把這封郵件放到收件人在這個服務器上的信箱中;
收件人使用用戶代理來收取郵件。首先用戶代理使用 POP3 協議來鏈接收件人所在的郵件服務器,身份驗證成功後,用戶代理就能夠把郵件服務器上面的收件人郵箱裏面的郵件讀取出來,並展現給收件人。
這就是郵件發送的一個完整流程。
4. 簡單使用
最先期的時候使用 JavaMail 相關 API 來開發,須要本身去封裝消息體,代碼量比較龐大;
後來 Spring 推出了 JavaMailSender 簡化了郵件發送過程,JavaMailSender 提供了強大的郵件發送功能,可支持各類類型的郵件發送。
如今 Spring Boot 在 JavaMailSender 的基礎上又進行了封裝,就有了如今的 spring-boot-starter-mail,讓郵件發送流程更加簡潔和完善。
下面給你們介紹如何使用 Spring Boot 發送郵件。
<1> pom 包配置
引入加 spring-boot-starter-mail 依賴包:
<2> 配置文件
在 application.properties 中添加郵箱配置,不一樣的郵箱參數稍有不一樣,下面列舉幾個經常使用郵箱配置:
163 郵箱配置:
126 郵箱配置
qq 郵箱配置以下:
注意: 測試時須要將 spring.mail.username 和 spring.mail.password 改爲本身郵箱對應的登陸名和密碼,這裏的密碼不是郵箱的登陸密碼,是開啓 POP3 以後設置的客戶端受權密碼。
這裏以 126 爲郵件舉例,有兩個地方須要郵箱中設置:
開啓 POP3/SMTP 服務、IMAP/SMTP 服務
圖片下方會有 smtp 等相關信息的配置提示。
開通設置客戶端受權密碼
設置客戶端受權密碼通常須要手機驗證碼驗證。
<3> 文本郵件發送
Spring 已經幫咱們內置了 JavaMailSender,直接在項目中引用便可。咱們封裝一個 MailService 類來實現普通的郵件發送方法。
文本郵件抄送使用:message.copyTo(copyTo) 來實現。
from,即爲郵件發送者,通常設置在配置文件中
to,郵件接收者,此參數能夠爲數組,同時發送多人
subject,郵件主題
content,郵件的主體
郵件發送者 from 通常採用固定的形式寫到配置文件中。
<4> 編寫 test 類進行測試
稍微等待幾秒,就能夠在郵箱中找到此郵件內容了。至此一個簡單的文本郵件發送就完成了。
5. 富文本郵件
在平常使用的過程當中,咱們一般在郵件中加入圖片或者附件來豐富郵件的內容,下面講介紹如何使用 Spring Boot 來發送富文本郵件。
發送 HTML 格式郵件
郵件發送支持以 HTML 語法去構建自定義的郵件格式,Spring Boot 支持使用 HTML 發送郵件。
咱們在 MailService 中添加支持 HTML 郵件發送的方法.
富文本郵件抄送使用:helper.addCc(cc) 來實現。
和文本郵件發送代碼對比,富文本郵件發送使用 MimeMessageHelper 類。MimeMessageHelper 支持發送複雜郵件模板,支持文本、附件、HTML、圖片等,接下來會一一使用到。
在測試類中構建 HTML 內容,測試發送
郵件內容大寫了一段話,下面爲接收到的效果:
由此咱們發現發送 HTML 郵件,就是須要拼接一段 HTML 的 String 字符串交給 MimeMessageHelper 來處理,最後由郵件客戶端負責渲染顯示內容。
發送帶附件的郵件
在 MailService 添加 sendAttachmentsMail 方法,發送帶附件的郵件主要是使用 FileSystemResource 對文件進行封裝,在添加到 MimeMessageHelper 中。
添加多個附件可使用多條 helper.addAttachment(fileName, file)
在測試類中添加測試方法
附件能夠是圖片、壓縮包、Word 等任何文件,可是郵件廠商通常都會對附件大小有限制,太大的附件建議使用網盤上傳後,在郵件中給出連接。
效果圖以下:
發送帶靜態資源的郵件
郵件中的靜態資源通常指圖片,在 MailService 添加 sendInlineResourceMail 方法。
在測試類中添加測試方法
添加多個圖片可使用多條 <img src='cid:" + rscId + "' > 和helper.addInline(rscId, res) 來實現
6. 郵件系統
若是隻是想在系統中作一個郵件工具類的話,以上的內容基本就能夠知足要求了。要作成一個郵件系統的話還須要考慮如下幾方面:
對外提供發送郵件的服務接口
固定格式郵件是否考慮使用模板
發送郵件時出現網絡錯誤,是否考慮適當的重試機制
郵件系統是否考慮異步化,提高服務響應時間
是否開發郵件後臺管理系統,開發出對應的管理軟件,經過頁面發送郵件,統計發送郵件成功率等數據。
常見異常處理措施
6.1 對外提供接口
做爲一個獨立的郵件系統,須要對外提供接口調用,咱們以簡單文本郵件爲例作個演示:
首先須要定義個實例返回對象:
默認成功的返回碼爲:00,返回消息爲:發送成功。
建立一個 MailController 類對外提供 HTTP 請求接口。
外部請求過來時首先進行參數校驗,若是參數有誤返回請求;發送郵件出現異常時返回錯誤,正常狀況下返回 00;
注意在 Service 層若是對異常信息進行了捕獲的話,須要將異常信息拋到上層。
相似上述代碼。
按照這個思路也能夠提供發送帶圖片、帶附件的郵件,同時也能夠封裝發送多人郵件,羣發郵件等複雜狀況。
6.2 郵件模板
一般咱們使用郵件發送服務的時候,都會有一些固定的場景,好比重置密碼、註冊確認等,給每一個用戶發送的內容可能只有小部分是變化的。
因此,不少時候咱們會使用模板引擎來爲各種郵件設置成模板,這樣咱們只須要在發送時去替換變化部分的參數便可。
咱們會常常收到這樣的郵件:
尊敬的 neo 用戶:
恭喜您註冊成爲 xxx 網的用戶,同時感謝您對 xxx 的關注與支持並歡迎您使用 xx 的產品與服務。
...
郵件正文只有 neo 這個用戶名在變化,郵件其它內容均不變,若是每次發送郵件都需拼接 HTML 代碼,程序不夠優雅,而且每次郵件正文有變化都需修改代碼很是不方便。
所以對於這類郵件,都建議作成郵件模板來處理,模板的本質很簡單,就是在模板中替換變化的參數,轉換爲 HTML 字符串便可,這裏以 Thymeleaf 爲例來演示。
Thymeleaf 是 Spring 官方推薦的前端模板引擎,相似 Velocity、FreeMarker 等模板引擎,相較與其餘的模板引擎,Thymeleaf 開箱即用的特性。
它提供標準和 Spring 標準兩種方言,能夠直接套用模板實現 JSTL、 OGNL 表達式效果,避免天天套模板、該 Jstl、改標籤的困擾。
Thymeleaf 在有網絡和無網絡的環境下皆可運行,即它可讓美工在瀏覽器查看頁面的靜態效果,也可讓程序員在服務器查看帶數據的動態頁面效果。
下面咱們來演示使用 Thymeleaf 製做郵件模板:
1. 添加依賴包
2. 在 resorces/templates 下建立 emailTemplate.html
emailTemplate.html 文件內容即爲郵件的正文內容模板。
咱們發現上述的模板中只有 id 是一個動態的值,發送過程當中會根據傳入的 id 值來替換連接中的 {id}。
3. 解析模板併發送
咱們發現最後調用的仍是 sendHtmlMail 的方法,郵件模板的做用只是處理 HTML 生成部分,經過 Thymeleaf 模板引擎解析固定的模板,再更具參數來動態替換其中的變量,最後經過前面的 HTML 發送的方法發送郵件。
效果圖以下:
點擊「激活帳號」跳轉的連接爲:http://www.ityouknow.com/register/006
6.3 發送失敗
由於各類緣由,總會有郵件發送失敗的狀況,好比:郵件發送過於頻繁、網絡異常等。在出現這種狀況的時候,咱們通常會考慮從新重試發送郵件,會分爲如下幾個步驟來實現:
接收到發送郵件請求,首先記錄請求而且入庫。
調用郵件發送接口發送郵件,而且將發送結果記錄入庫。
啓動定時系統掃描時間段內,未發送成功而且重試次數小於3次的郵件,進行再次發送.
從新發送郵件的時間,建議以 2 的次方間隔時間,好比:二、四、八、16 ...
常見的錯誤返回碼:
421 HL:ICC 該IP同時併發鏈接數過大,超過了網易的限制,被臨時禁止鏈接。
451 Requested mail action not taken: too much fail authentication 登陸失敗次數過多,被臨時禁止登陸。請檢查密碼與賬號驗證設置
553 authentication is required,密碼配置不正確
554 DT:SPM 發送的郵件內容包含了未被許可的信息,或被系統識別爲垃圾郵件。請檢查是否有用戶發送病毒或者垃圾郵件;
550 Invalid User 請求的用戶不存在
554 MI:STC 發件人當天內累計郵件數量超過限制,當天再也不接受該發件人的投信。
若是使用一個郵箱頻繁發送相同內容郵件,也會被認定爲垃圾郵件,報 554 DT:SPM 錯誤
若是使用網易郵箱能夠查看這裏的提示:企業退信的常見問題?
6.4 異步發送
不少時候郵件發送並非主業務必須關注的結果,好比通知類、提醒類的業務能夠容許延時或者失敗。這個時候能夠採用異步的方式來發送郵件,加快主交易執行速度。
在實際項目中能夠採用消息中間件 MQ 發送郵件,具體作法是建立一個郵件發送的消息隊列,在業務中有須要用到郵件發送功能時,給對應消息隊列按照規定參數發送一條消息,郵件系統監聽此隊列,當有消息過來時,處理郵件發送的邏輯。
6.5 管理後臺
考慮作一個完善的郵件系統,能夠設計一個獨立的郵件管理後臺,不但可讓系統之間調用時使用,也能夠提供圖形化界面讓公司的運營、市場部的同事來發送郵件,查詢郵件的發送進度,統計郵件發送成功率。
也能夠設置一些代碼鉤子,統計用戶點擊固定連接次數,方便公司營銷人員監控郵件營銷轉化率。
一個很是完善的郵件系統須要考慮的因素很是多,好比是否設置白名單、黑名單來作郵件接收人的過濾機制,是否給用戶提供郵件退訂的接口等。
所以在初期郵件發送的基本功能完成以後,再結合公司業務,快速迭代的逐步完善郵件系統,是一個推薦的作法。
在此我向你們推薦一個架構學習交流羣。交流學習羣號:821169538 裏面會分享一些資深架構師錄製的視頻錄像:有Spring,MyBatis,Netty源碼分析,高併發、高性能、分佈式、微服務架構的原理,JVM性能優化、分佈式架構等這些成爲架構師必備的知識體系。還能領取免費的學習資源,目前受益良多。
7. 總結
使用 Spring Boot 集成發送郵件的功能很是簡單,只須要簡單編碼就能夠實現發送普通文本郵件、帶附件郵件、HTML 格式郵件、帶圖片郵件等。
若是須要作成一個郵件系統還須要考慮不少因素,好比:郵箱發送失敗重試機制、防止郵件被識別爲垃圾郵件,固定時間內發送郵件的限制等。
在微服務架構中,經常將一些基礎功能下沉下來,做爲獨立的服務來使用,郵件系統做爲平臺的基礎功能,特別適合作爲獨立的微服務來支持整個系統。