Files
wasmeld/crates/wasmeld-console/README.md
T
Maofeng b5aa008729 docs(resident): document timer and TCP drivers
Describe ResidentSupervisor ownership, TCP policy and configuration, bounded stream backpressure, timer scheduling, restart behavior, and deterministic Host-resource shutdown order.
2026-07-31 10:04:46 +08:00

9.3 KiB
Raw Blame History

Wasmeld Console

wasmeld-consolewasmeld-runtime 的本地 HTTP 管理适配器。它负责:

  • 接收 .wasmpkg,校验并保存 component.wasm 与运行时 manifest
  • 发布、查询和下载不可变的二进制 WIT Package
  • 启动、停止和重启内嵌的 wasmeld-runtime
  • 启动、停止和重启常驻 Actor
  • 管理服务 ID 到活动版本的 Deployment
  • 通过独立 Gateway 转发原始二进制调用
  • 为导入 wasmeld:kv/store@0.1.0 的 Component 提供持久化 KV
  • 暴露调用计数、状态和最近事件
  • 在 Release 可执行文件中嵌入 Solid + Tailwind v4 管理面

Wasm 制品保存在本地文件系统。服务 manifest、调用计数和最近 256 条事件通过 Toasty 写入本地 libSQL 数据库,默认路径是 var/wasmeld/console.db。Deployment 也存储在同一数据库中。 Host KV 使用 (service_id, key) 复合主键存储在该数据库中:同一服务的 Revision 共享数据,不同服务互相隔离,Runtime 或 Console 重启不会清空 KV。

调用计数、错误数、延迟和调用事件通过容量受限的异步队列批量写入数据库,Gateway 响应不等待 libSQL。队列不可用或进程异常退出时,尚未写入的遥测可能丢失,但不会把 已经成功的 Component 调用改写成失败。注册、Deployment 和生命周期变更仍在响应前 完成持久化。

wasmeld-console 启动时会在同一进程内创建 wasmeld-runtime、加载 Wasmtime Engine 并重新注册已保存的 Component。Runtime 停止后 Console HTTP 和数据库仍然在线; 再次启动 Runtime 会重新注册 Component。未部署的版本恢复为 stoppedDeployment 指向的版本会用空初始化配置创建新的 Actor。Actor 的 Store 和线性内存不做快照。 编译后的本地机器码默认缓存在 var/wasmeld/component-cache,相同 Component 在 Console 或 Runtime 重启后可由 Wasmtime 直接复用;缓存不包含 Actor 内存。

常驻 Host Driver

导出 wasmeld:resident/actor@0.1.0 的 Component 启动时,Console 会为它创建独立的 ResidentSupervisor。Supervisor 在专用阻塞线程上独占 ResidentSession,避免同步 Actor mailbox 阻塞 Tokio executor。Timer、TCP Listener 和每条 TCP Stream 由 Tokio task 等待外部事件,再通过容量受限的 channel 汇聚到同一个 Supervisor。

Host 资源属于平台配置,不由 Component 创建,也不写入 .wasmpkg。当前可通过 ConsoleConfig::resident_services 为稳定服务 ID 配置 TCP Listener 和 Timer

use std::collections::BTreeMap;
use wasmeld_console::{
    ConsoleConfig, NetworkScope, ResidentPolicy, ResidentServiceConfig,
    ResidentTcpListenerConfig, ResidentTimerConfig,
};

let mut resident_services = BTreeMap::new();
resident_services.insert(
    "scheduler".to_owned(),
    ResidentServiceConfig {
        policy: ResidentPolicy {
            tcp_listen: NetworkScope::Loopback,
            ..ResidentPolicy::default()
        },
        tcp_listeners: vec![ResidentTcpListenerConfig {
            name: "internal-api".to_owned(),
            bind: "127.0.0.1:9000".parse().unwrap(),
        }],
        timers: vec![ResidentTimerConfig {
            name: "heartbeat".to_owned(),
            initial_delay_ms: 1_000,
            interval_ms: Some(30_000),
        }],
        ..ResidentServiceConfig::default()
    },
);
let config = ConsoleConfig {
    resident_services,
    ..ConsoleConfig::default()
};

ResidentPolicy 默认拒绝全部网络地址;配置 Listener 时还必须显式选择 LoopbackAny。Listener 在 Actor 启动过程中完成策略校验和 bind,任何失败都会回滚刚启动的 Actor,避免出现“Actor 正在运行但端口未监听”的半启动状态。Listener、Timer 和其它 顶层资源名称在同一服务内必须唯一,每个 Revision 都会获得独立的 Host handle 和不复用 资源 ID。

TCP Driver 每次最多读取 limits.resident.max_stream_chunk_bytes,不会把 socket handle 交给 Wasm。TCP 字节在 Supervisor 队列满时等待并把背压传递到内核接收缓冲区,不能像 Timer occurrence 一样丢弃。Component 可通过 WIT 返回 write-streamclose-streampause-streamresume-stream;每条连接的写入/控制队列也是有界的,慢客户端填满 队列时只关闭该连接,不阻塞同一 Actor 的其它连接。

Timer task 只负责等待,队列满时丢弃该次 occurrence,而不是创建无界积压。Component 返回 arm-timer 会替换 Host 初始日程,cancel-timer 会取消它;generation 使取消前 已经排队的迟到事件失效。

停止顺序固定为:停止接收新 Driver 事件、停止 accept、投递 shutdown、关闭 Stream 和 Timer task、先释放子 Stream 再释放 Listener、最后停止 Actor。服务重启和 Runtime Deployment 恢复都会重新 bind 并创建新 Session,不会复用旧 Revision 的 socket、Timer 或 Component 内存。UDP 和 Unix Driver 尚未接入,后续继续复用相同的 Supervisor 序列化边界。

启动

wasmeld-console 需要 Rust 1.95 或更高版本;Wasm 组件继续使用项目约定的 Rust 1.90 工具链构建。

cargo +stable run -p wasmeld-console

Debug 构建只启动 API,不构建或提供 Web 静态文件。前端开发服务器需要单独启动:

cd crates/wasmeld-console/web
npm ci
npm run dev

Release 构建会自动使用 package-lock.json 构建并嵌入管理面:

cargo +stable build --release -p wasmeld-console
./target/release/wasmeld-console

发布构建要求 Node.js 22.13+ 和 npm。可用 NPM 指定 npm 可执行文件;设置 WASMELD_BUILD_WEB=1 可以在非 Release Cargo 构建中验证嵌入流程。前端结构和 PWA 边界见 web/README.md

进程默认启动两个独立 Listener:

  • 管理 API127.0.0.1:8080
  • Gateway0.0.0.0:8081

管理 Router 不会挂载到 Gateway ListenerGateway 也不暴露注册、Runtime 生命周期和 WIT Registry 路由。

可用环境变量:

  • WASMELD_ADDR
  • WASMELD_GATEWAY_ADDR
  • WASMELD_ARTIFACT_DIR
  • WASMELD_DATABASE_PATH
  • WASMELD_WIT_REGISTRY_DIR
  • WASMELD_ALLOWED_ORIGINS,多个 Origin 使用逗号分隔
  • RUST_LOG

API

方法 路径 用途
GET /healthz 健康检查
GET /api/v1/runtime Runtime 状态
POST /api/v1/runtime/start 启动 Runtime 并重新注册受管 Component
POST /api/v1/runtime/stop 停止 Runtime 并释放所有 Actor
POST /api/v1/runtime/restart 重建 Runtime,并重新创建活动 Deployment 的 Actor
GET /api/v1/services 服务版本及精确 Host Capability 列表
POST /api/v1/services multipart 上传单个 package 字段(.wasmpkg
DELETE /api/v1/services/{id}/{revision} 注销非活动 Revision 并删除制品
POST /api/v1/services/{id}/{revision}/start 启动 Actor
POST /api/v1/services/{id}/{revision}/stop 停止 Actor
POST /api/v1/services/{id}/{revision}/restart 重启 Actor
POST /api/v1/services/{id}/{revision}/invoke Base64 二进制调用
GET /api/v1/deployments 活动 Deployment 列表
GET /api/v1/deployments/{id} 查询服务的活动版本
POST /api/v1/deployments/{id}/activate 启动目标版本并原子切换活动版本
GET /api/v1/events 最近 256 条持久化事件
GET /api/v1/wit/packages WIT 包版本列表
POST /api/v1/wit/packages multipart 发布单个二进制 WIT package 字段
GET /api/v1/wit/packages/{namespace}/{name}/{version} WIT 包元数据
GET /api/v1/wit/packages/{namespace}/{name}/{version}/content 下载 WIT 包

活动 Deployment 不能直接注销。开发工具会先完成新 Revision 的编译、注册和切换, 再注销上一份开发 Revision,因此失败的构建不会影响当前可调用版本。

激活已注册版本:

curl -X POST http://127.0.0.1:8080/api/v1/deployments/echo/activate \
  -H 'Content-Type: application/json' \
  -d '{"revision":"0.1.0"}'

Gateway 只提供 GET /healthzPOST /v1/services/{id}/invoke。调用请求与成功响应的 Content-Type 都是 application/octet-stream

curl http://127.0.0.1:8081/v1/services/echo/invoke \
  -H 'Content-Type: application/octet-stream' \
  --data-binary 'hello'

成功响应通过 X-Wasmeld-Revision 返回实际路由的版本。Gateway 错误统一返回 JSON; 未部署为 404Actor 不可用为 503,过载为 429,执行超时为 504

服务响应的 capabilities 来自 Component 二进制中的 WIT imports,例如 wasmeld:clock/monotonic-clock@0.1.0。Console 不接受手工能力声明,Runtime 只会链接 注册表中存在且版本完全匹配的接口。

Host KV

wasmeld:kv/store@0.1.0 提供 getsetdelete。Key 必须是非空 UTF-8 最大 256 BValue 最大 64 KiB。数据库命令通过有界专用工作线程执行,Host 等待时间 不会超过服务的 deadline_ms。v0.1.0 不提供 TTL、遍历、事务或 CAS。

组件包格式、开发循环、WIT 依赖、Registry 和本地 replace 说明见 wasmeld-package CLI 文档