Go语言可用net/http+中间件+路由分发构建轻量API网关,适合中小团队灰度路由、协议转换等场景;需用gorilla/mux或httprouter实现动态反向代理,支持路径重写、健康检查与服务发现,并通过fsnotify实现配置热更新。
Go 语言本身不内置 API 网关,但用 net/http + 中间件模式 + 路由分发,能快速构建轻量、可控、低延迟的微服务 API 网关。它不适合替代 Kong 或 Tyk 这类企业级网关,但对中小团队自研、灰度路由、协议转换或统一鉴权等场景足够高效且易于调试。
gorilla/mux 或 httprouter 做动态反向代理路由网关核心是把请求按规则转发给后端服务。硬编码 http.Redirect 或简单 http.ServeProxy 不够用,需支持路径重写、Host 改写、超时控制和健康检查感知。
gorilla/mux 提供 Router + Subrouter,适合按服务名、版本、路径前缀做多级匹配,比如 /user/v1/* → http://user-srv:8080/
httprouter 性能更高(无正则匹配),但不原生支持路径重写,需手动处理 req.URL.Path 和 req.Host
httputil.NewSingleHostReverseProxy,它默认不透传 X-Forwarded-For 和 X-Forwarded-Proto,后端服务拿不到真实客户端 IP 和协议proxy := httputil.NewSingleHostReverseProxy(&url.URL{
Scheme: "http",
Host: "order-srv:8080",
})
proxy.Transport = &http.Transport{
DialContext: (&net.Dialer{
Timeout: 5 * time.Second,
KeepAlive: 30 * time.Second,
}).DialContext,
TLSHandshakeTimeout: 5 * time.Second,
}
http.HandleFunc("/order/", func(w http.ResponseWriter, r *http.Request) {
r.URL.Host = "order-srv:8080"
r.URL.Scheme = "http"
r.Header.Set("X-Forwarded-For", getClientIP(r))
proxy.ServeHTTP(w, r)
})所有进来的请求必须经过一层“守门人”,而不是在每个服务里重复实现。Go 的 http.Handler 链式调用天然适合中间件模式。
Authorization: Bearer ,验证签名和 exp,从 payload 提取 user_id 注入 context.Context
golang.org/x/time/rate 的 Limiter,按 client IP 或 user_id 维度控制 QPS,超限返回 429 Too Many Requests
method、path、status、latency、upstream_addr,避免打满磁盘——用 log/slog + 异步写入或对接 Loki硬编码 user-srv:8080 会丧失弹性。Kubernetes 下可用 Service DNS,但跨集群或混合云需更主动的发现机制。
health.service.user-srv,拿到节点列表后定期 GET /health 探活,剔除失败节点sync.RWMutex 保护,并在后台 goroutine 里定时刷新round-robin 均匀分发,要加权重(如新版本实例权重设为 10,老版本设为 90)和熔断(连续 3 次超时,暂停转发 30 秒)改个路由规则或加个限流阈值,不应该触发网关重启。Go 的 fsnotify 是最轻量的选择。
gateway.yaml,启动时加载,然后用 fsnotify.Watcher 监听文件变化http.ServeMux 或替换 gorilla/mux.Router 实例,注意旧连接仍在处理,新请求走新配置即可atomic.Value)存储当前生效的 *mux.Router,读取时 Load(),更新时 Store()
真正难的不是转发逻辑,而是错误传播控制——上游返回 502 时要不要重试?重试几次?重试是否幂等?这些决策点必须显式编码,不能依赖默认行为。