寫在前邊:國內有很多的郵件服務商,尤其幾個大的雲廠商都有非常成熟的郵件推送服務,如果可以的話,建議大家直接使用國內雲廠商的服務。我們公司因為做的國際業務,很多東西都在亞馬遜,所以拗不過我們老大,只能使用 AWS 的 SES;
進入正題,首先我們需要下載亞馬遜的 SDK , 執行 go get ,我這裡用的是 1。43。15 版本(不同版本之間亞馬遜可能會進行細微調整)。
go get github。com/aws/aws-sdk-go@v1。43。15
當然,基本上所有云廠商都支援使用 SMTP 的方式傳送郵件,各大語言均對 SMTP 有較好的支援,本文不介紹SMTP,介紹的是 SES 的模板傳送。
模板傳送的好處有很多,類似於強型別語言,雖然寫起來複雜,但是我們在編寫過程中的錯誤可以更好地暴露出來;其他優點不再贅述。
完整原始碼
先上程式碼,如果程式碼閱讀能力比較強的同學,可以直接拿走開始用了,使用方法比較簡單,將這個程式碼新建一個檔案,初始化一下,在需要發郵件的地方,呼叫即可,後邊會展開對下方程式碼的分析解讀;
tip: 程式碼裡邊的 xlog 是我們自己封裝的一個log包,換成fmt列印或者自己的日誌包
package aws_sesimport ( “context” “encoding/json” “github。com/aws/aws-sdk-go/aws” “github。com/aws/aws-sdk-go/aws/credentials” “github。com/aws/aws-sdk-go/aws/session” “github。com/aws/aws-sdk-go/service/sesv2” “github。com/aws/aws-sdk-go/service/sesv2/sesv2iface” “github。com/microcosm-cc/bluemonday”)const ( AwsRegion = “us-west-2” // 此處填寫你自己的亞馬遜所屬地區 Endpoint = “https://email-smtp。us-west-2。amazonaws。com” // 此處是對應的所處地址 host FeedbackForwardingEmailAddress = “” // 發生錯誤時亞馬遜通知地址,前提是開通了aws配套的SNS 花不了多少錢,強烈建議開啟,否則你發不出來郵件,不知道怎麼回事兒。。。)type AwsSesRepo struct { SesV2BioAPI sesv2iface。SESV2API}func NewAwsSesRepo() *AwsSesRepo { ctx := context。Background() sess, err := session。NewSession(&aws。Config{ CredentialsChainVerboseErrors: nil, Credentials: credentials。NewStaticCredentials(accessKey, accessSecret, “”), Endpoint: nil, EndpointResolver: nil, EnforceShouldRetryCheck: nil, Region: aws。String(AwsRegion), DisableSSL: nil, HTTPClient: nil, LogLevel: nil, Logger: nil, MaxRetries: nil, Retryer: nil, DisableParamValidation: nil, DisableComputeChecksums: nil, S3ForcePathStyle: nil, S3Disable100Continue: nil, S3UseAccelerate: nil, S3DisableContentMD5Validation: nil, S3UseARNRegion: nil, LowerCaseHeaderMaps: nil, EC2MetadataDisableTimeoutOverride: nil, UseDualStackEndpoint: 0, UseFIPSEndpoint: 0, SleepDelay: nil, DisableRestProtocolURICleaning: nil, EnableEndpointDiscovery: nil, DisableEndpointHostPrefix: nil, STSRegionalEndpoint: 0, S3UsEast1RegionalEndpoint: 0, }) if err != nil { xlog。Error(ctx, “初始化亞馬遜 session 出錯”, “err”, err) return nil } svc := sesv2。New(sess) return &AwsSesRepo{ Conf: conf, SesV2BioAPI: svc, }}type SendEmailWithCtxParams struct { FromEmail string TemplateName string ToAddress []string BccAddress []string CcAddress []string TemplateData map[string]string}// 傳送郵件。func (r *AwsSesRepo) SendEmailWithContext(ctx context。Context, p *SendEmailWithCtxParams) (*sesv2。SendEmailOutput, error) { // 需要將結構體的內容json一下傳遞給發信介面 templateDataStr, err := json。Marshal(p。TemplateData) if err != nil { return nil, err } // 如果沒有設定from,建議在這個地方給個預設值 if len(p。FromEmail) == 0 { p。FromEmail = “demo@toutiao。com” //預設值 } // 呼叫 SDK 傳送郵件 resp, err := r。SesV2BioAPI。SendEmailWithContext(ctx, &sesv2。SendEmailInput{ ConfigurationSetName: aws。String(“default”), // 設定集 後邊有詳解 Content: &sesv2。EmailContent{ Raw: nil, // 原始傳送方式,需要自行拼裝各種引數。 Simple: nil, Template: &sesv2。Template{ // 使用模版方式傳送 TemplateArn: nil, TemplateData: aws。String(string(templateDataStr)), // 模版需要的 value TemplateName: aws。String(p。TemplateName), // 模版的名稱 必須 唯一 }, }, Destination: &sesv2。Destination{ BccAddresses: aws。StringSlice(p。BccAddress), // 密送 CcAddresses: aws。StringSlice(p。CcAddress), // 抄送 ToAddresses: aws。StringSlice(p。ToAddress), // 要發給誰 }, EmailTags: nil, // 郵件的標籤 FeedbackForwardingEmailAddress: nil, FeedbackForwardingEmailAddressIdentityArn: nil, FromEmailAddress: aws。String(p。FromEmail), // 誰發出的 FromEmailAddressIdentityArn: nil, ListManagementOptions: nil, // 取消訂閱時的通知時間tag。 ReplyToAddresses: aws。StringSlice([]string{“”}), // 使用者收到郵件,進行回覆時的郵件地址。 }) return resp, err}// 建立模版。func (r *AwsSesRepo) CreateEmailTemplateWithContext(ctx context。Context, templateName, title, content string) (*sesv2。CreateEmailTemplateOutput, error) { p := bluemonday。UGCPolicy() outPut, err := r。SesV2BioAPI。CreateEmailTemplateWithContext(ctx, &sesv2。CreateEmailTemplateInput{ TemplateContent: &sesv2。EmailTemplateContent{ Html: aws。String(content), Subject: aws。String(title), Text: aws。String(p。Sanitize(content)), }, TemplateName: aws。String(templateName), }) return outPut, err}// 獲取單個郵件模版。func (r *AwsSesRepo) GetEmailTemplateWithContext(ctx context。Context, templateName string) (*sesv2。GetEmailTemplateOutput, error) { info, err := r。SesV2BioAPI。GetEmailTemplateWithContext(ctx, &sesv2。GetEmailTemplateInput{ TemplateName: aws。String(templateName), }) if err != nil { xlog。Error(ctx, “GetEmailTemplateWithContext ”, “err”, err) return nil, err } return info, nil}// 修改模版func (r *AwsSesRepo) UpdateEmailTemplateWithContext(ctx context。Context, templateName, html, text, title string) (*sesv2。UpdateEmailTemplateOutput, error) { data, err := r。SesV2BioAPI。UpdateEmailTemplateWithContext(ctx, &sesv2。UpdateEmailTemplateInput{ TemplateContent: &sesv2。EmailTemplateContent{ Html: aws。String(html), Subject: aws。String(title), Text: aws。String(text), }, TemplateName: aws。String(templateName), }) if err != nil { xlog。Error(ctx, “UpdateEmailTemplateWithContext ”, “err”, err) return nil, err } return data, nil}// 批次獲取模版列表func (r *AwsSesRepo) ListEmailTemplatesWithContext(ctx context。Context, pageSize int64, cursor string) (*sesv2。ListEmailTemplatesOutput, error) { data, err := r。SesV2BioAPI。ListEmailTemplatesWithContext(ctx, &sesv2。ListEmailTemplatesInput{ NextToken: aws。String(cursor), PageSize: aws。Int64(pageSize), }) if err != nil { xlog。Error(ctx, “ListEmailTemplatesWithContext ”, “err”, err) return nil, err } return data, nil}
如何使用:
func main(){ sesRepo := NewAwsSesRepo() sesRepo。SendEmailWithContext(。。。。。)}
可以看到我只是對接了幾個簡單的介面
傳送郵件SendEmailWithContext建立模版CreateEmailTemplateWithContext獲取單個模版資訊GetEmailTemplateWithContext更新模版UpdateEmailTemplateWithContext批次獲取模版列表ListEmailTemplatesWithContext
其他的介面大家可以舉一反三,在自己實際業務情況下進行擴充,亞馬遜的 Golang SDK 提供了非常全面的操作介面,全都封裝在這個檔案中:
。。。mod/github。com/aws/aws-sdk-go@v1。43。15/service/sesv2/sesv2iface/interface。go
如圖所示:
亞馬遜Ses API 介面
詳解步驟
現在我們來逐步拆解,講解每一步引數都是從哪裡拿到的。核心的
關鍵引數
都會用
黃色背景加粗
展示。
登入亞馬遜賬號:
https://signin。aws。amazon。com/signin
在亞馬遜中開通 SES 服務 (強烈建議同時開通 SNS 服務,當我們傳送一個郵件不成功,尤其在除錯階段,希望知道失敗原因,比如模版引數不對,對方拒收等等,SES傳送完之後,會透過 SNS 進行回撥通知)簡單地說 SES 是發郵件的, SNS是郵件傳送之後的訊息佇列,用來推送郵件傳送過程中的任何階段結果。
Aws ses 控制檯
AwsRegion:
開通完畢,我們要看一下我們開通的服務是屬於哪個區,如圖所示,這個就是我們在程式碼中要用到的 AwsRegion
Endpoint(模版傳送不需要):
指的是你要將請求傳送到aws的哪個叢集, SMTP 使用的必要引數,
模版傳送不需要
,與awsRegin 配合使用的,位置如下圖
Verified identities:
身份驗證,這個是指我們要給使用者發郵件的時候,已什麼身份發,比如發給 to address: zhangsan艾特126。com, From 的郵箱身份,只有在這裡配置過的,才可以放到From 裡
點選右上角的 create 按照步驟一步步來即可。
Configuration sets:
配置集,直接用預設的 default 即可,在上方我提供的原始碼中,大家可以看到有填寫配置集合的,我們可以設定多套配置,來分發不同的業務線郵件;
Configuration sets
這裡有一個比較重要的概念 Event destinations 這個是關聯你的SNS事件的,傳送成功,失敗,被拒收等等事件都可以在這裡設定:
Event destinations
重要 Email Templates
這裡就是核心了,不過你不能在後臺直接設定,需要透過介面呼叫,增刪改查模版的資訊。
aws 模版設定
上方的程式碼裡已經封裝了模版的增改查。
Suppression list
禁止列表 瞭解即可:
這個列表裡記錄的是投遞之後被使用者或者使用者郵件服務商彈回的郵箱地址,為了提高郵件的送達率,建議開啟,保持自己的“發信信譽”很重要,超量的彈回郵件,會造成你的賬號信譽降低。支援手動單個、批次新增。
Cross-account notifications & Email receiving 可
忽略
Cross-account notifications
跨賬號通知是V1版本的功能,舊版做支援時使用;新版用的是 SNS 通知,aws 正在棄用中;不建議使用這個功能。
Email receiving 使用者設定使用者回信時的相關設定;推薦直接在發信時
ReplyToAddresses
填寫這個 Slice,這樣使用者回信會直接投遞到你指定的回覆郵箱。
上邊就是整個 SES 的配置了;
還有一個非常重要的東西 —— 身份驗證: accessKey Secret
Credentials: credentials。NewStaticCredentials(accessKey, accessSecret, “”),
如下圖所示,點選個人資訊 安全憑證,建立憑證,即可拿到屬於自己的金鑰對,注意保管,只顯示一次。
SES郵件傳送原理:
配額是你最應該注意到的坑
詳細文件: https://docs。aws。amazon。com/ses/latest/dg/quotas。html
每個郵件加上附件 base64 編碼之後不能大於10M
每一封郵件的最大收件人不能超過
50
人,包含(收件人、抄送、密件抄送)
每個 區域 中最大可以建立 10k 個郵件模板。
每個 模版 不能超過 500kb
每個 模版可以有 [無限個] 可以替換的變數位
Ses API 的呼叫頻率被限制在了 每秒1個,所以要做好速率控制。所有操作(除了
SendEmail
、
SendRawEmail
和
SendTemplatedEmail
)都被限制為每秒一個請求。喜歡開協程的同學注意下,踩著點兒剎車…
參考資料:
這裡的文件是幫助大家更加深入瞭解 SES ,以及發生錯誤時的排查方法。
亞馬遜官方SES 文件:https://docs。aws。amazon。com/zh_cn/ses/latest/dg/send-email。html
常見錯誤碼列表:https://docs。aws。amazon。com/ses/latest/APIReference/CommonErrors。html
SMTP 常見錯誤碼: https://docs。aws。amazon。com/zh_cn/ses/latest/dg/troubleshoot-smtp。html
如果大家在使用中遇到問題,歡迎評論提問,我會在第一時間答覆,祝大家開發順利~
本文首發於: 今日頭條 —— 全棧社畜小吉 2022-09-27 00:51:25