• golang go-simple-mail+fasttemplate+mailhog 发送邮件


    一个很简单的需求,就是基于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{"dalongdemo@qq.com"}...).
            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("dalong@qq.com", "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{
                "to1@example1.com",
                "to3@example2.com",
                "to4@example3.com",
            } {
                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

    说明

    能复用连接,肯定很不错,但是也得慎用,同时碰到问题应该多看日志,分析下原因,对于网络的应用,抓包也是一个很不错的选择(如果需要到那一步的时候)

    参考资料

    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

  • 相关阅读:
    BZOJ 2154 Crash的数字表格 【莫比乌斯反演】
    BZOJ 3529 [Sdoi2014]数表 【莫比乌斯反演】
    BZOJ 2820 YY的GCD 【莫比乌斯反演】
    BZOJ 2440 [中山市选2011]完全平方数 【莫比乌斯反演】
    [BalticOI 2004] Sequence
    AtCoder [ARC070E] NarrowRectangles
    AtCoder [AGC022E] Median Replace
    AtCoder [ARC101E] Ribbons on Tree
    CF107D Crime Management
    Loj 6497「雅礼集训 2018 Day1」图
  • 原文地址:https://www.cnblogs.com/rongfengliang/p/14018564.html
Copyright © 2020-2023  润新知