直接用 golang.org/x/time/rate.Limiter,它经生产验证、支持突发、精确计时、无锁稳定;自写易出时钟漂移、溢出、goroutine泄漏等错误。

Go 限流器选 golang.org/x/time/rate 还是自写令牌桶?
直接用 rate.Limiter,别自己实现令牌桶逻辑。它已通过大量生产验证,支持突发请求(AllowN)、精确时间控制(基于 time.Now()),且无锁设计在高并发下更稳。自己手撸容易漏掉时钟漂移、整数溢出、goroutine 泄漏等细节。
常见错误是把 rate.NewLimiter 的 burst 设得太小——比如设成 1,结果连健康检查探针都拦掉了;或者误以为 burst 是“每秒最多放行数”,其实它是“可透支的瞬时容量”,真正速率由第一个参数 limit(rate.Every 或 float64)决定。
-
rate.NewLimiter(rate.Every(100*time.Millisecond), 5)表示:平均 100ms 放一个,但允许最多积压 5 个请求一起过 - HTTP 中间件里调用
limiter.Allow()前,务必先做路径白名单判断(如/healthz绕过限流) - 不要在每个请求里 new 一个
Limiter,它要复用;全局共享或按路由分组初始化
HTTP 层怎么快速识别异常连接(SYN Flood / 连接耗尽)?
Go 的 net/http.Server 本身不暴露连接级指标,没法直接感知 SYN 半开连接。真要防御连接层 DDoS,得靠前置组件:Linux 的 net.ipv4.tcp_syncookies=1、iptables 限速,或用 nginx 做连接数限制(limit_conn)。Go 应用层能做的,是尽早释放无效长连接。
典型场景是攻击者建大量空闲连接却不发请求,吃光 Server.MaxConns。这时必须主动断连:
立即学习“go语言免费学习笔记(深入)”;
- 设置
http.Server.ReadTimeout和WriteTimeout(建议 ≤ 30s),避免连接悬停 - 启用
http.Server.IdleTimeout(推荐 60–90s),强制回收空闲连接 - 若用
fasthttp,可配MaxConnsPerIP直接按 IP 限连,但标准net/http没这功能 - 错误现象:
dmesg | grep "possible SYN flooding"出现,说明内核已在告警,Go 层再优化也晚了
熔断器和限流器能不能套着用?什么时候该熔断而不是限流?
能套,但顺序不能错:必须「先限流 → 再熔断」。限流防的是量大,熔断防的是质差——比如下游服务响应延迟飙升、错误率超阈值。两者目标不同,混用反而互相干扰。
用 sony/gobreaker 时,别拿 HTTP 429(Too Many Requests)当失败计入熔断统计,这是限流器主动返回的正常控制信号,不是下游故障。否则一限流就熔断,整个链路直接雪崩。
- 熔断器的
ReadyToTrip函数应只检查真实下游错误(5xx、timeout、connect refused) - 限流器返回
false或429时,应跳过熔断器的OnRequestFailure回调 - 熔断窗口期(
Interval)别设太短(如 10s),否则抖动期间反复开闭,比不限更伤 - 观察指标优先看
gobreaker.State变化 +rate.Limiter.Reservations拒绝率,别只盯日志
为什么加了限流和熔断,还是被慢请求拖垮?
因为 Go 的 HTTP handler 默认在同一个 goroutine 里跑完所有中间件和业务逻辑,一个慢请求会卡住整个 goroutine,而 Go 调度器不会主动抢占它。限流只管“进不进来”,不管“进来了干多久”。
真正要防的是阻塞型操作:同步 DB 查询、未设 timeout 的 HTTP 调用、大文件读写。这些必须拆出去,并发控制粒度得下到单个操作,不是整个请求。
- DB 查询加
context.WithTimeout,超时直接 cancel - 对外 HTTP 调用必须带
ctx,且 client 设置Timeout和Transport.IdleConnTimeout - 避免在 handler 里用
time.Sleep或for {}死循环——这不属于 DDoS 防御,是 bug - 用
pprof抓/debug/pprof/goroutine?debug=2看是不是大量 goroutine 堆在某一行上
DDoS 防御不是加几个中间件就完事,关键路径上的每个阻塞点,都得有超时、取消、降级三手准备。没覆盖到的地方,永远是突破口。










