Workstation 技术简报:为什么 Rust 仍是许多现代应用(API、边缘、智能体、CI/CD 平台)的强默认选择,以及如何在异步运行时旁正确放置 CPU-bound 工作。我们从 Alice Ryhl 的文章 Async: What is blocking? 获得启发(非逐字转载),尤其是 Rayon 相关指导。配套:博客 · 证据:Polyglot Benchmarks · 相关:多语言文章。
- 阻塞(异步含义):阻止运行时切换任务——通常是长时间不触及
.await。 - CPU-bound 解法:优先 Rayon(或专用线程),不要把重计算塞进 Tokio worker。
- IO-bound 同步库:
tokio::task::spawn_blocking通常是正确的池。 - 多语言:Rust 是同行中的强工具之一——用 Polyglot Benchmarks 测量后再强制选型。
- 致谢:概念框架受 Alice Ryhl / ryhl.io 启发;Workstation 将其落到平台工程。
1. 生产环境中真正重要的 Rust 优势
现代应用组合很少是单一语言。但某些表面特别奖励 Rust 的特性:
- 无 GC 抖动的内存安全 — 所有权与借用在编译期捕获 use-after-free 与数据竞争;延迟敏感路径避免 stop-the-world。
- 可预期的性能 — 零成本抽象与精细分配控制,使 Rust 在热 API 路径与边缘变换上具有竞争力。
- 有结构的无畏并发 — Send/Sync 与异步生态(Tokio、async-trait 模式、channel)鼓励可扩展的扇出设计。
- 可部署二进制 — 近似静态产物简化网关、智能体与 CI/CD sidecar 容器。
- 多语言庄园中的互操作 — FFI、Wasm 与 HTTP 让 Rust 与 Go、Python、JVM、Lua 边缘共存,无需重写一切。
Workstation 的立场与我们的 Polyglot Benchmarks 博客 和 长文 一致:为有界上下文选择合适工具。在 polyglot-benchmarks.fictionally.org 实时仪表板上,Rust(该 harness 中的 Actix)常在 CPU-bound 与并发敏感 HTTP 行上表现强劲——这是 ADR 论证热路径用 Rust 的证据,而不是信仰。
2. 协作式调度:“阻塞”的含义
Async Rust 使用协作式调度。运行时在任务到达 .await 时切换。Alice Ryhl 那句难忘的规则适用于我们交付的所有异步服务:
异步代码绝不应长时间不触及 .await。
在此词汇中,“阻塞线程”不仅仅是“做 IO”。它意味着阻止运行时切换当前任务。经典踩坑:
- 在 async fn 内使用
std::thread::sleep(无 await — 在join!下计时器串行)。 - 在 Tokio worker 上跑重循环、压缩、密码学、向量运算或重度 JSON。
- 在异步池上跨长临界区持有同步互斥锁(短锁可以;长锁不行)。
多线程运行时可能把 bug 藏到 worker 饱和为止。生产流量会替你发现它。对延迟 SLO,把 await 之间数十到数百微秒当作协作预算;更长的工作应离开异步池。
3. 必须阻塞时的三种去处
当你有意需要阻塞——昂贵 CPU 或同步 IO——把工作移出 Tokio 调度线程。速查表(对齐 Ryhl 的框架):
| 方法 | CPU-bound | 同步 IO | 永不退出 |
|---|---|---|---|
spawn_blocking | 次优(大池) | OK | 否 |
| Rayon | OK | 否 | 否 |
专用 std::thread | OK | OK | OK |
3.1 同步 IO 用 spawn_blocking
tokio::task::spawn_blocking 调度到 Tokio 阻塞池(默认可达数百线程)。适合文件系统与阻塞数据库驱动。对持续 CPU 不佳,因为过订阅会与 OS 调度器对抗——少量短计算可以,作为并行重算默认则危险。
3.2 昂贵 CPU 用 Rayon
Rayon 维护按 CPU 规模的并行池。关键集成细节:不要在等待 Rayon 时阻塞 Tokio worker。在 Rayon 上 spawn,通过 tokio::sync::oneshot 回传结果,在异步侧 .await 接收端。并行迭代器(par_iter)仍需要外层 rayon::spawn,因为它们会阻塞到完成。
// Shape only — see ryhl.io for a full walkthrough
async fn parallel_work(data: Vec<i32>) -> i32 {
let (tx, rx) = tokio::sync::oneshot::channel();
rayon::spawn(move || {
let sum: i32 = data.into_iter().sum(); // or par_iter inside
let _ = tx.send(sum);
});
rx.await.expect("rayon task panicked")
}
致谢:该集成模式是 ryhl.io 上 Rayon crate 章节的核心。Workstation 建议在产品服务中采用同样形状,使请求线程保持可调度。
3.3 永不退出的工作用专用线程
永不退出的循环(专用 DB 连接所有者、长寿命桥接)不应永久占用任一池的槽位。优先 std::thread::spawn,并用 channel 通信。
4. 映射到现代应用类型
| 表面 | 留在异步 | 卸载 |
|---|---|---|
| APIs | 接受、认证、扇出 HTTP、流式 | 重序列化、密码学批处理、打分 |
| 边缘 / 网关 | 路由、缓存查找、WAF 决策 | 偶发 CPU 变换;测得更好时优先 Lua/njs |
| 智能体 | 工具编排、MCP 会话、超时 | 嵌入准备、评测套件、大型本地变换 |
| CI/CD 平台 | 晋升 API、健康轮询、UI/API | 深度产物分析、批量校验 |
Workstation 产品体现这种拆分:Ring Promoter 必须保持晋升与健康门快速;WSL Proxy 保持边缘路径空闲;KubePilot 即使分析很重也需要灵敏的事故循环。Rust(或 Go、或 Lua)按表面测量后选择——绝不是走廊辩论的赢家。
5. 采用 async Rust 的架构清单
- 盘点 await 缺口:永不让步的 profiler 与 tracing span。
- 分类阻塞工作:同步 IO vs CPU vs 永不退出循环。
- 选择池:
spawn_blocking、Rayon + oneshot,或专用线程。 - 用真实并发做负载测试——多线程运行时在 N=1 时会藏 bug。
- 在 ADR 中记录决策;涉及语言选型时附上 Polyglot Benchmarks 行。
- 重读 Tokio 关于共享状态与协作让步对尾延迟的指导。
6. 延伸阅读
- Alice Ryhl — Async: What is blocking?(Rayon / spawn_blocking 框架的主要灵感)。
- Workstation — Polyglot Benchmarks 实时仪表板。
- Workstation — Polyglot 博客 · Polyglot 文章。
- Workstation — 配套博客,本简报的速览版。
由 Workstation 发布。概念致谢 Alice Ryhl 关于异步阻塞与 Rayon 的公开写作;产品框架与多语言指导均为 Workstation 原创。