应用层只需守住三道边界:连接生命周期、请求体约束、关键路径熔断;超时设置、MaxBytesReader限上传、context+熔断器控下游、IP级原子限流、Accept阶段拒长连接、防护前置至Nginx/CDN/eBPF。

应用层不该承担DDoS主防御,但必须守住三道边界
Go的net/http服务不是防火墙,强行在http.HandlerFunc里塞IP封禁、行为分析、动态限流,只会让问题更糟:每个恶意请求都已创建goroutine、分配内存、走完TLS握手和路由匹配。真正该做的,是明确应用层只管“连接生命周期”“请求体约束”“关键路径熔断”这三件事。
-
http.Server.ReadTimeout、WriteTimeout、IdleTimeout必须显式设置,尤其IdleTimeout要小于Nginx的keepalive_timeout,否则Slowloris类攻击能长期占着连接不发数据 - 用
http.MaxBytesReader包装r.Body,对上传接口强制设上限(如5MB),避免大payload触发GC风暴或OOM - 数据库查询、下游HTTP调用等阻塞点,必须用
context.WithTimeout+gobreaker,且ReadyToTrip函数只检查5xx、timeout、connect refused——429是限流器主动返回的,不是故障
rate.Limiter按IP复用是唯一安全的限流姿势
全局单例rate.NewLimiter等于没限;每次handler里new一个会导致内存泄漏和锁竞争;用sync.Map存IP桶在高并发下争抢严重。正确做法是用atomic.Int64做连接计数,用sync.RWMutex保护map[string]*rate.Limiter,并从r.RemoteAddr提取纯IP(strings.Split(r.RemoteAddr, ":")[0])。
- 登录页等敏感路径建议
rate.NewLimiter(2, 3)(每秒2个,最多积压3个令牌) - 普通API可放宽到
(10, 20),但别盲目调高——令牌桶积压太多反而放大突发冲击 - 注意
rate.Limit返回值为0时代表桶空,此时应直接http.Error(w, "", http.StatusTooManyRequests),不进业务逻辑
长连接服务(如Livego)必须在Accept阶段就拒绝
RTMP、WebSocket这类长连接,攻击者建1000个空连接就能耗尽net.Conn和goroutine,限流完全无效。防护必须下沉到net.Listener.Accept()之后、握手之前。
- 在
Accept后立刻调用atomic.LoadInt64检查当前活跃连接数,超限时直接conn.Close()并记录日志 - Livego的
max_connections: 1000只是内部统计,不自动拒绝新连接;需在protocol/rtmp/accept.go类似位置手动加守门逻辑 - 绝对避免用
sync.WaitGroup或全局计数器——Add/Done在万级并发下就是性能杀手
所有有效防护都该前置到Go进程之外
真正的防线不在Go代码里。SYN Flood、UDP Flood、HTTP Flood的流量清洗,必须由Nginx、CDN或eBPF/XDP在内核层完成。Go应用层只负责“不被自己拖垮”。
- Nginx用
limit_req配burst和nodelay做IP级QPS控制,比Go里任何限流都早、都轻量 - Cloudflare或阿里云DDoS高防提供JS Challenge、Bot管理,Go连请求头都收不到
- 若需自研底层防护,用Go eBPF加载XDP程序,在驱动收包阶段丢弃恶意IP,延迟低于1微秒——但这已是网络工程师范畴,非应用开发者职责


















