golang go-simple-mail+fasttemplate+mailhog 傳送郵件
阿新 • • 發佈:2020-11-22
一個很簡單的需求,就是基於golang 的模版傳送郵件,同時為了提高效能,希望複用smtp的連線,以下是
一個基於開源庫實現的學習,同時包含了一些使用中問題的說明
依賴的庫
為了簡化配置以及提高效能,使用了fasttemplate 進行模版處理,go-simple-mail 進行email傳送,yaml進行配置管理
Mailhog 作為測試的smtp伺服器(方便測試)
環境準備
- go mod
module demoapp
go 1.15
require (
github.com/valyala/fasttemplate v1.2.1
github.com/xhit/go-simple-mail/v2 v2.5.1
gopkg.in/yaml.v2 v2.3.0
)
- 專案結構
├── README.md
├── config
│ └── config.go
├── config.yaml
├── docker-compose.yaml
├── go.mod
├── go.sum
├── main.go
├── notify
│ ├── email.go
│ └── notidy.go
├── pprof
│ ├── README.md
│ ├── app.sh
│ ├── profile001.pb.gz
│ └── trace.log
└── templates
└── email.html
- 程式碼說明
yaml 配置解析config/config.go
package config
import (
"io/ioutil"
"log"
"gopkg.in/yaml.v2"
)
// Config cmp sync config
type Config struct {
Email struct {
ServerHost string `yaml:"serverhost"`
ServerPort int `yaml:"serverport"`
FromEmail string `yaml:"fromemail"`
FromPasswd string `yaml:"from_passwd"`
} `yaml:"email"`
Template struct {
EmailTemplate string `yaml:"email"`
} `yaml:"template"`
}
// Default config path is local with name config.yaml
// New get sync config
func New() Config {
config := Config{}
bytes, err := ioutil.ReadFile("config.yaml")
log.Printf("%s", bytes)
if err != nil {
log.Fatalln("read config error: ", err.Error())
}
err = yaml.Unmarshal(bytes, &config)
if err != nil {
log.Fatalln("Unmarshal config error: ", err.Error())
}
return config
}
email.go 核心傳送
package notify
import (
"demoapp/config"
"io/ioutil"
"log"
"time"
"github.com/valyala/fasttemplate"
mail "github.com/xhit/go-simple-mail/v2"
)
// EmailNotidy is a email notify
type EmailNotidy struct {
config config.Config
smtpClient *mail.SMTPClient
templateCache map[string]string
}
// NewEailNotidy NewEailNotidy instance
func NewEailNotidy() *EmailNotidy {
config := config.New()
server := mail.NewSMTPClient()
// SMTP Server
server.Host = config.Email.ServerHost
server.Port = config.Email.ServerPort
server.Username = config.Email.FromEmail
server.Password = config.Email.FromPasswd
server.Encryption = mail.EncryptionNone
// Since v2.3.0 you can specified authentication type:
// - PLAIN (default)
// - LOGIN
// - CRAM-MD5
server.Authentication = mail.AuthPlain
// Variable to keep alive connection
// 實現smtp 連線的複用,注意有坑
server.KeepAlive = true
server.ConnectTimeout = 10 * time.Second
server.SendTimeout = 10 * time.Second
smtpClient, err := server.Connect()
if err != nil {
log.Fatalf("init mail instance error:%s", err.Error())
}
bytes, err := ioutil.ReadFile(config.Template.EmailTemplate)
if err != nil {
log.Fatalf("init mail instance error:%s", err.Error())
}
return &EmailNotidy{
config: config,
smtpClient: smtpClient,
templateCache: map[string]string{
config.Template.EmailTemplate: string(bytes),
},
}
}
// Send Send
func (e *EmailNotidy) Send(to string, subject string, datafiles map[string]interface{}) error {
// 使用fasttemplate 進行內容替換處理,使用了cache
t := fasttemplate.New(e.templateCache[e.config.Template.EmailTemplate], "{{", "}}")
htmlBody := t.ExecuteString(datafiles)
email := mail.NewMSG()
from := e.config.Email.FromEmail
email.SetFrom(from).
AddTo(to).
AddCc([]string{"[email protected]"}
SetSubject(subject)
email.SetBody(mail.TextHTML, htmlBody)
err := email.Send(e.smtpClient)
if err != nil {
return err
}
return nil
}
main.go 入口
package main
import (
"demoapp/notify"
"log"
"net/http"
_ "net/http/pprof"
"sync"
)
func main() {
emailnotidy := notify.NewEailNotidy()
// not working tcp out of order
// 不能工作,因為複用連線,傳送的資料包不一致
http.HandleFunc("/send", func(res http.ResponseWriter, req *http.Request) {
res.Write([]byte("send email"))
wg := sync.WaitGroup{}
wg.Add(2)
for i := 0; i < 2; i++ {
go func(wg *sync.WaitGroup) {
defer wg.Done()
err := emailnotidy.Send("[email protected]", "demoapp", map[string]interface{}{
"content": "dalongdemoapp",
})
if err != nil {
log.Println("err:", err.Error())
}
}(&wg)
}
wg.Wait()
})
// 推薦使用次方法進行連線複用以及多人傳送
http.HandleFunc("/send2", func(res http.ResponseWriter, req *http.Request) {
res.Write([]byte("send email"))
for _, to := range []string{
"[email protected]",
"[email protected]",
"[email protected]",
} {
err := emailnotidy.Send(to, "demoapp", map[string]interface{}{
"content": "dalongdemoapp",
})
if err != nil {
log.Println("err:", err.Error())
}
}
})
http.ListenAndServe(":9090", nil)
}
執行效果
- 命令
go run main.go
- 效果
可以複用的傳送 http://localhost:9090/send2
異常的send smtp 請求日誌
說明
能複用連線,肯定很不錯,但是也得慎用,同時碰到問題應該多看日誌,分析下原因,對於網路的應用,抓包也是一個很不錯的選擇(如果需要到那一步的時候)
參考資料
https://github.com/rongfengliang/golang-email-learning
https://github.com/xhit/go-simple-mail
https://github.com/mailhog/MailHog
https://github.com/valyala/fasttemplate
https://github.com/go-yaml/yaml