Go服务器性能调优:避坑选型,QPS翻倍实战
|
我原以为去年元旦那版灰度切得过去,结果监控面板凌晨三点弹窗像催命——QPS掉到八百还飘红,而隔壁组的旧服务稳在两千五。你猜怎么着?我们全把锅甩给新埋点逻辑,直到老张把压测脚本扔我脸上:同一个请求体,go-http2 跟 fasthttp 对着干,前者 CPU 占满,后者连 GC 都懒得动。我们还在争要不要加熔断,人家压根没开 Goroutine 池。 投影仪对焦不准,字全糊成毛边,小王伸手去调旋钮,手一滑碰倒水杯,水漫到那摞打印的需求单上,没人收。最上面那张写着「登录态强校验」,底下画了三个叉——后来谁还记得这功能做了没?反正埋点数据里没它。空调在滴水,滴、滴、滴,跟老板敲桌子的节奏一模一样。他问「为什么不用 gRPC?」我刚说「序列化开销大」,李工直接切屏贴出 benchmark:protobuf 3.21+gRPC-go 在 4KB payload 下比 JSON+HTTP/1.1 快 1.8 倍。我哑火了。不是因为他说得对,是因为我根本没跑过这个量级的真实 payload。 跟老板或开发的争执,就发生在评审那一刻。他指着白板说「先用 echo,轻量好上手」,白板笔没水了,划拉两下只剩灰印。我补了一句「echo 的中间件链太深」,老张突然拍桌:「你上次说 gin 拦截器有锁,结果呢?自己写的自定义 Router 还多包一层 map 查找」。我嘴硬:「至少没 panic」——结果三天后线上真 panic 了,panic 日志里夹着一行 `net/http.(conn).serve: read: connection reset by peer`,跟 Go 1.21 的 HTTP/2 连接复用 bug 一模一样。我查文档查了六小时,最后发现升级 net/http 补丁包就能解。但没人记得这事——因为 PRD 里写的是「支持 HTTPS」,不是「撑住十万并发下的 HTTP/2 连接漂移」。 所以翻倍不是调出来的,是砍出来的。砍掉那个没人用的「设备指纹增强上报」模块,删掉三套重复的 JWT 解析逻辑,把两个 config 文件 merge 成一个——不是为了优雅,是怕部署时 env 变量漏配。我们花两天重写了健康检查端点,只返回 `{"status":"ok"}`,不查 DB 不走 Redis 不读 config;QPS 当场涨三百。这不是性能优化,是优胜劣汰:活下来的代码,都见过真实流量打脸。那些写进 PRD 里「高可用」「低延迟」的形容词,最后都死在第一个没覆盖的边界 case 上。 外卖汤洒了一点在桌上,油星子浮着,李工拿纸巾按了一下,纸巾破了,汤渗进木纹里。他忽然讲起他们组去年圣诞上线的「用户行为快照」功能:前端每 5 秒传一次 DOM 快照,后端存 ES,PRD 写着「支撑 AB 实验归因」,结果上线两周,运营没查过一次快照数据,AB 实验全靠后端埋点。功能活着,但没人用。和「登录态强校验」一样,需求评审时所有人都点头,没人问「谁查日志?」、「下游调用方有几个?」、「超时阈值设在哪?」 我原以为换 fasthttp 就万事大吉,结果发现 goroutine 泄漏在 pprof 里藏得极深:一个被 defer 忘关的 http.Client,每个请求偷偷 spawn 一个永不退出的 keep-alive 线程。查这玩意花了四天。第四天傍晚,小王手机一直在震,看了八次都没接,第五次亮屏显示「我妈住院」,他起身就走了,椅子少一把,歪在门边。我们继续盯着火焰图——红色热点钉在 runtime.mcall 上,像一颗长了毛的痣。
文章配图,仅供参考 其实最坑的是 logger。用了 zerolog 的 global logger,默认带 trace_id 字段,每次 log 都得做 map 构造+json marshal;我们没改,默认采样率是 100%,QPS 五百时它自己占了 17% CPU。改成 lazy logger 后,CPU 降了八个点。但这事谁提过?PRD 里写的「日志可追溯」,没人定义「可追溯」= 每条 log 必须含 12 个字段还是只要 error 时才打上下文。我们自己定了,然后自己把自己拖垮。自己那份没人看的文档——我写的《Go 服务性能红线清单》,里面第三条写着「禁止在 handler 中 new struct{} 类型的空结构体作 map key」,因为 gc 扫描耗时指数增长。去年十月发的,群里@全员,零回复。上周代码审查又看见有人这么干,还是同一个人,我截图发他微信,他回「忘了」。我也没再追。不是宽宏,是知道追了也没用:排期卡在「客户急要首页改版」,而首页改版里那个「加载骨架屏」功能,灰度七天,转化率-0.3%,现在停了,代码没删,就挂着。 现在想来,QPS 翻倍根本不是技术目标,是淘汰机制:留下的都是被压测锤过三次、被夜班告警骂醒过、被用户在工单里用「你们接口慢得像拨号上网」戳过脊梁骨的代码。别的,全归零。今年别搞新框架了,把去年元旦那版的 defer 链全捋一遍,从最外层 handler 往里数,数到第几个 defer 开始丢 context;数完,删掉第一个不 check err 的地方。别管 ROI,先让 panic 日志里不再出现 `net/http.(persistConn).roundTrip`——这行字我见得太多,多到能默写出来。 (编辑:草根网) 【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容! |






浙公网安备 33038102330471号