为什么选 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 更合适。

浙公网安备 33010602011771号