上线必须用fiber.new()禁用默认中间件,关闭disablestartupmessage和enableprintroutes,保持strictrouting为true;禁用fiber.logger等日志中间件,避免i/o瓶颈和ttfb增加0.3–0.8ms。

fiber.New() 启动前必须关掉哪些默认行为
上线用 fiber.Default() 就等于主动给自己埋雷:Logger 每秒打几百条日志、Recover 捕获 panic 时堆栈全量输出、RequestID 自动生成并写入响应头——三者叠加,在 4 核 8G 环境下实测让首字节时间(TTFB)增加 0.3–0.8ms,压测到 2 万 QPS 时 I/O 直接成为瓶颈。
正确做法是只用 fiber.New(),再手动加真正需要的中间件。必须显式配置:
-
DisableStartupMessage: true—— 关闭启动 banner,避免首次请求前多一次 stdout 写入 -
EnablePrintRoutes: false—— 路由树打印会遍历全部节点,CPU 白耗 -
StrictRouting: true(保持默认)—— 不要关它,关了反而引发 /users/:id 和 /users/ 匹配冲突
如果你看到启动后还有日志,大概率是误启了 fiber.Logger() 或第三方 recover 中间件,得逐个排查。
路由和参数解析怎么写才不拖慢 QPS
Fiber 的 Radix 树本身极快,但你一写错 handler,性能就退化到 net/http 水平。关键不是“能不能跑”,而是“并发上来后延迟是否突增”。
常见错误写法及替代方案:
- 用
c.Body()→ 改用c.BodyBytes():前者拷贝整个缓冲区,后者直接返回底层[]byte引用(前提是不修改内容) - 用
strconv.Atoi(c.Params("id"))→ 改用c.ParamsInt("id"):内置缓存、跳过 panic、避免重复类型转换 - 用正则路由
app.Get("/user/*", ...)→ 改用参数路由app.Get("/user/:id", ...):前者性能差 3–5 倍,且通配节点只能在路径末尾 - 用
json.Marshal(data)+c.Send(...)→ 改用c.JSON(200, data):Fiber 内部用预分配 buffer 和 unsafe 字符串转换,快 20% 左右
注意:/user/:id 不做格式校验,/user/abc 也能匹配成功;真要校验,用 v3 的 RegisterCustomConstraint,别在 handler 里手写 if 判断。
HTTP 客户端调用下游服务为啥总卡住
Fiber 本身不封装 http.Client,但很多人在 handler 里直接用 http.Get 或裸 &http.Client{},结果压测时连接排队、TLS 握手翻倍、DNS 卡顿——这不是 Fiber 的锅,是 http.Transport 默认值太保守。
生产必须复用自定义 client,并显式配置:
-
MaxIdleConnsPerHost: 100(别用默认 2) -
IdleConnTimeout: 30 * time.Second(默认为 0,易被服务端断连) -
TLSClientConfig: &tls.Config{ClientSessionCache: tls.NewLRUClientSessionCache(100)}(启用 TLS 会话复用) - 所有调用必须带
context.WithTimeout,别依赖time.AfterFunc或全局超时
千万别在 handler 里每次 new client,也别用 http.DefaultClient——它共享 Transport,一个慢请求就能拖垮整个 goroutine 池。
为什么开了 Prefork 反而更慢
Prefork: true 听起来能压榨 CPU,但它只在明确有多个物理 CPU 核、且单进程已吃满一个核还扛不住时才有效。小规模部署(≤4 核)、SSD 一般、连接数
真实场景中容易忽略的点:
- Prefork 启动后,
app.Shutdown()必须用os.Interrupt信号控制,否则子进程可能残留 - 所有路由注册必须在
app.Listen()前完成,运行时不能动态改路由树——Prefork 下各子进程共享同一份冻结的 Radix 树,但各自独立处理请求 - 如果你用了全局变量或未加锁的 map/slice,Prefork 会让并发写冲突更早暴露(
fatal error: concurrent map writes)
真正卡顿的根源往往不在框架层,而在 handler 里没设超时的数据库查询、同步 HTTP 调用,或用了 time.Sleep() 模拟延迟——这些操作会让单个 goroutine 挂起,P95 延迟直接抬升,跟 Prefork 无关。
golang免费学习笔记(深入):立即使用
在学习笔记中,你将探索golang的核心概念和高级技巧!











