sidecar代理不能直接用net/http转发请求,因其默认不透传原始请求头(如connection、upgrade),对websocket和http/2升级请求失败;需手动复制头、禁用重定向与keepalive、流式转发响应体,并正确处理unix域套接字通信及超时传递。

Sidecar 代理为什么不能直接用 net/http 转发请求
Go 的 net/http 默认不透传原始请求头(比如 Connection、Upgrade、Transfer-Encoding),遇到 WebSocket 或 HTTP/2 升级请求会直接失败。更麻烦的是,http.Transport 对 100-continue、分块传输、空响应体等边界情况处理严格,而 Sidecar 必须无感透传——不是“代理”,是“管道”。
- 必须显式复制所有请求头:
req.Header.Clone()不够,要跳过 hop-by-hop 头但保留业务相关头(如X-Request-ID) -
http.Transport需禁用重定向、关闭 KeepAlive(由上游控制),否则连接复用逻辑会干扰服务网格的连接生命周期 - 响应体必须用
io.CopyBuffer流式转发,不能全量读取再写入,否则大文件上传会 OOM
如何正确透传 HTTP/1.1 和 WebSocket 请求
WebSocket 升级依赖 Connection: upgrade 和 Upgrade: websocket 两个头同时存在且大小写敏感,Go 的 http.Header 会自动规范化 key,导致透传失败。
- 手动检查并保留原始 header key:用
req.Header["Connection"]获取值,但用outReq.Header.Set("Connection", ...)设置,避免被 normalize - 对
Upgrade头,需额外设置outReq.Header.Set("Upgrade", req.Header.Get("Upgrade")),不能依赖Header.Clone() - 启用
http.Transport的FlushInterval(如100 * time.Millisecond),防止 WebSocket ping 帧卡在缓冲区
gorilla/mux 路由器在 Sidecar 中的误用陷阱
很多人用 gorilla/mux 做路径匹配再转发,但 Sidecar 的核心职责是透明路由,不是业务路由。一旦引入路径重写或中间件,就脱离了“代理”本质,变成网关。
- 不要用
mux.Router.HandleFunc匹配/api/users/{id}—— 这会让 Sidecar 参与业务语义解析,违背轻量原则 - 真正需要的是基于 Host 或
X-Forwarded-For的简单分流,用http.ServeMux+strings.HasPrefix就够用 - 若必须做路径改写(如注入 trace ID),用
req.URL.Path = strings.Replace(req.URL.Path, "/v1/", "/v2/", 1),别注册子路由器
Sidecar 进程如何与主服务共享 Unix 域套接字
本地通信比 localhost:port 更高效且免网络栈,但 Go 默认不支持 http.Transport 直连 Unix socket。必须手动构造 http.Client 并指定 DialContext。
- 创建
http.Transport时设置:Transport.DialContext = func(ctx context.Context, _, _ string) (net.Conn, error) { return net.DialUnix("unix", nil, &net.UnixAddr{Name: "/tmp/app.sock", Net: "unix"}) } - 请求 URL 必须是
http://unix/协议(非标准但net/http支持),且req.URL.Host设为空,req.URL.Path为实际路径 - 注意 socket 文件权限:Sidecar 和主服务需同属一个 group,且 socket 创建时设
0660,否则connect: permission denied
Sidecar 最容易被忽略的不是功能实现,而是连接复用粒度和超时传递——上游服务的 Timeout 应透传到下游 Transport,而不是统一设 30s;HTTP/2 的 MaxConcurrentStreams 若不匹配,会导致连接被静默拒绝。
golang免费学习笔记(深入):立即使用
在学习笔记中,你将探索golang的核心概念和高级技巧!











