Rust in Everything
基于 reqwest 0.13.5

阻塞与 WASM

前面四部分讲的都是 async_impl::Client。reqwest 还导出另外两套同名的 Client / RequestBuilder / Response:reqwest::blocking 给不想碰 async 的调用方,wasm32-unknown-unknown 目标上的 reqwest::Client 给浏览器。两者和异步客户端的关系完全不同。阻塞客户端是一层包装:每个 Client 一个后台线程,线程里的异步客户端照常走请求主线和连接主线,调用线程只负责送请求、停车等结果。WASM 是另一套实现:没有 tower、没有连接池,一次请求就是一次 fetch(),连接主线整个交给浏览器。

代码分布(按 tag v0.13.5 统计 .rs 行数,含文档注释和测试):src/blocking/ 4217 行,其中 client.rs 1583 行大半是转发给异步 builder 的方法;src/wasm/ 2186 行。

  1. 阻塞客户端:ClientHandle 起一个 current_thread 运行时线程,请求经无界 mpsc 送去、oneshot 送回,调用线程在 wait::timeout 里 park;默认 30 秒超时是每段等待的上限,不是总超时;在异步上下文里调用,debug 构建 panic「Cannot drop a runtime ...」,release 构建不检查,current_thread 下会死锁到超时;一个 Client 多一个线程、约 20 µs 的 build,单请求延迟和异步客户端同一量级。
  2. WASM:if_wasm! / if_hyper! 按目标切换实现;请求翻译成 RequestInit 交给 fetch,AbortGuard 管取消和超时;ClientBuilder 只剩 4 个方法,redirect、Proxy、tls 等模块不存在,另有 12 个 fetch_* 方法只在 WASM 上有;wasm32-unknown-unknown 离线 cargo check 能过,依赖从 103 个降到 63 个。
flowchart TB
  user["调用方代码"] --> blk["blocking Client"]
  user --> asy["async Client"]
  user --> wsm["wasm Client"]
  blk -- "mpsc 和 oneshot" --> rt["后台运行时线程"]
  rt --> asy
  asy --> stack["tower 栈与 hyper-util"]
  wsm --> fetch["浏览器 fetch"]

三套实现的名字在 lib.rs 里怎么分配,见源码地图;这种「同名不同实现」的取舍放在设计取舍里讨论。

💬 关联讨论

发起讨论
加载中...