Go 写业务,Rust 扛底盘:一套可落地的混合架构
"Go 还是 Rust"吵了十年,大多停留在语言层面互喷,产不出架构决策。工程上更有价值的问法是三段论:**两者的成本模型差在哪?边界应该画在哪?用什么方式接起来?**本文按这三段展开:先把 Go 的 GC 税与 Rust 的优势边界算成账(含"怎么量自己系统里的这笔税"),再给出"变化频率 × 延迟敏感度"的切分准则,然后把四种集成模式(进程边界 / sidecar / FFI / WASM)按集成成本排序逐一拆解------每种都给到代价清单与适用判据,最后落到一个真实样本:用 Rust 实现的中后台平台(RushWind Admin)与 Go 业务服务如何共享同一份 proto 契约协同。反例与不适用场景一并摆出来,量化表述尽量给可核对的量级与出处。
一、先承认各自的好,把争论拉回成本模型
两个语言各自的优势清单,先不吵架地摆一遍:
Go 的强项:
- 编译秒级,改一行十秒内看到结果,业务迭代的反馈回路极短;
- 产物是单个静态二进制,部署等于复制文件,交叉编译一个环境变量的事;
- goroutine + netpoller 的心智模型极其省事:每个连接一个 goroutine、阻塞式写法,调度器兜底------IO 密集服务的工程效率天花板;
- 生态厚:数据库驱动、中间件客户端、可观测性组件、K8s 周边,几乎都是一等公民;
- 团队可替换性强,招人与交接成本低,pprof / trace 工具链开箱即用。
Rust 的强项:
- 无 GC:内存回收在编译期安排妥当,运行时没有停顿窗口,也没有"GC 周期的 CPU 波动";
- 零成本抽象:迭代器、泛型单态化、async 状态机,抽象不付运行时税;
- 内存密度高:常驻内存约等于活数据加分配器余量,没有 GC 要求的堆余量;
- 内存安全:数据竞争在编译期报错,整类内存漏洞不存在;
- 对 CPU 密集负载的火力:SIMD、数据并行、直接调用原生库无语言隔阂。
关键论点在这里:这两张清单根本不在同一个维度上竞争 。Go 的清单本质是"变更成本低",Rust 的清单本质是"运行时成本低"。于是问题从"哪个语言好"变成:你的工作负载,变更成本和运行时成本哪个占大头?
- 业务编排、CRUD、报表、流程------模型天天变,一年改三百次。这类代码的瓶颈是人的时间,运行时那点税折算成钱可以忽略。→ 变更成本主导,Go 赢。
- 鉴权、限流、网关、序列化密集的读写面、大扇出推送------契约十年难得一变,却对每个请求、每毫秒计税。→ 运行时成本主导,Rust 赢。
所以混合架构的地基只有一句:按工作负载选语言,而不是按信仰选语言。下面把两笔账算细------算细了,边界自然浮出来。
二、Go 的账:GC 是延迟与 CPU 的双重税
先把话说公道:Go 的 GC 是并发三色标记清扫,STW 停顿通常在亚毫秒级(官方 GC guide 的口径),goroutine 初始栈只有 2KB,1.14 起还有异步抢占,调度器对 IO 密集负载的处理是工业级的。多数业务服务完全不需要为 GC 焦虑。
但"通常亚毫秒"不是税的全部。GC 的真实代价分三层:
税一:分配税(mark assist) 。标记阶段,分配越猛的 goroutine 越会被拉去帮忙标记------这是延迟直接落在业务 goroutine 上的机制:你这一批请求分配的对象多,你这一批请求就慢,GC guide 明说这是设计取舍。后果是:Go 服务的尾延迟不只取决于你的代码,还取决于你的分配模式------一条意外的大对象列表序列化,能把同进程的无关请求一起拖进 P99。
税二:堆放大 。默认 GOGC=100 下,堆目标约为活数据的 2 倍(heap goal = live × (1 + GOGC/100)),这是用内存换吞吐的默认交易。放进容器就得面对内存限额:调低 GOGC 换 CPU、设 GOMEMLIMIT(1.19+)防 OOM 但回收周期更频繁------内存税和 CPU 税可以互相转移,但不能消灭。三个旋钮(GOGC、GOMEMLIMIT、实例数)拧哪个,是每个 Go 团队迟早要上的一课。
税三:CPU 占用与 RSS 密度 。分配密集的负载下,GC 本身的 CPU 占用可以到两位数百分比(具体取决于分配率与活堆大小,runtime.MemStats.GCCPUFraction 可以直接读出来);RSS 上,真实业务服务几十到几百 MB 常见------GC 需要余量,而余量按峰值活堆算。单机部署时无感,"每个业务系统都部署一份"的平台服务会被乘以副本数。
调度器的功劳也要记上:netpoller 让"每连接一个 goroutine"成为免费午餐,2KB 起步、按需增长的栈让百万 goroutine 不再是噱头。IO 密集、分配平缓的服务,Go 的运行时几乎不需要你操心------这是它工程效率的一部分,别在赞美 Rust 的时候忘了它。
公开案例方面,Discord 从 Go 迁 Rust 的复盘值得一读:他们的读状态服务每两分钟被 GC 周期打出一波延迟尖刺,最终整个服务用 Rust 重写。这个案例常被 Rust 派引用,但它的正确读法是:那是每请求都要做状态查询的高频数据面,GC 周期性尖刺直接打穿尾延迟预算------是工作负载命中了 GC 的痛处,不是"Go 不行"。
怎么量自己系统里的这笔税(动手迁移前先测量,这是本文反复出现的纪律):
GODEBUG=gctrace=1看 GC 周期频率、停顿时长、堆目标;runtime.MemStats.GCCPUFraction看 GC 吃掉的 CPU 比例;- pprof 的 alloc_space / inuse_space 看分配热点------分配税大的地方,往往也是 Rust 化收益最大的地方。
结论:IO 密集、分配平缓、变更频繁的业务层,留在 Go 是理性决策。但当某个服务满足以下任意一条时,账就开始倒向另一边:尾延迟进了 SLO、内存成本随副本数失控、GCCPUFraction 经测量显著、负载是 CPU 密集的纯计算。
三、Rust 的账:为可预测性与密度付费
无 GC 的直接推论是尾延迟可预算。没有停顿窗口、没有标记辅助,P99.9 由内核、锁与分配器决定------都是可以 profile、可以调的东西(分配器还能整体换:jemalloc / mimalloc 即插即用),而不是"取决于这批请求谁分配得多"。对每请求都要做权限判定、令牌校验的服务,这一点直接转化为 SLO 的置信度。
零成本抽象是可以算到字节的 。迭代器链折叠成手写循环的性能;泛型单态化后没有虚表开销;async fn 编译成状态机,tokio 的一个 task 常驻开销在几百字节量级------百万并发连接的内存账算得过来,且不需要 GC 周期来打扫。
内存密度是钱,而且算得出来 。经验量级上,同业务形态的服务 Rust 的 RSS 比 Go 低 3~10 倍(取决于负载与 GC 配置,别当成精确承诺)。做个假想算例:一个平台服务被 50 个业务系统各部署 2 个副本,Go 实现 150MB、Rust 实现 15MB------集群里这一块的常驻差异就是 13.5GB,还没算 GC 余量随峰值活堆涨的部分。
CPU 密集火力 :memchr、simd-json 这类 SIMD 库在解析类负载上是数倍于朴素实现的吞吐;rayon 一行代码把数据并行铺满所有核;zstd、加密、图像处理这些原生库直接调用;TechEmpower 吞吐榜单的头部常年被 Rust 框架占据------定性的话:纯计算与序列化密集的负载,Rust 的上限显著更高。
但代价清单同样真实,一条都别假装看不见:
- 借用检查器与生命周期标注,让"改模型"这件事的成本显著高于 Go------业务逻辑三天一变的团队会被磨到怀疑人生;
- 编译时间以分钟计,迭代反馈回路长一个量级;
- 中后台常见的 CRUD 生态(ORM、后台框架、管理端生成)远不如 Go 厚------好在本文给出的路线里,Rust 侧不需要天天写新 CRUD;
- 招人面窄,代码 review 的门槛真实存在;
- 一两个人写就的 Rust 服务,接手成本高于同规模 Go 服务------文档与测试要更自觉。
所以 Rust 的正确打开方式不是"业务长在它身上",而是"业务长在别人身上,它扛住底盘"------底盘的定义下一节给。
四、边界画在哪:变化频率 × 延迟敏感度
把系统的每个模块放到两个轴上:契约变化频率 (横轴)与延迟/CPU 敏感度(纵轴):
markdown
延迟/CPU 敏感
▲
│ ② 平台层(Rust 岛) ③ 极端热点(FFI/WASM 优化)
│ 鉴权、限流、审计、 个别热函数:压缩、
│ 网关、推送扇出 批量加解密、解析
│
│ ① 常规基础设施(Go 足矣) ④ 业务层(Go 主场)
│ 配置、通知、轻量任务 编排、CRUD、报表、流程
└────────────────────────────────────▶ 契约变化频率
稳定 多变
四个象限四种待遇:④ 业务层 是 Go 主场没有争议;① 常规基础设施 没到值得换语言的量级;③ 极端热点 不值得换整个服务,用 FFI 或 WASM 只优化那一个函数(第五节展开);真正值得圈成"Rust 岛"的是 ② 平台层。
为什么平台层是最理想的 Rust 候选?它同时满足四个条件,缺一都不值:
- 每请求热路径 :鉴权、租户检查、权限判定发生在每一次 API 调用上。它的延迟是全系统延迟的乘数------平台层慢 5ms,所有业务全慢 5ms;平台层的 CPU 占比,是全系统 QPS 的线性函数;
- 契约稳定:权限模型、组织模型、审计模型的演化以年计,Rust 的变更摩擦在这个区域几乎不被触发------你避开它唯一的弱点,只取它的强项;
- 安全敏感:凭证、密钥、审计------内存安全在这里的性价比最高,一个越界读的价值是负的 CVE 与一次合规事故;
- 写放大与扇出:审计批量落库、在线用户推送、任务队列扫描------内存密度与吞吐的主场。
顺理成章的推论:与其在业务进程里嵌 Rust 库,不如把整个平台域做成独立的 Rust 服务------管理后台、认证授权、租户、审计这一整块,对几乎所有的业务系统都是"要写、难写、写完不产生差异化价值"的部分。它就是那个天然应当存在于语言边界另一侧的东西。
五、四种接法,按集成成本排序
模式一:进程边界 + 共享 proto 契约(默认推荐)
Go 业务服务与 Rust 平台服务各自独立进程,proto 是唯一契约:路由、错误模型、分页语义全部进契约,两侧各自生成桩代码。
markdown
┌─────────────┐ ┌─────────────┐ ┌─────────────┐
│ React / Vue │────▶│ Go 业务服务 │────▶│ Rust 平台服务 │
│ 前端(任意) │ │ 编排/CRUD │ │ 鉴权/租户/RBAC│
└─────────────┘ └──────┬──────┘ │ 审计/任务/推送 │
│ └─────────────┘
┌──────┴──────┐
│ 业务数据库 │ ← 平台数据与业务数据分库
└─────────────┘
工程治理是这个模式的生命线,四件套缺一不可:
- 契约文件独立目录 + 哈希门 :契约不许手改,CI 校验哈希(RushWind Admin 的做法是 MANIFEST 清单 +
--check门); - 两侧都生成,都不手写:Go 侧生成桩与客户端,Rust 侧生成路由与接口 trait,DTO 永远不从契约"翻译"过来------翻译必有漏;
- 金样锁定序列化语义:64 位整数在 JSON 里的字符串化、proto3 presence 未设置时的省略行为、well-known 类型的时间戳格式------跨语言互操作 90% 的事故出在这几处,用金样测试钉死;
- 差分回归:同一份契约的双栈实现全路由逐字节比对(RushWind Admin 的台架对 203 条路由 + 89 条 HEAD 自动 sweep,豁免显式登记),换语言、升级依赖、重构生成器都有回归兜底。
通信语义要提前约定,这是新手混合架构翻车的高发区:
- 超时与截止传播:上游传来的 deadline 要穿透到对 Rust 服务的调用上,别让平台层变成无超时的悬挂点;
- 幂等与重试:GET 天然幂等随便重试;写操作要么幂等令牌(Idempotency-Key),要么服务端按业务键去重------跨了进程边界,重试是常态而非异常;
- 契约只加不改:字段只新增、语义只收窄,废弃走标记期;两侧发版节奏不同,任何"破坏式顺手改"都会变成两侧联调事故;
- 数据所有权 :平台数据归平台库、业务数据归业务库,跨域只引用 ID(用户 ID、租户 ID),不跨库 join、不共享表------这是进程边界在数据层的投影,破了这条,边界就白画了;
- 可观测性先统一 :W3C
traceparent头透传让两个运行时的 span 拼在同一条链上;日志字段名、错误信封、指标标签提前对齐,否则排障时两条日志各说各话。
运行期的一致性同样落在契约上:错误信封统一四字段(code / reason / message / metadata),Go 侧消费者不用猜 Rust 侧的错误形状;认证用 RS256 非对称签名------业务服务只需要公钥就能本地验签,不必每请求回查平台。
代价 :一跳网络(同机房亚毫秒)、两套运维、契约演进要有流程。买到:独立部署与扩缩容、故障隔离(平台挂了业务还能降级跑)、两种语言各自的发布节奏。
变体------前端零改动验证:这个模式成熟度有个便宜的试金石------三套前端(React / Vue Element / Vue Vben)共享同一份生成客户端,后端在两个语言栈之间切换,前端一行不改。RushWind Admin 正是这样验证契约兼容的。
模式二:Sidecar / 数据面下沉
把鉴权校验、限流、审计采样做成业务进程旁边的 sidecar:请求先过 sidecar 完成令牌校验与身份注入,业务进程拿到的请求已带可信身份头,业务代码不再关心认证。
两种形态按需选:每 pod 一个 sidecar (隔离彻底,资源 ×副本数)或共享网关层(资源省,但成了所有流量的单点,要自己做高可用)。限流、审计这类"按实例计量"的逻辑适合前者,鉴权这种"验完就放行"的适合后者。
存在性证明很硬:linkerd2-proxy(Linkerd 的数据面)是纯 Rust 写的,扛着生产环境的网格流量;Cloudflare 的 Pingora 同样是 Rust 代理框架。Rust 在"每包都要过一遍"的数据面上,是经过大规模生产验证的选择。
取舍:sidecar 的资源乘以每个 pod;本地回环那一跳的延迟要算进预算;sidecar 升级与业务发布的编排有运维复杂度。适合网格化部署、或平台层要以基础设施形态(而非业务服务形态)存在的团队。
模式三:FFI(cgo + cdylib):用得其所,别上头
当热点收敛在单个函数(压缩、批量加解密、SIMD 解析),不值得为此拆服务,可以让 Go 直接调 Rust 编译的动态库。Rust 侧暴露 C ABI:
rust
// 编译目标 cdylib;panic 不得跨 FFI 边界,边界处统一收敛为错误码
#[no_mangle]
pub extern "C" fn hl_compress(
src: *const u8, src_len: usize,
dst: *mut u8, dst_cap: usize,
) -> isize {
std::panic::catch_unwind(|| unsafe {
let input = std::slice::from_raw_parts(src, src_len);
let out = std::slice::from_raw_parts_mut(dst, dst_cap);
zstd_compress(input, out, 3) // 返回写入长度
})
.unwrap_or(-1)
}
Go 侧经 cgo 调用:
go
/*
#cgo LDFLAGS: -L${SRCDIR}/libs -lhotlib
#include <stddef.h>
extern long long hl_compress(const void*, size_t, void*, size_t);
*/
import "C"
func Compress(src []byte) ([]byte, error) {
dst := make([]byte, len(src)+len(src)/8+64)
n := C.hl_compress(unsafe.Pointer(&src[0]), C.size_t(len(src)),
unsafe.Pointer(&dst[0]), C.size_t(len(dst)))
if n < 0 {
return nil, errors.New("compress failed")
}
return dst[:n], nil
}
动手之前把这份代价清单看完:
- 固定开销 :单次 cgo 往返在百纳秒量级;一旦发生线程迁移或需要回调进 Go,进入微秒量级。结论:低频大载荷才划算------一次调用处理 100KB 数据,百纳秒的开销忽略不计;循环里每元素调一次,开销就是主角;
- 线程占用:goroutine 进入 C 调用会占住一个 OS 线程,C 侧阻塞期间这个线程不能服务其他 goroutine------并发预算要重算;
- panic 即 UB :Rust panic 跨过 FFI 边界是未定义行为,边界处必须
catch_unwind收敛为错误码(上面代码里那一层不是装饰); - 内存所有权:谁分配谁释放,分配器不能跨界 free------要么调用方提供缓冲区(如上例),要么导出成对的 alloc/free;
- 构建矩阵 :每个目标平台配 C 工具链,
CGO_ENABLED=0的纯静态构建路径直接失效,交叉编译复杂度上一个台阶。
决策算术 给一个例子:导出报表前要压缩一批 10MB 的 JSON,Go 侧现有库吞吐 x、Rust 原生 zstd 吞吐约数倍------每请求节约几十毫秒,cgo 固定开销百纳秒可忽略,构建矩阵一次性成本,值 。反过来,为了"列表页快 1ms"在循环里逐元素 FFI,固定开销乘上调用次数反而更慢------不值 。判据一句话:有 profile 证据的 CPU 热叶子,且调用粒度粗到固定开销可摊。没有火焰图证据,不要用 FFI"优化"任何东西。
模式四:Go 宿主 + Rust WASM 插件
如果需要的是租户自定义逻辑、风控规则、UDF 这类"会变的逻辑"跑在 Go 宿主里,WASM 是比 FFI 干净得多的边界:Rust 编译到 wasm32-wasip1,Go 用 wazero 加载------纯 Go 运行时,零 cgo、零外部依赖,还自带沙箱(插件崩溃不伤宿主、内存访问被限制在模块内)。
go
// 示意:热加载一个 Rust 编译的风控打分模块
r := wazero.NewRuntime(ctx)
mod, _ := r.Instantiate(ctx, riskModuleWasm)
res, _ := r.ExportedFunction("score").Call(ctx, uint64(ptr), uint64(len))
和 FFI 相比,WASM 边界多买到三样东西:沙箱 (模块碰不到宿主内存之外的任何东西)、热更新 (换模块不重启宿主)、平台无关 (同一个 wasm 模块在 Linux/Windows/arm 上行为一致)。网络代理生态已经把这条路标准化了:proxy-wasm ABI 是 Envoy / Istio 生态的插件既成标准,Rust SDK 成熟。
取舍:计算性能接近原生,但宿主与模块之间的调用按次计费,内存传递要走线性内存拷贝------调用粒度要设计得粗(传批量数据进去一次算完,而不是逐条来回),否则每条风控规则一次跨界调用的开销就是主角。
模式五(一句话带过):共享内存 / 环形缓冲
mmap + SPSC 环形缓冲、io_uring 这类零拷贝通道是延迟的终极形态,但两侧都要处理内存序、生命周期与崩溃恢复,运维成本只有网关级产品才摊得平。业务系统知道了就好,别 first try。
六、实战样本:RushWind Admin 作为 Go 技术栈的平台层
把前四节的论点落到一个具体的东西上。RushWind Admin 是一个用 Rust 实现的企业级中后台平台:组织、用户、RBAC、多租户、认证(验证码 / 限流 / 登录策略 / TOTP MFA / AK-SK)、六类审计流、任务队列 + cron、SSE 推送、文件服务、Lua/JS 脚本系统,底座是 axum + SeaORM + PostgreSQL/Redis 的 RushWind 框架。一次启动拉起四个服务面:REST 网关(:7788)、SSE 推送网关(:7789)、任务队列 worker 与 cron 生产者,共享生命周期、配置驱动装配。
先看一眼它跑起来的样子。仪表盘的用户 / 角色 / 登录与操作审计实时统计,来自真实运行环境:

在线会话视图------SSE 推送通道的可见面,支持强制下线:

它恰好是第四节"② 平台层"象限的完整标本:
每请求热路径:203 条路由条条过四阶段门------
- 认证:RS256 验签 + 会话白名单核对(登出即吊销),失败 401;
- 租户:租户存在性、状态、套餐有效期(到期自动只读:写拒绝、读放行),失败 403;
- 授权 :
(路径模板, 方法)查接口表 fail-closed(未登记路由默认拒绝)→ 动态 RBAC(角色---权限---接口映射存库、变更热生效),失败 403,且每次判定落策略评估日志; - 注入:claims(租户、数据范围、角色)进请求上下文,下游处理器免回查。
这四步没有任何一步"沾业务",却是每个请求的固定开销------乘数属性拉满。
契约稳定:权限与组织模型十年难变,Rust 的变更摩擦在此区几乎不触发;而契约面被构建期生成器(112 proto → 203 路由、441 条错误映射、198 个接口)与 Go / Rust 双栈差分台架两头钉死。
安全敏感:口令 bcrypt + 恒时假校验防枚举、AES 应用层加密传输、TOTP 密钥加密存储、六类审计流留痕(登录风控加权评分分档)------内存安全直接缩小这类模块的漏洞面。
写放大与扇出:审计批量落库、SSE hub 按用户严格过滤后扇出、Postgres 任务队列的批量认领(指数退避 + 死信)------内存密度与尾延迟的主场。
Go 业务服务怎么接?最薄的一层就够了------本地验签中间件,约二十行:
go
func AuthGate(pub *rsa.PublicKey, next http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
raw, ok := bearer(r) // Authorization: Bearer <token>
if !ok {
writeStatusError(w, 401, "UNAUTHORIZED", "missing bearer token")
return
}
claims, err := verifyRS256(raw, pub) // 本地验签,不出进程
if err != nil {
// 错误信封与平台侧同形:四字段 protojson
writeStatusError(w, 401, "UNAUTHORIZED", "access token expired")
return
}
next.ServeHTTP(w, r.WithContext(withClaims(r.Context(), claims)))
})
}
JWT 里携带租户、数据范围、角色等业务声明,下游服务不回查就能做本地粗判;细粒度判定(数据范围过滤、字段黑名单裁剪)由平台在它的数据面上完成。管理操作(建用户、改角色、查审计)走 HTTP 调平台 REST 面,错误信封与业务服务自己的响应同构,前端的错误处理逻辑一套通吃。
把第 2、3、4 节的账对着这个样本核一遍:
| 前文论点 | 样本里的兑现 |
|---|---|
| 平台层是每请求乘数 | 四阶段门在 203 条路由上逐条执行,无一豁免(免鉴权白名单同样由契约生成、显式登记) |
| 契约稳定,变更摩擦不触发 | 生成器 + 金样 + 差分台架把契约钉死,平台域按年演化 |
| 内存安全的性价比 | bcrypt、AES、TOTP、审计六流------编译期挡掉整类内存漏洞 |
| 密度是钱 | 四个服务面单进程拉起,平台服务被每个业务系统各部署一份时按副本数省 |
这个样本顺带回答了一个常见质疑:"为个管理后台引入 Rust,值得吗?"------值得的从来不是"后台"本身,而是每个业务系统都要重复购买的那部分平台能力(认证、权限、租户、审计)从此变成一个可独立部署、尾延迟可预算、内存占用个位数十位数 MB 的服务,且前端与业务侧感知不到它的实现语言。
七、反模式与成本清单(诚实章)
三个反模式:
- 为了"快"上 FFI,却拿不出 profile 证据。cgo 的固定开销与构建复杂度是真实成本,没有火焰图支撑的 FFI 是负优化;
- 双栈运行,契约靠人肉同步。两侧 DTO 手工对齐,漂移只是时间问题------契约不进版本化管线、不做生成、没有差分回归的"混合架构"是事故温床;
- 一刀切全公司换语言。业务层迁 Rust 的变更成本没有回报,见第二节的账。
成本清单(混合架构的固定开销,立项前算清):
- 两套工具链与 CI:构建、测试、镜像、发布管线 ×2;
- 两拨技能池:招人、排班、on-call 都要覆盖两个运行时;
- 契约治理的持续投入:同步门、金样、差分台架的一次性建设成本不高,但纪律要长期维持;
- 排障跨进程:一条 trace 穿两个语言栈,日志与指标要提前统一(traceparent、统一错误信封、统一日志字段)。
落地路线图(渐进式,每一步独立可回退):
- 先把契约治起来------无论换不换语言,proto 单一事实源 + 生成 + 契约测试都稳赚,这是后面一切的地基;
- 鉴权与限流下沉------先以 sidecar 或独立服务把"每请求都过"的逻辑收走,业务进程拿到干净身份,这步用 Go 自己写都值;
- 平台域整体迁 Rust------按第四节清单选中的那个域,整个服务用 Rust 重写,靠第一步的契约与差分台架保证前端零感知;
- 个别热点 FFI/WASM------只有当第 3 步之后仍有 profile 证据的进程内热点时才考虑。
顺序反过来(先 FFI 炫技、契约后补)是大多数混合架构失败故事的开头。
什么时候别拆 :低 QPS 内部系统、五人以下团队、PMF 之前的探索期------纯 Go 是这些场景的最优解,混合架构对它们是纯粹的开销。什么时候必须拆:尾延迟进 SLO、内存成本随副本数失控、平台域要被多条业务线复用、或安全合规对平台层提出了内存安全级别的要求。
八、决策表
| 场景 | 建议 |
|---|---|
| CRUD 为主、QPS 温和、团队小 | 纯 Go,别拆 |
| 尾延迟进 SLO(P99.9 有预算) | 热路径下沉 Rust,进程边界(模式一) |
| 每请求都要过的平台逻辑(鉴权/限流/审计) | 独立 Rust 平台服务或 sidecar(模式一/二) |
| 有 profile 证据的 CPU 热叶子 | FFI(模式三),低频大载荷 |
| 租户自定义逻辑/风控规则,要沙箱与热更 | Go 宿主 + Rust WASM(模式四) |
| 平台域(认证/权限/租户/审计)被多业务线复用 | 独立 Rust 平台服务------RushWind Admin 这类 |
九、收拢
回到开头的三段论,答案收拢成三句话:成本模型决定语言分工 (变更成本主导的给 Go,运行时成本主导的给 Rust);"变化频率 × 延迟敏感度"决定边界 (平台层是第一个该圈出去的 Rust 岛);契约决定成败(proto 单一事实源 + 两侧生成 + 金样 + 差分回归,四件套齐了,混合架构才从论文变成工程)。
Go 与 Rust 从来不是二选一。真正的问题只有一个:你的边界画在哪,契约怎么钉。
相关阅读:
- RushWind Admin --- 用 Rust 写的企业级中后台,开源了------本文实战样本的全貌
- RushWind Admin --- 契约驱动:203 条路由零手写的工程化拆解------模式一"契约治理四件套"的完整落地
- 项目地址:Gitee | GitHub
- 框架底座:Gitee | GitHub