go应用层限流必须按ip复用rate.limiter实例,禁止在handler内新建;应使用sync.map缓存,key为标准化ipv4/ipv6地址(如net.parseip().to16()),并设置idletimeout防连接耗尽。

Go应用层限流必须按IP复用rate.Limiter
直接在handler里new一个rate.Limiter等于没限——所有请求共享同一个桶,IP维度完全失效。恶意IP和正常用户被平均稀释,攻击者反而能更稳地压着阈值发包。
正确做法是用sync.Map或带读写锁的map缓存每个IP对应的limiter实例,key必须是纯IPv4/IPv6地址(去掉r.RemoteAddr里的端口号),否则同一客户端多连接会被当成多个IP。
-
NewLimiter(10, 20)表示每秒最多10个请求、最多积压20个令牌;登录接口建议收紧到(2, 3) - 不要在每次请求里调用
rate.NewLimiter()——初始化开销大,且rate.Limiter本身不是线程安全的 - IPv6地址需标准化处理,比如用
net.ParseIP再.To16()转为统一格式,避免::1和0:0:0:0:0:0:0:1被当成两个key
http.Server必须显式配置IdleTimeout
不设IdleTimeout,Go的HTTP服务就默认永不关闭空闲连接。攻击者建1000个TCP连接只发一次GET然后挂起,30秒后你的文件描述符和goroutine就全被占满,新请求连accept都进不来。
ReadTimeout和WriteTimeout只管单次读写,对Keep-Alive长连接无效;真正起作用的是IdleTimeout(Go 1.8+才支持),它控制连接空闲多久后自动断开。
- 典型配置:
IdleTimeout: 30 * time.Second、ReadTimeout: 5 * time.Second、WriteTimeout: 10 * time.Second - 若用
fasthttp,它的Server.ReadTimeout不含idle逻辑,得额外配MaxConnsPerIP或中间件 - 对IoT设备等非浏览器客户端,可在响应头加
Header.Set("Connection", "close")禁用Keep-Alive复用
长连接服务必须在Accept阶段就做并发数硬限制
HTTP短连接靠限流能防住,但WebSocket、RTMP、MQTT这类长连接服务,攻击者不需要发任何业务数据,只要不断net.Dial就能耗尽net.Conn和goroutine。等握手完成再检查,黄花菜都凉了。
必须在net.Listener.Accept()返回后立刻判断当前活跃连接数,超限时直接conn.Close()并记录日志。别用sync.WaitGroup或全局计数器——高并发下Add/Done锁争抢严重。
- 用
atomic.Int64做连接计数:activeConns.Add(1)和activeConns.Add(-1) -
Livego的max_connections只是内部统计开关,不自动拒绝新连接,得在protocol/rtmp/accept.go之类位置手动加守门逻辑 - 注意:连接数上限要结合系统
ulimit -n和内核net.core.somaxconn一起调,光改Go代码没用
XDP拦截不能用Go写处理函数,只能当“遥控器”
网上有些教程说“用Go写XDP程序”,这是错的。eBPF校验器只认C/Clang编译出的字节码,Go编译器生成的指令流100%被内核拒绝,报错invalid instruction或program too large。
Go在这里的角色只是加载器和配置器:编译好C写的xdp_drop.o,用libbpfgo调用bpf_object__load()加载,再通过eBPF map(如BPF_MAP_TYPE_HASH)动态更新黑名单IP。
- 传IP进map时必须转网络字节序:
binary.BigEndian.PutUint32(buf, ip),否则127.0.0.1变成0x0100007f匹配不上 - map必须预分配内存,不能用
BPF_F_NO_PREALLOC,否则Map.Update()返回EINVAL - XDP在网卡驱动层运行,连
sk_buff都没分配,所以C代码里不能调用任何内核函数,所有包解析(ethhdr/iphdr/tcphdr)都得手撸指针偏移
真实防御从来不是靠某一段Go代码扛住全部流量,而是在连接建立前就让恶意请求失败——从net.Listener.Accept()的瞬间,到http.Server的超时参数,再到内核XDP的原始包判断,每一层的守门逻辑都得对准攻击者的发力点。最容易被忽略的,其实是那个没设IdleTimeout的HTTP服务,它看起来一切正常,直到某天凌晨连接数突然卡死在65535。
golang免费学习笔记(深入):立即使用
在学习笔记中,你将探索golang的核心概念和高级技巧!











