直接用net/http启动服务易丢请求,因默认配置缺乏超时控制(read/writetimeout为0)、无连接数限制、无并发限流,导致goroutine堆积、连接队列满或内核丢包;需显式设超时、maxconns限流、异步短信队列及固定worker池。

为什么直接用 net/http 启动服务容易在高并发下丢请求
Go 的 http.Server 默认配置对突发流量很敏感:超时设置不合理、连接队列满、没有限流,会导致验证码请求排队甚至被内核丢弃。真实场景中,短时 500+ QPS 就可能触发 connection reset by peer 或大量 502 Bad Gateway(如果前面挂了 Nginx)。
-
ReadTimeout和WriteTimeout必须显式设为 5–8 秒,否则默认 0(无限),连接卡住会耗尽 goroutine -
MaxConns(Go 1.19+)或配合net.Listener做连接数硬限制,防止夯住整个服务 - 别依赖默认的
http.DefaultServeMux,用chi或gorilla/mux做路由,方便后续加中间件(如日志、鉴权)
短信发送逻辑必须异步化,但不是简单扔给 go func()
同步调用第三方短信接口(比如阿里云 SendSms)会阻塞 HTTP handler,QPS 直接掉到个位数。但盲目用 go sendSMS(...) 也不行——没缓冲的 goroutine 泄漏、panic 没 recover、失败无重试。
- 用带缓冲的 channel(比如
make(chan *SmsReq, 1000))做任务队列,避免瞬间洪峰压垮内存 - 启动固定数量 worker(如 4–8 个),每个 worker 循环读 channel 并调用 SDK;worker 内部必须
recover(),否则 panic 会杀死整个 goroutine - 失败任务要进重试队列(建议最多 2 次),用
time.AfterFunc或简单 sleep 实现,别用复杂调度器
验证码存储选 Redis 还是本地内存?看你的部署规模
单机部署且 QPS sync.Map 加 TTL 定时清理足够快又省资源;但上 K8s 多副本后,sync.Map 就失效了——用户请求可能打到不同实例,校验必然失败。
Go语言(Golang)1.26.0版本提供 Go 官方 Windows amd64 MSI 安装包下载入口,版本号 1.26.0,可用于旧项目维护、兼容性测试和指定版本开发环境配置。
- 生产环境一律走 Redis:用
SET key code EX 300 NX命令,原子写入 + 过期 + 防覆盖,比先GET再SET安全得多 - Redis key 设计要带业务前缀和手机号哈希,比如
sms:verify:sha256(138****1234),防 key 冲突和扫描遍历 - 别用
redis.StringMap存结构体,就存纯字符串验证码,解耦、快、兼容老版本 SDK
怎么让发码接口扛住每秒 2000 次请求而不崩
光靠 Go runtime 不够,得从入口层压住流量。HTTP 层做令牌桶限流,比靠下游短信服务商的频控更可控——他们报错太晚,你已经积压了一堆未处理请求。
- 用
golang.org/x/time/rate.Limiter,每秒 2000 token,burst 设为 500,平滑放行突增流量 - 限流放在 middleware 里,对
/api/v1/send单独生效,别全局限流影响健康检查等路径 - 返回码必须区分:限流返回
429 Too Many Requests,短信渠道失败返回503 Service Unavailable,别全塞500
真正难的是短信通道稳定性——哪怕你的 Go 服务再稳,运营商网关抖动、签名审核不通过、余额不足,都会导致验证码发不出。这些状态得暴露出来,而不是静默失败。
golang免费学习笔记(深入):立即使用
在学习笔记中,你将探索golang的核心概念和高级技巧!










