FastHTTP代理不能直接套用net/http中间件模式,因其RequestCtx不兼容http.Handler,所有读写基于预分配byte slice,零拷贝设计要求显式调用Body()或SetBodyString()等方法,否则会panic或响应为空;需用fasthttp.NewClient替代http.DefaultClient,并手动处理hop-by-hop头及chunked编码。

为什么 FastHTTP 代理不能直接套用 net/http 的中间件模式
FastHTTP 的 RequestCtx 不兼容标准库 http.Handler,所有读写都基于预分配的 byte slice(如 c.Request.Body() 返回的是切片而非 io.ReadCloser),直接复用 http.RoundTripper 或 http.ReverseProxy 会 panic 或静默失败。它的零拷贝设计意味着你不能像往常那样用 io.Copy 直接转发 body —— 必须显式调用 c.Request.Body() / c.Response.SetBodyString() 等方法。
常见错误现象:panic: interface conversion: interface {} is nil, not []uint8(因未检查 c.Request.IsBodyStream() 就调用 c.Request.Body());或响应体为空(忘记调用 c.Response.SetStatusCode() 和 c.Response.SetBody())。
- 必须用
fasthttp.NewClient()替代http.DefaultClient,它内部复用连接池和 buffer,避免 goroutine 泄漏 - 转发请求头时需手动过滤/重写:FastHTTP 不自动处理
Connection、Keep-Alive、Transfer-Encoding等 hop-by-hop 头,否则下游服务可能解析失败 - 若上游返回 chunked 编码,
fasthttp.Client默认不自动 decode,需设client.DisableHeaderNamesNormalizing = true并自行处理Transfer-Encoding: chunked
如何安全地透传 streaming 请求(如 SSE、文件上传)
FastHTTP 默认将整个请求 body 加载进内存(c.Request.Body()),对大文件或长连接流(如 Server-Sent Events)极易 OOM。正确做法是启用流式处理:判断 c.Request.IsBodyStream() 为 true 后,用 c.Request.BodyStream() 获取原始 io.Reader,再通过 fasthttp.StreamRequestBody 配合自定义 RequestHandler 分块转发。
关键点:不能在 handler 中直接调用 c.Request.Body(),否则会提前消费 stream,导致后续转发无数据。
- 对 POST/PUT 请求,先检查
c.Request.Header.ContentLength() > 0且!c.Request.IsBodyStream()才走内存模式;否则必须用StreamRequestBody+io.Copy到 client request body - 转发响应流时,禁用
c.Response.SetBody(),改用c.SetBodyStreamWriter(),并在回调中持续io.Copyclient response body 到sw - 务必设置超时:
client.ReadTimeout = 30 * time.Second,否则流挂起会阻塞整个 goroutine
如何避免 FastHTTP 代理中的 goroutine 泄漏
FastHTTP 的 RequestCtx 生命周期由 server 控制,但如果你在 handler 内部启 goroutine 并持有 *fasthttp.RequestCtx 或其字段(如 c.Request.URI() 返回的指针),会导致 ctx 无法被回收,最终内存持续增长。
使用 uber-go/dig 在 Golang 中实现依赖注入——基于反射容器,提供 Provide/Invoke、dig.In/dig.Out 参数与结果对象,支持命名依赖等
典型泄漏场景:异步记录日志、上报 metrics、或调用下游后做 post-process 时直接捕获 c。
- 所有异步操作必须复制所需字段:用
string(c.Path())、c.UserAgent()等拷贝值,而非保留c指针 - 禁止在 goroutine 中调用
c.Success()、c.Error()等修改响应的方法 —— 此时 connection 可能已关闭 - 使用
fasthttp.AcquireClient()/fasthttp.ReleaseClient()管理 client 实例(仅限极简场景),生产环境推荐单例fasthttp.Client+ 连接池配置
如何让 FastHTTP 代理支持 TLS 终止与 SNI 路由
FastHTTP 原生不解析 TLS SNI,无法像 nginx 那样根据域名分发到不同后端。若需多租户 TLS 终止,必须在 TCP 层拦截 ClientHello —— 即用 fasthttp.ListenAndServeTLS 仅作证书卸载,再通过 tls.Config.GetConfigForClient 动态返回证书,并结合 fasthttp.Server.TLSConfig 的 NextProtos 支持 ALPN。
但真正路由仍需靠 HTTP Host 头:FastHTTP 的 c.Host() 是解析后的字符串,比 net/http 更可靠(不会受攻击性 header 影响),可放心用于匹配虚拟主机。
- 若需 SNI 级路由(如不同域名指向不同 upstream),必须用
crypto/tls手动监听,解析 SNI 后再把 conn 交给对应fasthttp.Server实例 - 开启 TLS 时,禁用
client.InsecureSkipVerify = true,改用client.TLSConfig = &tls.Config{RootCAs: pool}验证后端证书 - 注意:FastHTTP 的
ListenAndServeTLS不支持通配符证书自动匹配,需在GetConfigForClient中手动比对clientHelloInfo.ServerName
最易被忽略的点:FastHTTP 的 RequestCtx 在 TLS handshake 完成前不可用,所有依赖 c.Host() 或 c.IsHTTPS() 的逻辑必须放在 handler 内,而非连接建立阶段。
golang免费学习笔记(深入):立即使用
在学习笔记中,你将探索golang的核心概念和高级技巧!










