小李把Go单体重写成Rust Axum,公司一年省了800多万,他涨薪到200万
小李那个让我意外的涨薪
上周小李约我吃饭,这小子平时抠门得很,主动请客肯定有事。
果然,酒过三巡他开口了:“我把公司后端从Go重写成Rust了,一年省了800多万,公司给我涨薪到200万。”
我差点没把筷子掉桌上。这小子我知道,技术是不错,但也没夸张到这种程度。
“你先别急着羡慕,“小李干了杯啤酒,“这事儿不能简单看。大多数重写项目最后都变成了职业生涯的坑——进度慢、风险大、锅背不完。我这次能全身而退,是因为情况刚好满足了几个条件。”
他们原来的系统有多烂
小李接手那个Go单体的时候,系统已经运转好几年了。代码写得其实还行,不是什么烂摊子。但就是——怎么说呢——累了。
“就像你家那台用了五六年的冰箱,“小李说,“没坏,但总是嗡嗡响,耗电比以前高,有时候制冷不太给力。你说它坏了吧,还能用;说它好吧,心里总不踏实。”
经过分析,这是一个典型的技术重写案例,原系统存在性能优化空间,同时成本优化的需求也很迫切。
看几个数字:
- 峰值能抗住2.8万请求每秒
- 每周大概出三次故障
- 每个月基础设施费用130多万
- 团队里慢慢形成了"那块代码别碰"的默契
这些问题都指向一个技术重写的必要性,同时也能带来显著的性能优化和成本优化效果。
大多数故障不是那种惊天动地的宕机。是延迟抖动、部分服务变慢、重试风暴之类的。用户觉得慢,但不至于打客服电话。这种问题最麻烦——不够严重所以没人批预算修,但一直在消耗团队精力。
[客户端]
|
[负载均衡]
|
[Go 单体]
| | |
认证 搜索 计费
|
[主数据库]
所有东西挤在一起。想优化搜索,得考虑会不会影响计费。想上新功能,得把整个系统测一遍。每次扩容,所有模块一起扩,但其实只有搜索需要更多资源。
“这种架构不是不能用,“小李说,“而是到了一定规模,钱就开始哗哗流。通过微服务架构改造,可以实现更精细的资源管理和更高效的性能优化。”
这就是为什么小李决定进行Go语言迁移到Rust的决策背景。
学Rust的那三个月有多痛苦
小李没一上来就动生产代码。前三个月全是晚上和周末自己学,公司一行代码没改。
从Go过来学Rust,有些地方特别别扭。Go的并发模型,开个goroutine就完事了,runtime帮你调度。Rust不是这样——你得明确告诉它这个任务什么时候跑、在哪跑、数据归谁管。
“一开始我觉得这是Rust在折腾人,“小李说,“学了一阵子才明白,这其实是把Go藏在runtime里的复杂性给拎出来了。Go的调度器确实优秀,但它帮你做的决定,你看不见也改不了。Rust让你直面这些决定——累是累点,但出了问题你知道往哪查。”
这次Go语言迁移到Rust的技术重写过程,让小李深刻体会到了两种语言的差异。
举个具体的:Go里goroutine阻塞了,runtime会自动切换。你可能根本不知道哪里阻塞了,直到某天流量上来卡住。Rust的Tokio不会帮你藏这些,阻塞调用会直接把worker线程卡死,你立刻就会发现。
“这种’不帮你藏问题’的设计哲学,“小李说,“后来救了我们好几次,也是这次性能优化成功的关键因素。”
第一刀只切搜索
小李他们没搞大爆炸式重写。第一刀只切搜索服务。
为什么选搜索?几个原因:读多写少,状态简单;API边界清晰,不用动其他模块;是当时最吃资源的部分;出了问题能快速回滚到Go版本。
[客户端]
|
[负载均衡]
|
[Go 单体] ---> [Rust Axum 搜索服务]
|
[旧数据库]
两个系统并行跑了一个月。用流量镜像对比结果,确保Rust版本返回的数据和Go版本一模一样。
结果让小李自己都有点意外:搜索吞吐量从2.8万飙到12万请求每秒,大概4.7倍的提升,高负载下P99延迟更稳定。
但小李很实在:“这个数字有水分。新系统做了更激进的缓存,请求路径更短,数据模型也针对搜索优化过。不能简单说Rust比Go快4.7倍。”
无论如何,这次微服务架构改造带来的性能优化效果是显著的。
真正让他惊喜的不是速度,是稳定性。以前流量高峰会触发重试风暴,然后整个系统一起抖。换成Rust之后,搜索服务在压力下表现得特别——无聊。就是稳稳地跑,没什么drama。
“无聊,“小李说,“在后端工程里是最高的赞美。”
一年时间慢慢磨
搜索服务稳定跑了三个月,领导开始问:其他部分要不要也换?
接下来一年小李他们做的事情可以总结成一句话:永远保持两条腿能跑。
阶段1: [客户端] -> [负载均衡] -> [Go 单体]
阶段2: [客户端] -> [负载均衡] -> [Go 单体] -> [Rust 微服务]
阶段3: [客户端] -> [负载均衡] -> [Rust 微服务] -> [Go 残留]

通过这种渐进式的Go语言迁移策略,他们成功构建了微服务架构,实现了显著的成本优化。
“Istio在这个过程中帮了大忙,“小李说,“我们可以按百分比切流量,某个接口出问题就在网格层回滚,不用动代码重新部署。”
OpenTelemetry是另一个功臣。Go的trace也做了,但Rust的span有个特点——生命周期跟代码结构绑定。资源泄漏、阻塞调用、请求扇出,在trace里一目了然。以前排查问题是破案,现在是看图说话。
这种可观测性改变了他们处理故障的方式。以前是"又出问题了,这次什么症状”;现在是"图上这里异常,查一下是不是那个逻辑”。
最后的数字
迁移全部完成后:
| 指标 | Go 单体 | Rust 微服务 |
|---|---|---|
| 峰值吞吐量 | 2.8万 RPS | 18.7万 RPS |
| 月基础设施成本 | 130万 | 30万 |
| 年节省 | - | 约800万 |
| 生产故障 | 每周约3次 | 14个月零故障 |

130万降到30万,一年就是800万的差价。但这不是"Rust快"带来的,是几个因素叠加的结果:异步调度可预测,没有隐藏的runtime开销;Axum中间件支持显式背压控制;服务拆小了,微服务架构带来更好的故障隔离;缓存命中率上去了,重试少了,实现了显著的性能优化和成本优化。
当然也有代价。编译时间变长了,招Rust工程师比招Go工程师难,新人上手需要更多系统编程基础。有些内部工具他们没动,继续用Go,因为技术重写它们不划算。
为什么这次重写能变成涨薪
“涨薪不是因为Rust牛,“小李说,“是因为这次重写改变了几个对公司来说很重要的东西。”
每年800万的成本节省是可审计的,财务能直接从账单上看到;以前每周三次故障,运维团队长期疲于奔命,现在这些人力释放了;产品上新功能不用再担心"会不会把后端搞挂”;容量规划会议从"要加多少机器"变成"现在资源够用一年”。
“大多数技术重写止步于’它能跑了’,“小李说,“这次Go语言迁移改变了公司的成本结构和运营节奏。这才是领导会注意到的。涨薪不是奖励,是公司重新评估了我的经济贡献。”
那几件他不会再做的事
小李说如果重来一次,有些事会做不同的处理。
第一,低估了重写带来的社会成本。团队里有人担心自己被边缘化,有人觉得自己的代码被否定了。这些情绪需要花时间处理,但他当时太专注技术了。
第二,Rust不适合所有场景。后台管理界面、低频批处理任务,他现在不会去碰。重写它们的收益太小,不值得承担风险。这次技术重写的经验告诉他,选择合适的场景比技术本身更重要。
第三,没有服务网格和分布式追踪,不要开始Go语言迁移。他们能做到零故障切换,全靠Istio和OpenTelemetry。没有这些基础设施,“平滑迁移"就是一句空话。
你应该做吗
“我临走时问小李,那我们公司要不要也试试?“我回忆着饭局的情景。
他反问我:“你们现在痛吗?”
如果系统运行稳定、成本可接受、团队不疲惫,那技术重写就是制造问题而不是解决问题。不是所有系统都需要重写,大多数系统其实都不需要。
Go语言迁移到Rust这样的技术重写只在一种情况下有意义:业务已经感受到了痛,而且愿意承担一种新的风险来消除旧的痛,同时期待通过这次性能优化和成本优化带来实际的收益。
“我们的情况刚好满足这个条件,“小李说,“你们的情况呢?”
觉得这篇文章有用吗?
如果你的公司也在考虑技术栈迁移,或者对Rust和Go的实际生产对比感兴趣,欢迎点赞让更多人看到。
有什么问题或者想法?欢迎在评论区聊聊。你们团队遇到过什么样的技术债务?最后是怎么处理的?
关注梦兽编程,下一篇我们聊聊Rust异步编程的几个容易踩的坑,以及如何用Tokio Console来调试它们。
