Go服务器性能调优:避坑选型,QPS翻倍实战
|
接口背锅那天我正对着监控大屏看 latency p99 拐了个歪脖子,咖啡杯上印的“GopherCon 2019”已经磨花了边——去年寒假的事,现在想起来手还麻。PM 在会议室用激光笔戳着「QPS 翻倍」那行字,声音像在念遗嘱,而我刚把灰度开关切到 73%,没推完,后端就飘红了。不是 panic,是那种软塌塌的、每秒掉三百七十二次调用的红。 先扔结论:翻倍根本不是靠压栈优化或者 GC 调参,是砍掉了三个 SDK、换掉两个中间件、干掉一个伪幂等层。我说不清那个 RPC 超时兜底逻辑到底在哪一行埋的雷——但这个我敢打包票:你所有“优雅降级”的代码,80% 在真实故障里连编译都过不去,更别说跑了。前端昨天改了 header 大小写,我们重发了五版;测试发现 mock 数据里时间戳是零值,没人记得是谁写的那个 time.Now().Add(-1 time.Hour) 的 patch。
文章配图,仅供参考 我原以为把 GOMAXPROCS 锁死在 16 就能稳住调度毛刺,结果 oncall 手机震了一下,震得我手抖,误点了告警确认——那会儿正好 DBA 在清临时表,他们用的是 pg_dump + restore 的“原子切换”,其实根本不是原子,只是慢得让人愿意信它是。监控大屏上 Redis 连接池那条曲线忽然往下秃了一小截,像被谁咬了一口,没人理。反正它三天两头秃,秃完自己长回来,比我的发际线规律。 选型避坑?呵。去年圣诞前一周,我们团队围在白板前画拓扑,PM 把「支持最终一致性」圈出来打钩,DBA 抬头问:「你们真打算让订单状态从 MySQL 写进 Kafka 再捞出来更新 ES?」我点头,他当场笑出声,说他老婆上周还在用那个电商 App 查物流,查了四次,三次显示「已发货」,一次「待支付」——而那个 App 后端用的正是我们正在拍板的方案。我说这属于业务兜底问题,他说:「不,这是你把 CAP 当成乐高拼插。」我没反驳,因为他说对了。后来我们真砍了那套链路,换回双写+对账,上线当晚测试去倒了杯水,水杯放桌上时溅了两滴在笔记本键盘上,C 键失灵了三小时。这事跟性能无关,但我就是记得。 优胜劣汰不是修辞。是某天凌晨两点,老王——就是那个写了十七年 Java、三年前转 Go、至今还留着 $GOPATH/bin 在 PATH 里的老王——突然把 grpc-gateway 的中间件全删了,说“HTTP/1.1 的 header 都扛不住你还搞 OpenAPI Spec 校验?”第二天上线,p95 掉了十八毫秒。没人表扬他,因为他同时删了日志 traceID 注入,导致三天内五次故障排查卡在“找不到上下文”。但那个十八毫秒,是真的。我不说准是不是 header 解析省出来的,但数字没骗人。 KPI 上个月调了,说是把“线上 P0 故障平均恢复时长”从 47 分钟压到 38 分钟——可上次那个 goroutine 泄漏,我们花了六小时定位到一个 channel 缓冲区设成了 0,却只记了 11 分钟 MTTR,因为值班表上记录的是“11:07 接到报警,11:18 下发 kill -15”。至于那之前的五十分钟里,我们在翻 Docker logs、改 liveness probe、骂 k8s readiness 健康检查逻辑……没人记。工资条上季度绩效多发了五百块,扣税后实得四百一十二元,刚好够我买两盒速溶黑咖啡和一包没开封的薄荷糖,糖现在还在抽屉第三格,包装纸折痕还很新。 反常识结论来了:你花两天调优 net/http 的 keep-alive timeout,不如花半天确认 client.Do 是否真的复用了 Transport。我试过,Transport.MaxIdleConnsPerHost 设成 100 后 QPS 上不去,设成 0 反而涨了一截——后来发现 upstream 是个 nginx,配了 proxy_http_version 1.0,keep-alive 根本没生效。这种事没法自动化检测,只能人肉抓包。上周我就蹲在 wireshark 前盯了四十一分钟,直到眼睛酸胀,才看见那个 FIN-ACK 乱序。旁边同事问我:“你还在看那个 HTTP/1.0?”我说:“嗯。”他没再问,低头继续改他那份 PR,标题写着「remove deprecated config file loader」——那个 loader 我们半年前就说要下掉,到现在还活在 init() 里,只是 never called。 最后说个跑题的:运维今天发邮件说下周开始,所有服务必须接入新日志平台,强制 json 格式、必填 trace_id 和 request_id。我回了一句:“trace_id 怎么注入?中间件链里哪一层生成?如果 gateway 已经透传,你们平台还重复生成怎么办?”没收到回复。可能他们觉得这个问题太初级,也可能他们也卡在这儿。反正我先把那段 logrus hook 里的 trace 生成逻辑注释掉了——加了句 // TODO: sync with platform team (not urgent),编译过了就行。别的事,等我喝完这杯咖啡再说。 (编辑:站长网) 【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容! |


