prefork开启后qps下降,主因是场景不匹配:单核机器或短连接密集型服务下fork开销反超收益;需验证cpu占用、物理核数,并确保db等资源按进程独立初始化,配反向代理实现请求级负载均衡。

Prefork 开启后 QPS 没涨反降?先确认 CPU 和连接模型是否匹配
开启 Prefork: true 不等于自动变快。它只在单进程已吃满 CPU(持续 ≥95%)、且机器有 ≥6 物理核、SSD IO 充足时才可能带来收益。压测发现 QPS 下降,大概率是场景不匹配:比如单核机器硬开 Prefork,或短连接密集型服务(如大量 HTTP/1.1 小请求),fork 开销反而盖过并行收益。
验证方式很简单:top -H 看单个 fiber 进程的 CPU 占用;lscpu | grep "CPU(s)" 查物理核数;再用 wrk -t12 -c400 -d30s http://localhost:3000/ 模拟中等并发,观察整体吞吐变化。
配置 Prefork 必须同步处理信号和资源初始化
没加信号处理就开 Prefork,app.Shutdown() 会失效,子进程残留成僵尸进程。必须显式监听 os.Interrupt 或 syscall.SIGTERM,并在 handler 中调用 app.Shutdown()。
另外,每个子进程独立运行,DB 连接池、Redis 客户端、本地缓存这些资源都得在每个进程里重新初始化——不能靠全局变量“共享”。常见错误是把 gorm.Open() 放在 main() 开头,结果所有 worker 都抢同一个连接池配置,引发超时或 panic。
- 正确做法:把 DB 初始化逻辑放进
fiber.New()后、app.Listen()前,确保每个进程走一遍 - 加
DisablePrint: true,否则每个 worker 都打印启动 banner,日志混杂难排查 - 若用
log.Printf记录启动信息,务必带上os.Getpid(),区分是哪个 worker 打的
为什么开了 Prefork 还要配反向代理?
Prefork 依赖内核的 SO_REUSEPORT 实现端口共享,但它的负载均衡策略是“连接级”而非“请求级”。一个长连接(比如 WebSocket 或 HTTP/2 流)始终绑定到固定 worker,无法动态迁移。当某 worker 因慢查询卡住,新连接仍可能被内核分给它。
所以生产环境必须前置 Nginx 或 Traefik,做两件事:
- 终结 TLS,卸载加密开销(Fiber 的
ListenTLS在 Prefork 下每个 worker 都重复加载证书) - 用 upstream 的
least_conn或ip_hash做更可控的分发,避免单点阻塞放大 - 健康检查探针也得配在 proxy 层,别直接探
:3000,否则 probe 请求也会被轮到卡死的 worker
环境变量怎么影响 Prefork 行为?
别写死 Prefork: true。开发环境开 Prefork 会让调试器 attach 失败、断点失效、日志乱序——根本没法 debug。应该用 os.Getenv("APP_ENV") 控制:
APP_ENV=production 时才启用,同时关掉 Logger 和 Recover 中间件(它们在多进程下日志竞争严重,还可能掩盖真实 panic);APP_ENV=development 时强制 Prefork: false,哪怕你手动设了 true 也忽略。
最容易被忽略的是 ulimit:Prefork 启动后,worker 数 = CPU 核数,每个 worker 都要独立维护连接、文件描述符、goroutine 栈。如果系统默认 ulimit -n 是 1024,4 核机器瞬间就耗尽 fd,报错 accept: too many open files。上线前必须 ulimit -n 65536 并写进 systemd service 文件的 LimitNOFILE=65536。











