Go发短信需封装HTTP客户端防崩丢泄密:设超时、查状态码、显式头、外置密钥、限流重试、异步解耦、接口抽象、渠道适配。

Go 本身不提供短信能力,必须调用第三方 HTTP API(如阿里云、腾讯云、Twilio),关键不在“能不能发”,而在于怎么封装才不崩、不丢、不泄密。
用自定义 http.Client 控制超时和错误分类
裸用 http.Post 或 http.DefaultClient 是最常见崩点:网络卡住会永久阻塞 goroutine,401/502 响应体被当成功解析,415 报错却没设 Content-Type。
- 显式构造 client:
client := &http.Client{Timeout: 10 * time.Second},别依赖默认值 - 必须检查
resp.StatusCode:状态码 ≥ 400 时先读resp.Body,再根据服务商约定字段(如阿里云的"Code")判断是否业务失败 - 网络错误(
context.DeadlineExceeded、net.OpError)可重试;400/401/429/5xx 多数不可重试,需记日志并告警 - 所有请求头必须显式设置:
req.Header.Set("Content-Type", "application/json")
敏感参数必须外置,禁止硬编码或全局变量
AccessKey、Secret、签名模板 ID 写死在代码里等于把门钥匙贴在门上。Git 提交一次,风险就不可逆。
- 优先走环境变量:
os.Getenv("SMS_ACCESS_KEY"),启动时校验非空 - 配置文件方式用
viper加viper.AutomaticEnv(),允许环境变量覆盖 YAML 中的值 - 云厂商 SDK(如 aliyun-go-sdk-core v3+)支持
credentials.NewEnvironmentVariableProvider()自动识别ALIYUN_ACCESS_KEY_ID等标准变量
加限流 + 有节制重试,别靠服务商兜底
高频场景(如注册验证码)不做限流,分分钟触发服务商频控,返回 429 Too Many Requests 却无响应,用户卡在“发送中”。
使用 uber-go/dig 在 Golang 中实现依赖注入——基于反射容器,提供 Provide/Invoke、dig.In/dig.Out 参数与结果对象,支持命名依赖等
立即学习“go语言免费学习笔记(深入)”;
- 用
golang.org/x/time/rate在入口层做令牌桶限流,比如每秒最多 50 次 - 重试只针对网络超时和 5xx:用
time.AfterFunc延迟 1–3 秒后重入队列,别用无限 for 循环 - 别在 handler 里直接调 API——必须异步进带缓冲 channel(如
make(chan Message, 1000)),配固定 worker 消费
渠道工厂注册,避免 if-else 分支爆炸
每加一个新渠道就改一次 if channel == "sms" → else if channel == "dingtalk",半年后没人敢动,一改就发错。
- 定义接口:
type MessageSender interface { Send(ctx context.Context, msg Message) error } - 每个渠道实现独立结构体(
SmsSender、DingTalkSender),工厂函数按配置动态返回:func NewSender(cfg config.ChannelConfig) (MessageSender, error) - 启动时从
app.yaml读取channels.sms.enabled: true,只注册启用的实例 - Token 类型渠道(如企微、钉钉)的 access_token 必须带刷新逻辑,用
sync.RWMutex包裹或走 Redis 缓存+过期时间
最容易被忽略的是不同渠道对消息格式的实际约束:钉钉卡片不认换行符,腾讯云短信限制单条 70 字符且不支持 HTML,这些没法靠统一模板解决,得在各 Send() 实现里做适配。


















