为什么选 Go 不选 Rust?宝塔的技术选型逻辑

宝塔 13.1.0 做了一件事:把后台任务引擎 BT-Task 从 Python 换成了 Go。

换完之后,数据很漂亮:CPU 均值降了 44.5%,P95 峰值降了 58.3%,文件描述符降了 65.6%。

然后有人问我:既然都要换语言,为什么选 Go 不选 Rust?Rust 不是更快吗?

这个问题,我一开始也想过。今天这篇,从技术选型的角度聊聊宝塔的逻辑。


先看结果:Go 带来了什么

换 Go 之前,先看看 Python 的表现:

指标 Python 问题
CPU 均值 1.20% 后台任务占了不少 CPU
P95 峰值 1.35% 偶尔飙高,导致网站卡顿
内存 RSS 14.4 MB 常驻内存占用不低
文件描述符 20.9 资源利用率偏低

换 Go 之后:

指标 Go 降幅
CPU 均值 0.64% -44.5%
P95 峰值 0.77% -58.3%
内存 RSS 12.5 MB -11.8%
文件描述符 7.2 -65.6%

这个成绩已经不错了。但 Rust 能做得更好吗?


理论上,Rust 确实更快

如果只看性能,Rust 在大多数场景下确实比 Go 更快:

  • CPU 占用 — Rust 通常比 Go 低 10-20%
  • 内存占用 — Rust 通常比 Go 低 30-50%
  • 启动速度 — Rust 编译后的二进制文件启动更快

但性能不是唯一的考量标准。


为什么不选 Rust?四个原因

原因一:开发效率

Go 的语法简单,团队上手快。一个有 Python 经验的开发者,学 Go 大概需要两周就能写出生产级代码。

Rust 呢?所有权、借用检查、生命周期——这些概念对习惯了 Python/Java 的开发者来说,学习曲线很陡。

对于一个要频繁迭代的产品来说,开发效率比极致的性能更重要。

原因二:编译速度

Go 的编译速度非常快。一个中型项目编译只要几秒钟。

Rust 的编译速度……用过的人都懂。特别是项目大了之后,编译时间以分钟计。

对于宝塔这种需要频繁发布更新的场景,编译慢意味着迭代周期长。今天改个 bug,明天才能发版——这个节奏太慢了。

原因三:生态成熟度

在服务器运维这个领域,Go 的生态已经很成熟了。

  • 网络库:net/http 标准库就够用了
  • 数据库驱动:MySQL、PostgreSQL、Redis 都有成熟的库
  • 监控指标:Prometheus client 开箱即用
  • 日志处理:zap、logrus 生态丰富

Rust 的生态虽然在快速发展,但在运维领域的成熟度还是不如 Go。很多场景下,Go 有现成的库,Rust 可能需要自己写。

原因四:够用了

这是最重要的一点。

Go 的性能已经满足了宝塔的需求——CPU 降了 44.5%,P95 降了 58.3%,文件描述符降了 65.6%。

Rust 可能能再降 10-20%,但边际收益递减。从 Python 到 Go 的 44.5% 降幅是质的飞跃,从 Go 到 Rust 的 10-20% 只是量的优化。

而付出的代价是:开发周期长、维护成本高、迭代速度慢。

这笔账不划算。


一个有趣的数据:线程数

换 Go 之后,线程数从 6 变成了 14,增加了 133%。

但 CPU 反而降了 44.5%。

为什么线程多了,CPU 反而少了?

因为 Go 用的是 goroutine,不是系统线程。

  • 系统线程:每个占用 1-2 MB 内存,切换开销大
  • goroutine:每个只占 2 KB,由 Go 运行时调度,切换开销极小

所以 Go 可以同时跑十几个 goroutine,而 Python 只能跑 6 个线程。goroutine 让后台任务可以高效并发——漏洞扫描、基线检查、日志清理、证书续签,这些任务可以并行跑,不用排队等。

虽然线程数多了,但每个 goroutine 更轻量,调度更高效,CPU 切换开销更小。结果就是:线程多了,CPU 反而少了。

这个现象,用 Rust 的线程模型反而做不到——Rust 的线程也是系统线程,不是轻量级协程。


实际运行稳定性

换了语言之后,稳定性怎么样?

我看了两组数据:

  • 两次 Python 版本之间的漂移:6%
  • 两次 Go 版本之间的漂移:14%

Go 的波动略大一些,但在可接受范围内。对于生产环境来说,14% 的漂移完全没问题。

而且 Go 的 P95 峰值降幅(58.3%)远大于均值降幅(44.5%),说明 Go 在高峰期的表现更稳定。"偶尔卡顿"的情况少了,这对用户体验来说更重要。


宝塔的技术债

其实宝塔早就该换 Go 了。

面板本身是 Python 写的,后台任务一直跟着面板跑。面板越来越大,后台任务的资源占用也越来越明显。

为什么现在才换?

第一,以前没到非换不可的时候。 服务器数量少的时候,后台任务占的那点 CPU 感知不到。

第二,换语言有风险。 重写一遍,要测试、要验证、要确保兼容性。

第三,现在到了临界点。 用户服务器多了,后台任务的资源占用开始被感知了。而且 Go 的生态也成熟了,有了足够的信心来重写。

所以 13.1.0 做了这个决定。


Go 之后,下一个会是什么?

BT-Task 换了 Go 之后,面板本身还是 Python 写的。

未来会不会把面板也换成 Go?

我觉得短期不会。原因很简单:

  • 面板的业务逻辑复杂,重写成本高
  • Python 在面板这个场景下性能瓶颈不明显
  • 面板的迭代速度比后台任务快得多,换语言风险大

但如果面板的资源占用开始成为问题,换 Go 是一个选项。

或者,更现实的路径是:把面板里资源密集的部分(比如文件管理、数据库管理)逐步用 Go 重写,做成独立的服务。


写在最后

技术选型不是选"最快的",而是选"最合适的"。

Rust 更快,但开发慢、编译慢、生态不够成熟。

Go 不是最快的,但性能够用、开发快、生态好。

对于一个要服务百万用户、频繁迭代的产品来说,Go 是更务实的选择。

不是 Rust 不好,是 Go 更合适。


posted @ 2026-09-24 17:43  Bacon大王  阅读(4)  评论(0)    收藏  举报