用 Go 写个极简反向代理,把 CC 攻击挡在业务容器之外
最近容器化改造,所有业务都跑在 K8s 里。某天 3 点 15 分,Prometheus 疯狂告警:某个业务 Pod CPU 飙到 200%,原因是 /api/search
接口被刷。传统的 WAF 在集群外,流量已经压到 Ingress 上了,再往上加规则来不及。于是干脆在业务前面再插一层 Go 写的「微型网关」,在流量进容器之前就把它丢掉。
1. 项目结构
tiny-guard/
├── main.go
├── limiter.go
├── go.mod
2. 核心代码
main.go(不到 120 行,可直接 go run
):
package mainimport ("log""net/http""net/http/httputil""net/url""sync""time"
)// 滑动窗口计数器
type window struct {mu sync.RWMutexcounter intresetTime int64
}var store = make(map[string]*window)func getIP(r *http.Request) string {// 简单取 X-Forwarded-For 第一个ip := r.Header.Get("X-Forwarded-For")if ip == "" {ip = r.RemoteAddr}return ip
}func allow(ip string) bool {w, ok := store[ip]if !ok {w = &window{}store[ip] = w}w.mu.Lock()defer w.mu.Unlock()now := time.Now().Unix()if now-w.resetTime > 60 {w.counter, w.resetTime = 0, now}w.counter++return w.counter <= 100 // 每分钟 100 次
}func main() {target, _ := url.Parse("http://127.0.0.1:8080 ")proxy := httputil.NewSingleHostReverseProxy(target)http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {ip := getIP(r)if !allow(ip) {http.Error(w, "too fast", http.StatusTooManyRequests)return}proxy.ServeHTTP(w, r)})log.Println("listening :9000")log.Fatal(http.ListenAndServe(":9000", nil))
}
3. 运行测试
go run .
# 另开窗口
ab -n 1000 -c 50 http://localhost:9000/api/search
观察日志,大量 429 Too Many Requests
,后端 Pod CPU 立刻降回正常水位。
4. 进阶玩法
- 把
store
换成 Redis + TTL,就能在多个网关实例间共享。 - 需要更高并发量?把限流逻辑换成令牌桶即可,代码量不超过 20 行。
- 如果你不想自己维护网关镜像,直接给集群前面挂一个 高防 IP,厂商已经帮你把滑动窗口、令牌桶、人机验证都封装好了,剩下的就是配几条规则、喝杯咖啡等告警解除。
5. 一行部署
docker build -t tiny-guard .
kubectl apply -f k8s/deployment.yaml