deno_runtime 运行时库剖析:MainWorker、bootstrap 流程与 WebWorker 架构(Deno 源码实战)
本文基于 Deno 仓库中的 runtime/README.md 展开,系统讲解 deno_runtime crate 的定位与 API 设计:如何以 MainWorker 封装 deno_core::JsRuntime 并注入 Deno 命名空间的 ops、如何完成 bootstrap 初始化、如何通过 WorkerOptions/BootstrapOptions 定制模块加载、错误格式化、V8 inspector、User-Agent、CA 证书与随机数种子等运行时属性,以及 WebWorker 如何以“每 worker 一个专用 OS 线程”的模型实现 Web Worker API。读完后你可以理解 Deno CLI 的运行时底座是如何搭建的,并具备在自定义宿主中嵌入 deno_runtime 的工程思路。
deno_runtime 是什么:Deno CLI 的“瘦身版”
README 对 crate 的定义非常明确:它是 Deno CLI 的精简版本,移除了 TypeScript 集成以及 lint、doc 等工具链,本质上只保留JavaScript 执行能力加上 Deno 的操作系统绑定(ops)。这个 crate 发布在 crates.io 上,包名为 deno_runtime,当前仓库中版本为 0.266.0,描述为 "Provides the deno runtime library"(见 runtime/Cargo.toml)。
README 同时给出了一段重要的稳定性声明:该 crate 由原先位于 deno crate 中、久经考验的模块构建而成,但其 API 处于快速变化阶段,随时可能发生破坏性变更。因此引用它的下游项目需要做好跟随升级的准备。
从 runtime/Cargo.toml 的 features 定义可以看出该 crate 的几个编译开关,它们对应不同的集成场景:
docsrs:一个"假 feature",仅用于在 docs.rs 上正常生成文档;exclude_runtime_main_js:从导出的扩展中排除js/99_main.js,适合宿主希望自行接管主模块引导逻辑的场景;snapshot/transpile:启用 V8 快照能力(snapshot依赖transpile,后者引入deno_ast);hmr:开发特性,禁用快照的创建与加载,改为在运行时从磁盘读取各扩展的lazy_loaded_*源文件。
它聚合了哪些能力
deno_runtime 自身并不从零实现 Web API,而是把 Deno 的各个能力 crate 重新导出并组装。从 runtime/lib.rs 可以看到它 pub use 了二十多个功能模块:
pub use deno_cache;
pub use deno_core;
pub use deno_crypto;
pub use deno_fetch;
pub use deno_ffi;
pub use deno_fs;
pub use deno_http;
pub use deno_kv;
pub use deno_napi;
pub use deno_net;
pub use deno_node;
pub use deno_os;
pub use deno_permissions;
pub use deno_process;
pub use deno_web;
pub use deno_webgpu;
pub use deno_websocket;
// ...
并对外暴露了自身的关键模块:code_cache(V8 代码缓存)、coverage(覆盖率)、cpu_profiler(CPU 剖析)、fmt_errors(错误格式化)、js(内置 JS 源)、ops(宿主 ops)、permissions(权限系统)、snapshot(快照,feature 开关控制)、web_worker、worker 等。此外还重导出 deno_features 中的 FeatureChecker 与 UNSTABLE_FEATURES,用于 --unstable-* 特性门禁。换句话说,README 所说的“JavaScript 执行 + 操作系统绑定”在这套 re-export 与扩展注册中得到了具体体现。
主 API:MainWorker
README 指出该 crate 的主 API 是 MainWorker:一个封装了 deno_core::JsRuntime、并带有一组用于实现 Deno 命名空间的 ops 的结构体。源码与文档一致,见 runtime/worker.rs:
/// This worker is created and used by almost all
/// subcommands in Deno executable.
///
/// It provides ops available in the `Deno` namespace.
///
/// All `WebWorker`s created during program execution
/// are descendants of this worker.
pub struct MainWorker {
pub js_runtime: JsRuntime,
should_break_on_first_statement: bool,
should_wait_for_inspector_session: bool,
exit_code: ExitCode,
bootstrap_fn_global: Option<v8::Global<v8::Function>>,
dispatch_load_event_fn_global: v8::Global<v8::Function>,
dispatch_beforeunload_event_fn_global: v8::Global<v8::Function>,
dispatch_unload_event_fn_global: v8::Global<v8::Function>,
dispatch_process_beforeexit_event_fn_global: v8::Global<v8::Function>,
dispatch_process_exit_event_fn_global: v8::Global<v8::Function>,
memory_trim_handle: Option<tokio::task::JoinHandle<()>>,
}
从字段设计可以读出几个实现事实:
MainWorker的“人格”由两个回调集合构成:bootstrap_fn_global(JS 侧 bootstrap 入口,被bootstrap调用一次后消费)和五个事件分发函数——load、beforeunload、unload、process:beforeExit、process:exit。这说明MainWorker承担了 Node 风格的进程级事件生命周期;should_break_on_first_statement与should_wait_for_inspector_session对应--inspect调试场景:等待调试器连接、在第一条语句处断点;Drop实现会 abortmemory_trim_handle,即 worker 销毁时终止周期性的内存整理任务。
创建 MainWorker:bootstrap_from_options
README 强调“创建 MainWorker 的实现者必须调用 MainWorker::bootstrap 来准备 JS runtime”。当前源码中这一约定收敛为组合方法 bootstrap_from_options(runtime/worker.rs):先通过私有的 from_options 构造出 worker 和 BootstrapOptions,再立即调用 worker.bootstrap(bootstrap_options),保证创建与初始化不可分离。
from_options 接收两个大参数,正好对应 README 列举的可配置项:
1. WorkerServiceOptions(runtime/worker.rs)——宿主必须提供的服务依赖:
| 字段 | 作用 |
|---|---|
module_loader: Rc<dyn ModuleLoader> |
V8 请求加载 ES 模块时的回调实现;不提供则执行代码尝试加载模块会直接报错(对应 README 的“module loading implementation”) |
permissions: PermissionsContainer |
权限容器,多数 ops 依赖它做权限检查 |
fs、blob_store、fetch_dns_resolver |
文件系统、Blob 存储、DNS 解析器 |
root_cert_store_provider |
自定义根证书存储,对应 README 的 “CA certificate” 定制 |
shared_array_buffer_store |
多 isolate 间共享 SharedArrayBuffer;不提供则无法序列化 |
compiled_wasm_module_store |
多 isolate 间共享已编译的 WebAssembly.Module |
v8_code_cache |
模块/脚本源码的 V8 代码缓存 |
node_services / npm_process_state_provider |
可选的 Node 兼容与 npm 集成服务 |
2. WorkerOptions(runtime/worker.rs)——运行时行为参数:
| 字段 | 作用 |
|---|---|
bootstrap: BootstrapOptions |
注入 JS 环境的启动配置(下一节详述) |
extensions: Vec<Extension> |
额外注册的扩展(ops 与 JS/ESM 源);若已用快照则不应重复提供已快照化的 JS |
startup_snapshot: Option<&'static [u8]> |
启动时加载的 V8 快照 |
seed: Option<u64> |
随机数生成器种子(对应 README 的 “random number generator seed”) |
unsafely_ignore_certificate_errors |
对指定域名忽略证书错误 |
create_web_worker_cb |
创建 WebWorker 时必须提供的回调(README 中 WebWorker 一节的硬性要求,见后文) |
format_js_error_fn: Option<Arc<FormatJsErrorFn>> |
错误格式化钩子(对应 README 的 “error formatting”) |
create_params |
isolate 创建参数,例如堆内存上限 |
wait_for_inspector_session 类标志 |
对应 README 的 “V8 inspector 与 Chrome DevTools 调试器支持” |
快照分支:UnconfiguredRuntime 的两阶段构建
WorkerOptions 中还有一个值得注意的选项 unconfigured_runtime:当宿主(如 IDE 语言服务或 LSP 场景)需要复用已经创建好的运行时而不想重新构建时,可以先通过 UnconfiguredRuntime::new 建立运行时(runtime/worker.rs)。它内部用 PlaceholderModuleLoader 占位——一个所有方法都先 unwrap 一个尚未插入的真实 ModuleLoader 的空壳;随后 hydrate(module_loader) 把真实的加载器注入占位符并返回 JsRuntime。from_options 中检测到 unconfigured_runtime 后走 hydrate 分支,而非现场构建扩展与 runtime;并且一旦请求了 trace_ops 或 op metrics,占位 runtime 会被丢弃回退到常规路径,因为这两项必须在创建期配置。
BootstrapOptions:注入 JS 环境的“配置总线”
bootstrap 阶段真正发生的事,是把 BootstrapOptions 序列化后传给 JS 侧执行 99_main.js 的引导逻辑。BootstrapOptions 定义在 runtime/worker_bootstrap.rs,其字段覆盖了 README 可配置清单中“进程级”的那一部分:
pub struct BootstrapOptions {
pub deno_version: String,
/// Sets `Deno.args` in JS runtime.
pub args: Vec<String>,
pub cpu_count: usize,
pub log_level: WorkerLogLevel,
pub enable_testing_features: bool,
pub locale: String,
pub location: Option<ModuleSpecifier>,
pub color_level: deno_terminal::colors::ColorLevel,
// --unstable-* flags
pub unstable_features: Vec<i32>,
pub user_agent: String,
pub inspect: bool,
/// If this is a `deno compile`-ed executable.
pub is_standalone: bool,
pub has_node_modules_dir: bool,
pub argv0: Option<String>,
pub node_debug: Option<String>,
pub mode: WorkerExecutionMode,
// Used by `deno serve`
pub serve_port: Option<u16>,
pub serve_host: Option<String>,
pub auto_serve: bool,
pub otel_config: OtelConfig,
pub close_on_idle: bool,
pub disable_offscreen_canvas: bool,
}
其中几个字段与 README 的可配置项直接对应:user_agent(“HTTP client user agent”定制)、inspect(inspector 支持)、unstable_features(细粒度 unstable 特性位)。Default 实现(worker_bootstrap.rs)透露了合理默认值的来源:cpu_count 取 thread::available_parallelism(),user_agent 格式为 Deno/{CARGO_PKG_VERSION},locale 默认 "en",mode 默认 WorkerExecutionMode::None。注释还特意提醒:默认 user_agent 用的是 crate 版本号,实现者应当提供更有意义的 UA。
序列化路径也值得细看:as_v8 通过 serde_v8 把选项打包成 BootstrapV8 元组(worker_bootstrap.rs),结构体上的注释明确要求 “Keep this in sync with 99_main.js”——即 Rust 侧的字段顺序与 JS 侧解包顺序必须一一对应。这个“Rust 元组 ↔ JS 解构”契约的 JS 一端就在 runtime/js/99_main.js,整个 runtime/js/ 目录承载了运行时引导的全部 JS 层:01_version.ts(Deno.version)、90_deno_ns.js(Deno 命名空间组装)、11_workers.js(Worker 全局构造)、10_permissions.js(权限)、41_prompt.js(权限提示)等。
Worker Web API:WebWorker 与专用 OS 线程模型
README 的最后一节描述了 Worker Web API 的实现约定,源码中这三句话都能在 runtime/web_worker.rs 与 runtime/ops/worker_host.rs 中找到印证:
1. “Worker API 由 WebWorker 结构体实现”——web_worker.rs 中的 WebWorker 持有自己的 JsRuntime、name、worker_id、worker_type、main_module,以及 bootstrap_fn_global、poll_for_messages_fn 等 JS 回调。它的 Drop 实现会清理 Node resolver 的线程本地 package.json 缓存并终止内存整理任务。
2. “创建 MainWorker 时必须提供创建 Worker 的回调”——WorkerOptions::create_web_worker_cb 的类型定义在 runtime/ops/worker_host.rs:
pub type CreateWebWorkerCb = dyn Fn(CreateWebWorkerArgs)
-> (WebWorker, SendableWebWorkerHandle);
JS 侧 new Worker(url) 最终经 op 调用该回调;回调同时返回 worker 本体与一个可跨线程的 SendableWebWorkerHandle(消息通道句柄),宿主据此把 handle 注册进 op state 供后续 postMessage 使用。WebWorkerOptions(web_worker.rs)则携带了子 worker 所需的全部参数:name、main_module、worker_id、独立的 bootstrap: BootstrapOptions、独立的 seed、stdio、worker_type 等——注意 create_web_worker_cb 自身也被放入其中,使 worker 可以递归创建孙 worker,这与 README “所有 WebWorker 都是 MainWorker 的后代”的树形结构描述吻合。
3. “每个 WebWorker 都会 spawn 一条只服务于该 worker 的 OS 线程”——worker_host.rs 中可以看到 std::thread::Builder 的调用:先按 resourceLimits(若用户通过 Node 风格 API 指定了 stackSizeMb)或默认值设置栈大小(默认 DEFAULT_STACK_SIZE_MB,见 worker_host.rs 的注释:默认 4MB,以避免与 Node 行为不一致),然后 thread_builder.spawn 在新线程内构建 WebWorker、运行其事件循环,并通过 create_and_run_current_thread 驱动 future。新线程随后把自己的线程句柄存入 WorkersTable(以 worker_id 为键的 op state 表),之后主线程与 worker 线程之间的所有交互(发消息、terminate、Ctrl-C 控制、CPU 用量查询)都通过该表进行。
这种“一 worker 一专用线程 + 跨线程消息通道”的架构,意味着 WebWorker 与宿主线程之间不共享 isolate,跨 worker 数据传递走 postMessage 序列化通道(SharedArrayBuffer 与编译好的 wasm 模块则通过前文提到的两个共享 store 传递)。
小结:从 README 到源码的完整图景
回到 runtime/README.md 的主张,源码给出的完整证据链是:
- 定位:
deno_runtime0.266.0 是 CLI 的瘦身后端,靠聚合 re-export(runtime/lib.rs)提供 JS 执行 + ops 绑定; - 主 API:
MainWorker(runtime/worker.rs)封装JsRuntime与五个进程级事件分发器,bootstrap_from_options保证“创建即 bootstrap”(runtime/worker.rs); - 可定制面:
WorkerServiceOptions提供模块加载、权限、CA 证书等依赖注入;WorkerOptions提供种子、错误格式化、inspector、V8 快照等开关;BootstrapOptions(runtime/worker_bootstrap.rs)负责把这些配置经serde_v8同步到 runtime/js/99_main.js 完成环境引导; - Worker API:
WebWorker(runtime/web_worker.rs)+CreateWebWorkerCb回调(runtime/ops/worker_host.rs)+ 每 worker 一条专用 OS 线程的线程模型,构成与 Web 标准一致的Worker语义。
需要再次强调 README 的稳定性警告:deno_runtime 的 API 会快速演进,本文所述字段与方法对应当前仓库版本,跨版本引用时请以该 crate 的 docs 与实际源码为准。
atomcodeClaude Code 的开源替代方案。连接任意大模型,编辑代码,运行命令,自动验证 — 全自动执行。用 Rust 构建,极致性能。 | An open-source alternative to Claude Code. Connect any LLM, edit code, run commands, and verify changes — autonomously. Built in Rust for speed. Get StartedRust0622
Hy4-previewHy4 preview 是由腾讯混元团队研发的新一代混合专家(MoE)旗舰模型。模型总参数量 770B,每个 token 激活 49B,主干共包含78层,第一层采用标准 FFN,其余 77 层均为 MoE 结构,每层包含 256 个路由专家与 1 个共享专家,每个 token 激活 top-8 路由专家及共享专家。主干之外原生内置 1 层 MTP(总参数量 10B,激活 0.7B)以支持投机解码。Python00
GLM-5.3GLM-5.3 与 GLM-5.2 使用相同的基座模型——所有提升均来自后训练。与 GLM-5.2 相比,它在复杂编程和长程任务上的表现显著提升。Jinja00
GLM-5.3-FlashGLM-5.3-Flash (320B-A18B),是GLM-5系列的首个原生多模态模型。320B总参数,能力超过GLM-5.2Jinja00
Spark-X2.5-4BSpark-X2.5-4B 旨在让强大的 AI 更实用、更高效、更易获得。在广泛日常任务中表现强劲,涵盖对话、写作、翻译、推理、编码、工具调用以及智能体工作流,并在同等规模的开源模型中取得领先成绩。Spark-X2.5 将面向效率的架构与最高 1M tokens 的原生上下文窗口相结合,并支持 200 多种语言。Python00
Spark-X2.5-1.7BSpark-X2.5-1.7B 旨在让强大的 AI 更加实用、高效且易于获取。这些模型在广泛的日常任务中表现出色,涵盖对话、写作、翻译、推理、编程、工具调用和智能体工作流,并在同等规模的开源模型中取得领先结果。Spark-X2.5 将面向效率的架构与最高 1M tokens 的原生上下文窗口相结合,并支持 200 多种语言。Python00