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

205 lines
9.3 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# Wasmeld Console
`wasmeld-console``wasmeld-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](https://github.com/tokio-rs/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。未部署的版本恢复为 `stopped`Deployment
指向的版本会用空初始化配置创建新的 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
```rust
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 时还必须显式选择 `Loopback`
`Any`。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-stream``close-stream`
`pause-stream``resume-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
工具链构建。
```bash
cargo +stable run -p wasmeld-console
```
Debug 构建只启动 API,不构建或提供 Web 静态文件。前端开发服务器需要单独启动:
```bash
cd crates/wasmeld-console/web
npm ci
npm run dev
```
Release 构建会自动使用 `package-lock.json` 构建并嵌入管理面:
```bash
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`](web/README.md)。
进程默认启动两个独立 Listener:
- 管理 API`127.0.0.1:8080`
- Gateway`0.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,因此失败的构建不会影响当前可调用版本。
激活已注册版本:
```bash
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 /healthz`
`POST /v1/services/{id}/invoke`。调用请求与成功响应的
`Content-Type` 都是 `application/octet-stream`
```bash
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
未部署为 `404`Actor 不可用为 `503`,过载为 `429`,执行超时为 `504`
服务响应的 `capabilities` 来自 Component 二进制中的 WIT imports,例如
`wasmeld:clock/monotonic-clock@0.1.0`。Console 不接受手工能力声明,Runtime 只会链接
注册表中存在且版本完全匹配的接口。
## Host KV
`wasmeld:kv/store@0.1.0` 提供 `get``set``delete`。Key 必须是非空 UTF-8
最大 256 BValue 最大 64 KiB。数据库命令通过有界专用工作线程执行,Host 等待时间
不会超过服务的 `deadline_ms`。v0.1.0 不提供 TTL、遍历、事务或 CAS。
组件包格式、开发循环、WIT 依赖、Registry 和本地 `replace` 说明见
[`wasmeld-package` CLI 文档](../wasmeld-package/README.md)。