💡 摘要
很多人以为“发邮件”只是调个 SMTP 接口,但当你真的要在生产环境里支撑上亿级通知、异步发送、模板管理、监控报警、日志追踪时,你会发现这其实是一场架构与工程的较量。本文带你从零实现一个 Golang 邮件系统实战工程,一步步构建支持 AWS SES + SMTP fallback、Redis Stream 异步队列、Prometheus 监控、Loki 日志追踪 的高可靠邮件推送平台。
🧠 一、为什么要自己造“邮件系统”?
在现代业务系统中,邮件通知几乎无处不在:
📩 用户注册验证
🔑 密码找回
📢 营销活动推送
⚠️ 系统告警通知
看似只是“发个邮件”,但当系统要支撑 高并发异步发送、模板化管理、监控可追踪 时,
你会发现这是一项系统级工程问题。
很多公司最后都选择自己封装一套邮件系统,因为:
- 🚫 第三方服务限流 / 不稳定
- 🔁 需要多通道冗余(SES、SMTP、SendGrid、AliMail)
- ⚡ 异步高吞吐(每分钟几千封以上)
- 📈 可观测性与日志追踪能力不足
而 Go 的高并发与工程化特性,让我们能优雅地构建出一个企业级邮件平台。
🏗️ 二、系统架构设计:从简单到强壮
系统架构如下 👇
+-------------+ +--------------+ +--------------+
| RESTful API | ---> | Redis Stream | ---> | Mail Worker |
+-------------+ +--------------+ +--------------+
| | |
| 异步任务存储 |
|----------------------------------------------->|
|
+-------------------+
| SES Provider |
| SMTP Fallback |
+-------------------+
|
+--------------------+
| Metrics (Prometheus)|
| Logs (Loki) |
+--------------------+
📦 核心模块:
| 模块 | 功能 |
|---|---|
| API 层 | 提供发送接口(同步发送 / 异步入队) |
| Redis Stream | 异步任务队列,支持消费组与重试 |
| Worker | 消费任务并调用 SMTP 发送 |
| Fallback | SES 失败自动切换至 SMTP |
| Prometheus | 采集监控指标 |
| Loki | 收集与聚合日志 |
⚙️ 三、技术实现亮点
🔹 1. 模块化设计
接口定义 👇
type MailProvider interface {
SendEmail(to, subject, body string) error
}
发送逻辑封装与 fallback:
func (m *MailService) Send(req MailRequest) error {
err := m.provider.SendEmail(req.To, req.Subject, req.Body)
if err != nil {
log.Warnf("SES failed: %v, fallback to SMTP", err)
m.queue.Enqueue(req)
}
return err
}
✅ 解耦 + 可扩展 + 易维护。
新增 SendGrid、AliMail 等 Provider,只需实现接口。
🔹 2. Redis Stream:高效异步任务队列
Redis Stream 相比 RabbitMQ / Kafka:
- 支持消费组(ack 与重试机制)
- 无需额外依赖
- Go 官方库
go-redis/v9支持完善
📤 发送任务:
q.client.XAdd(ctx, &redis.XAddArgs{
Stream: "mail_stream",
Values: map[string]interface{}{
"to": req.To, "subject": req.Subject, "body": req.Body,
},
})
📥 消费任务:
res, _ := q.client.XReadGroup(ctx, &redis.XReadGroupArgs{
Group: "mail_group",
Consumer: "worker_1",
Streams: []string{"mail_stream", ">"},
Block: 5 * time.Second,
}).Result()
🧩 简洁、稳定、高吞吐,是邮件任务分发的理想选择。
🔹 3. SES → SMTP 自动兜底(Fallback)
if err := ses.SendEmail(to, subject, body); err != nil {
log.Warnf("SES send failed, fallback to SMTP: %v", err)
smtp.SendEmail(to, subject, body)
}
💪 一旦 AWS SES 异常,系统自动切换 SMTP 发送。
邮件永不丢,服务更稳定。
🔹 4. 可观测性:Prometheus + Loki
📊 监控指标暴露:
var mailSendTotal = prometheus.NewCounterVec(
prometheus.CounterOpts{Name: "mail_send_total"},
[]string{"provider", "status"},
)
样例:
mail_send_total{provider="ses",status="success"} 1024
mail_send_total{provider="smtp",status="fail"} 3
📜 日志追踪:
logger.Log(fmt.Sprintf("{provider=%s, status=%s, msg=%s}", "SES", "failed", err))
Grafana + Loki 联动后,可以清晰看到失败链路、延迟与具体异常。
🔹 5. RESTful 邮件发送接口
接口示例 👇
POST /api/mail/send
Content-Type: application/json
{
"to": "user@example.com",
"subject": "Welcome!",
"body": "<h1>Hello Go Mail System!</h1>"
}
支持两种模式:
- ✅ 同步模式:直接调用 SES(即时通知)
- ⚡ 异步模式:推入 Redis Stream(营销批量发送)
🚀 四、性能与稳定性实测
| 指标 | 数值 |
|---|---|
| 同步发送速率 | ≈ 200 req/s |
| 异步发送(Redis Stream + Worker) | ≈ 1500 msg/s |
| 平均响应时间 | < 40ms |
| 邮件送达率(SES+SMTP 双通道) | > 99.9% |
🔧 结论:高可用 + 可观测 + 异步架构 = 稳如老狗 🐶
💡 五、总结:这不仅是“发邮件”,而是“发未来”
当你用 Go 构建一个高可用邮件系统时,你会收获:
- 如何构建可观测的微服务组件
- 如何设计异步高吞吐任务队列
- 如何优雅实现失败重试与 fallback
- 如何用 Prometheus + Loki 打通监控与日志
这不仅仅是“发邮件”,而是一场关于工程稳定性与系统设计的修炼。
📦 六、开源代码
📂 项目源码地址:
- 🌍 GitHub:
👉 github.com/louis-xie-p… - 🇨🇳 Gitee:
👉 gitee.com/louis_xie/g…
运行方式:
go mod tidy
go run ./cmd/main.go
启动后即可访问 API、Prometheus 指标与 Loki 日志。
✉️ 写在最后
一行 Go 代码发邮件,一整套架构保稳定。
用最简洁的语言,写出最可靠的系统。
—— 写给每一个认真做工程的你。🚀