必须用 gomail.v2 封装邮件发送,禁用 net/smtp;授权码、超时控制、模板解耦、错误重试四者缺一不可,否则上线即故障。

直接用 gomail.v2 封装,别碰 net/smtp 手动拼 MIME;授权码、超时、模板、错误重试这四点漏一个,上线就炸。
为什么不用 net/smtp 直接发
它只管“连上并扔数据”,不处理 MIME 结构、编码、附件边界、HTML/文本双版本这些实际必选项。你写 50 行代码发个带中文标题+HTML 正文+PDF 的邮件,net/smtp.SendMail 会静默失败或收件端显示为空白——因为没设 Content-Type、没做 quoted-printable 编码、没生成合法 boundary。
而 gomail.v2 内置这些:自动选 text/plain 或 text/html,附件用 m.Attach(),中文标题走 mime.WordEncoder,连 TLS 都默认启用。
- 发纯文本?
m.SetBody("text/plain", "hi")一行搞定 - 发 HTML + 文本双版本?
m.Embed()和m.SetBody()配合即可 - 发带附件的订单 PDF?
m.Attach("/tmp/order.pdf"),不用管Content-Transfer-Encoding
授权码必须填对,密码字段不是密码
QQ 邮箱、163、Outlook 等全部停用账户密码直登 SMTP。填错就会报 auth: 535 5.7.8 Authentication failed,不是网络问题,是认证被拒。
Go语言(Golang)1.26.0版本提供 Go 官方 Windows amd64 MSI 安装包下载入口,版本号 1.26.0,可用于旧项目维护、兼容性测试和指定版本开发环境配置。
- QQ 邮箱:网页版 → 设置 → 账户 → “POP3/IMAP/SMTP/Exchange 服务” → 开启 → 获取 16 位“SMTP 专用授权码”
- 163 邮箱:设置 → POP3/SMTP/IMAP → 开启 SMTP → “客户端授权码”(非登录密码)
- 代码里第三个参数必须是这个授权码:
gomail.NewDialer("smtp.qq.com", 587, "user@qq.com", "xxxxxx...") - 端口别硬记:QQ/163 用
587(STARTTLS),别用465(SSL)——gomail默认走 STARTTLS,填 465 会连不上
必须加超时和上下文控制
SMTP 连接卡住、服务器无响应、DNS 解析慢……这些都会让 goroutine 永久阻塞,拖垮整个定时任务系统。不能依赖 gomail 默认行为。
- 用
gomail.Dialer替代gomail.NewDialer,手动控制net.DialTimeout和tls.Config - 给
d.DialAndSend()加 context:ctx, cancel := context.WithTimeout(context.Background(), 25*time.Second),再传给d.DialAndSend(m.WithContext(ctx)) - 失败后别立刻重试:指数退避,最多 2 次,间隔从 1s → 3s,避免触发邮箱服务商限流(QQ 邮箱 1 小时限 50 封)
模板和动态内容要分层解耦
把 HTML 模板硬编码在 m.SetBody() 里,等于把业务逻辑焊死在通知层。改个文案就得发版,出问题没法灰度。
- 模板文件放
templates/email/welcome.html,用html/template渲染,支持{{.UserName}}、{{.OrderID}} - 数据组装独立成函数:
func buildWelcomeData(userID int) (map[string]interface{}, error),查 DB 或调接口都封装在里面 - 发送前校验关键字段:
len(to) > 0、len(subject) 、<code>bodySize (防附件过大被拒) - 失败必须落库:
INSERT INTO email_failures (to, subject, error, created_at) VALUES (?, ?, ?, ?),否则丢信不可查
最易被忽略的是:模板渲染错误(比如 {{.User.Name}} 但传入的是 nil)不会导致 gomail 报错,只会发出去一个空白邮件。务必在 template.Execute 后检查返回值。
golang免费学习笔记(深入):立即使用
在学习笔记中,你将探索golang的核心概念和高级技巧!










