请点击上方蓝字TonyBai订阅公众号!

大家好,我是Tony Bai。
最近,东南亚科技巨头、出行公司 Grab 的一篇技术博客《Counter Service: How we rewrote it in Rust》在技术圈引起了不小的震动。他们将一个高 QPS(每秒查询率)的 Go 微服务(Counter Service)用 Rust 进行了重写,结果令人瞩目:在保持相似 P99 延迟性能的前提下,基础设施成本降低了高达 70%! 。

P99延迟对比:Go(紫色),Rust(蓝色)
这个案例无疑给许多以 Go 作为主力语言的团队和开发者带来了强烈的冲击。Go 语言以其简洁、高效并发、快速编译以及强大的生态系统,在微服务、云原生领域早已占据重要地位。那么,Grab 的这次成功“叛逃”,是否意味着 Go 语言在某些场景下的“护城河”正在被侵蚀?Rust 真的是解决一切性能和成本问题的“银弹”吗?
今天,我们就来深入剖析 Grab 的这个重构案例,看看他们究竟“得”了什么,“失”了什么,以及这背后能给咱们 Gopher 带来哪些宝贵的启示。

Rust 的“杀手锏”:极致效率带来的基础设施成本骤降
Grab 的 Counter Service 主要负责计数和提供 ML 模型/欺诈规则的计数器服务,是一个典型的 I/O 密集型和计算密集型并存的服务,QPS 峰值可达数万。用 Go 实现时,该服务需要大约 20 个 CPU Cores 来支撑。
然而,在用 Rust 重写后,同样的负载下,新的 Rust 服务仅需 4.5 个 CPU Cores!这几乎是 近80% 的资源节省,直接带来了 70%以上的基础设施成本降低。
为什么 Rust 能做到如此极致的效率提升?Grab 的文章和 Rust 语言本身的特性共同揭示了答案:
无垃圾回收 (GC):这是 Rust 相比 Go 在追求极致性能和资源控制上的核心优势。Go 的 GC 虽然已经非常优秀,但在高并发、低延迟场景下,GC 扫描和 STW (Stop-The-World) 仍然可能引入不可预测的延迟抖动和额外的 CPU 开销。Rust 通过所有权系统在编译期保证内存安全,无需运行时 GC,从而消除了这部分开销。
内存安全与零成本抽象:Rust 的所有权、借用检查等机制虽然带来了陡峭的学习曲线,但也确保了内存安全,避免了空指针、数据竞争等常见问题。同时,Rust 的许多高级抽象(如迭代器、闭包)能够在编译期被优化掉,实现“零成本抽象”,性能接近 C/C++。
更精细的控制:Rust 赋予开发者对内存布局、线程模型更细致的控制权,使得在特定场景下可以进行深度优化。
Grab 的案例似乎在证明,当业务场景对资源消耗和运行成本极度敏感,且服务逻辑相对“简单”(Grab 特别强调了选择重写目标时,功能需要足够简单,复杂度可控)时,Rust 的这些特性能够带来实实在在的巨大回报。
光鲜背后的“代价”:Grab 的探索与挑战
然而,享受 Rust 带来的极致效率并非没有代价。Grab 团队在博客中也坦诚地分享了他们遇到的挑战和权衡:
陡峭的学习曲线,尤其是
async
文章提到:对于习惯了 Go 语言简洁 go关键字和 GMP 调度模型的 Gopher 来说,Rust 的所有权、生命周期已经是第一道坎,而 async/await 异步模型及其“函数着色”问题、显式 yield(通过 await)等概念,则带来了更高的认知负荷。Grab 团队也曾因错误地在异步代码中使用了同步 Redis 调用而导致性能不佳。
生态系统与内部库的“阵痛”
虽然 Rust 的生态在快速发展,但在某些特定领域,库的选择可能不如 Go 那样成熟和丰富。Grab 团队在选择 Datadog 和 Redis 客户端库时就进行了一番评估和取舍。
更痛的是内部库的迁移。Grab 内部大量基础库是用 Go 编写的,例如一个使用 Go Templates 进行配置管理的库。在 Rust 项目中,这些 Go 库无法直接复用,团队不得不使用 nom 解析器组合库在 Rust 中重写了类似的功能。这无疑增加了重构的成本和时间。
开发体验的差异
Go 的设计哲学之一就是“简单”,这使得开发者能够快速上手并高效迭代。Goroutine 和 Channel 的易用性,让并发编程的门槛大大降低。相比之下,Rust 为了安全和性能,在语言层面引入了更多复杂性,需要开发者投入更多精力去理解和驾驭。
“Rust 一定比 Go 快得多”是迷思
一个非常重要的发现是,Grab 明确指出:“神话 1:Rust 非常快!比 Golang 更快!判定:被驳斥。Golang 对于大多数使用案例来说“足够快”……仅仅为了性能提升而将 Golang 服务重写为 Rust 不太可能带来显著的好处。”
在 P99 延迟方面,Rust 版本与 Go 版本表现相当,甚至有时略差。这告诉我们,Go 在其设计领域内性能已经足够优秀,单纯为了追求“更极致”的速度而用 Rust 重写 Go 服务,可能并不能带来预期的巨大性能提升,反而可能因为生态、开发效率等问题得不偿失。Grab 的主要收益点在于显著的 资源效率 提升。
Gopher 何去何从?几点思考
Grab 的案例无疑是 Go 社区的一面镜子,它照见了 Go 的优势,也揭示了在特定场景下可能存在的“天花板”。作为 Gopher,我们应如何看待这个案例,并从中吸取经验呢?
首先,Go 的核心优势依然稳固。Go 语言以其简洁性、强大的并发模型(Goroutine + Channel)、高效的编译速度、完善的工具链以及成熟的生态系统,继续在云原生、微服务、中间件和 DevOps 工具等领域占据首选或极具竞争力的地位。对于绝大多数业务场景,Go 提供的开发效率和运行性能是“足够好”的,且具有高性价比。
其次,关于何时考虑使用 Rust 进行“动刀”,Grab 的案例提供了几个关键的决策参考点。
在面对极高的 QPS 和资源消耗时,如果服务本身成为性能瓶颈且占用了大量服务器资源,那么迁移可能是合适的。
当功能相对简单且逻辑内聚时,重写的复杂度较低,易于验证,这样可以避免对复杂业务系统进行大规模重写。
当基础设施成本成为显著负担,优化能带来巨大的商业价值时,也应考虑使用 Rust。
团队必须具备掌握 Rust 的能力,成员需熟悉 Rust 并愿意投入时间和资源进行团队赋能。在不满足这些前提条件的情况下,盲目追求 Rust 可能弊大于利。
再者,在考虑语言迁移之前,我们应充分挖掘 Go 本身的优化潜力。例如,进行代码层面的性能分析与优化、架构调整、选择更优的 Go 库,甚至是通过 Go 版本升级带来的 GC 改进等。重写通常应视为最后的手段。
关于 Gopher 是否需要拥抱 Rust,这取决于个人的发展方向和兴趣。如果你专注于业务开发和应用层构建,Go 依然能让你游刃有余。但如果你对系统编程、底层优化、嵌入式或游戏引擎等领域感兴趣,或者所在的公司/团队正在引入 Rust,那么学习 Rust 无疑会为你打开一扇新的大门。即使不深入学习,了解 Rust 的核心理念(如所有权、生命周期和无GC)也能帮助我们更好地理解程序运行的本质,从而写出更健壮、更高效的 Go 代码。
最后,Go 语言的未来同样值得关注。Go 社区在持续进化,例如对泛型的支持提升了表达力,而持续优化的 GC 以及不断丰富的高性能标准库也在不断减少对性能的影响。未来,Go 是否会在某些方面借鉴其他语言的优秀特性,以保持其核心优势的同时,进一步拓展能力边界,值得我们期待。
小结
Grab 用 Rust 重写 Go 服务的案例,再次印证了技术选型中“没有银弹,只有取舍”的黄金法则。Rust 以其极致的性能和资源控制能力,在特定场景下展现了巨大的潜力。但这并不意味着 Go 已经过时或不再优秀。
对于我们 Gopher 而言,重要的是理解不同语言的设计哲学、优势与代价,并根据具体的业务场景、团队能力和长远目标,做出最适合的决策。
你对 Grab 的这个案例有什么看法?你认为在哪些场景下,用 Rust 替代 Go 是值得考虑的?欢迎在评论区留下你的思考!
原文链接:https://engineering.grab.com/counter-service-how-we-rewrote-it-in-rust
点击下面标题,阅读更多干货!
- 百万行依赖的“恐惧”:一位Rust开发者的深度反思与Go的启示
- RedMonk最新排行出炉:Go语言稳居Top 12,AI冲击下Stack Overflow权重生变?
- 解构Go并发之核,与Dmitry Vyukov共探Go调度艺术
- 【征服Go并发测试】01 并发测试的“噩梦”:为何你的 Go 测试如此脆弱与缓慢?
🔥 你的Go技能,是否也卡在了“熟练”到“精通”的瓶颈期?
想写出更地道、更健壮的Go代码,却总在细节上踩坑?
渴望提升软件设计能力,驾驭复杂Go项目却缺乏章法?
想打造生产级的Go服务,却在工程化实践中屡屡受挫?
继《Go语言第一课》后,我的 《Go语言进阶课》 终于在极客时间与大家见面了!
我的全新极客时间专栏 《Tony Bai·Go语言进阶课》 就是为这样的你量身打造!30+讲硬核内容,带你夯实语法认知,提升设计思维,锻造工程实践能力,更有实战项目串讲。
目标只有一个:助你完成从“Go熟练工”到“Go专家”的蜕变! 现在就加入,让你的Go技能再上一个新台阶!


被折叠的 条评论
为什么被折叠?



