docs: 说明文件重整(README 改使用导向,运维细节移入 docs/worker-management.md)
This commit is contained in:
+3
-2
@@ -81,7 +81,7 @@ sudo docker compose down # 停止并移除容器(保留数
|
|||||||
sudo docker compose down -v # 停止并移除容器 + 数据卷(慎用,会清空数据)
|
sudo docker compose down -v # 停止并移除容器 + 数据卷(慎用,会清空数据)
|
||||||
```
|
```
|
||||||
|
|
||||||
### 验证
|
### 健康检查
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
sudo docker exec chongming-redis redis-cli ping # 期望 PONG
|
sudo docker exec chongming-redis redis-cli ping # 期望 PONG
|
||||||
@@ -94,7 +94,8 @@ sudo docker exec chongming-mosquitto mosquitto_sub -p 1883 -t '$SYS/broker/versi
|
|||||||
|
|
||||||
**Zenoh Router**:项目 [zenoh_config.json5](../config/zenoh_config.json5) 默认以 `peer` 模式连接 `tcp/127.0.0.1:7447`。本 router 容器把 7447 映射到宿主机,同机部署时应用即可直接连到该 router;若跨机部署,需把 `connect.endpoints` 改为 router 机器地址。
|
**Zenoh Router**:项目 [zenoh_config.json5](../config/zenoh_config.json5) 默认以 `peer` 模式连接 `tcp/127.0.0.1:7447`。本 router 容器把 7447 映射到宿主机,同机部署时应用即可直接连到该 router;若跨机部署,需把 `connect.endpoints` 改为 router 机器地址。
|
||||||
|
|
||||||
**Mosquitto**:项目 [config.toml](../config.toml) 默认 `use_embedded_broker = true`(内嵌 amqtt)。要切到本外部 broker,需改为:
|
**Mosquitto**:网关默认可内嵌 amqtt Broker(`use_embedded_broker = true`)。要改连本目录的
|
||||||
|
外部 broker,把 `config.toml` 的 `[<env>.mqtt]` 改为:
|
||||||
|
|
||||||
```toml
|
```toml
|
||||||
[development.mqtt]
|
[development.mqtt]
|
||||||
|
|||||||
@@ -1,11 +1,13 @@
|
|||||||
# 多语言插件演进统一规划(Rust 优先)
|
# 多语言插件演进统一规划(Rust 优先)
|
||||||
|
|
||||||
|
> **本文是演进规划记录,不是使用指南**。要开发/使用插件请先看:
|
||||||
|
> [进程插件开发指南(Python)](./process-plugin.md)、
|
||||||
|
> [Rust 插件编写说明](./rust-plugin.md)、
|
||||||
|
> [进程插件线协议规范(语言无关)](./plugin-wire-protocol.md)。
|
||||||
|
|
||||||
> 背景:团队技术栈杂糅,Python 在部分计算密集场景性能有限。
|
> 背景:团队技术栈杂糅,Python 在部分计算密集场景性能有限。
|
||||||
> 目标:在不破坏现有体系的前提下,开放进程插件多语言实现能力,
|
> 目标:在不破坏现有体系的前提下,开放进程插件多语言实现能力,
|
||||||
> **Rust 优先落地**,为后续其他语言(Go / C++ 等)铺路。
|
> **Rust 优先落地**,为后续其他语言(Go / C++ 等)铺路。
|
||||||
>
|
|
||||||
> 配套文档:[进程插件线协议规范(语言无关)](./plugin-wire-protocol.md)、
|
|
||||||
> [进程插件开发指南(Python)](./process-plugin.md)
|
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
@@ -60,7 +62,7 @@ chongming-worker-rs/ # 独立仓库(或 utils/worker-rs/,与 Pyth
|
|||||||
│ # 另有 [workers.config] 运行时读写(保留键校验 + 原子落盘)
|
│ # 另有 [workers.config] 运行时读写(保留键校验 + 原子落盘)
|
||||||
├── descriptor.rs # 描述符结构体(规范 §2.2 字段表)+ 端点 TOML 解析
|
├── descriptor.rs # 描述符结构体(规范 §2.2 字段表)+ 端点 TOML 解析
|
||||||
├── publisher.rs # RoutePublisher 等价:PUT / DELETE / 10s 重发布
|
├── publisher.rs # RoutePublisher 等价:PUT / DELETE / 10s 重发布
|
||||||
├── serve.rs # worker_serve 等价:liveliness token + Queryable + 生命周期
|
├── serve.rs # 入口等价:chongming-worker(Python 侧):liveliness token + Queryable + 生命周期
|
||||||
├── runtime.rs # 托管副本模型:ReplicaController(index/epoch/restart/stop/drain)
|
├── runtime.rs # 托管副本模型:ReplicaController(index/epoch/restart/stop/drain)
|
||||||
│ # + WorkerPool(按 target 动态扩缩,优雅排空)
|
│ # + WorkerPool(按 target 动态扩缩,优雅排空)
|
||||||
├── admin.rs # __admin__ Queryable:status/restart/stop/drain/get_config/
|
├── admin.rs # __admin__ Queryable:status/restart/stop/drain/get_config/
|
||||||
@@ -159,7 +161,7 @@ Rust 侧用 sqlx 在 `utils/worker-rs` 内提供等价封装(feature `database
|
|||||||
SQL 占位符遵循各自方言(SQLite `?`、PostgreSQL `$1`),SDK 不做 SQL 重写。
|
SQL 占位符遵循各自方言(SQLite `?`、PostgreSQL `$1`),SDK 不做 SQL 重写。
|
||||||
- **TLS**:默认不启用 TLS 后端;PostgreSQL 走 SSL 时需追加 `tls-rustls-ring` 等 feature。
|
- **TLS**:默认不启用 TLS 后端;PostgreSQL 走 SSL 时需追加 `tls-rustls-ring` 等 feature。
|
||||||
- **参考实现**:`workers/rsdemo` 的 `db_ping` 端点(写 + 读一表做连通性探测);`gen_plugin --rust` 模板已内置
|
- **参考实现**:`workers/rsdemo` 的 `db_ping` 端点(写 + 读一表做连通性探测);`gen_plugin --rust` 模板已内置
|
||||||
注释版 `[workers.config.database]` 段与 `database` feature(见 [rsdemo README](../../workers/rsdemo/README.md))。
|
注释版 `[workers.config.database]` 段与 `database` feature(见 [rsdemo README](../workers/rsdemo/README.md))。
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
@@ -167,7 +169,7 @@ Rust 侧用 sqlx 在 `utils/worker-rs` 内提供等价封装(feature `database
|
|||||||
|
|
||||||
### 4.1 build_plugin 支持非 Python 插件
|
### 4.1 build_plugin 支持非 Python 插件
|
||||||
|
|
||||||
- 现状:`build_plugin` 产物为 pyproject.toml + start.sh(`uv run worker_serve --type <name>`),纯 Python 链路
|
- 现状:`build_plugin` 产物为 pyproject.toml + start.sh(`uv run chongming-worker --type <name>`),纯 Python 链路
|
||||||
- 目标:支持声明插件语言/入口,允许打包任意可执行产物 + 自定义 start.sh
|
- 目标:支持声明插件语言/入口,允许打包任意可执行产物 + 自定义 start.sh
|
||||||
(Rust 插件 = 编译产物 + `--type` 参数约定),`--type` 语义跨语言保持一致
|
(Rust 插件 = 编译产物 + `--type` 参数约定),`--type` 语义跨语言保持一致
|
||||||
- 约束:打包结构(zip 根 = 容器工作目录)不变
|
- 约束:打包结构(zip 根 = 容器工作目录)不变
|
||||||
|
|||||||
@@ -237,11 +237,11 @@ worker/<type>/<id>/<subject>
|
|||||||
| 协议项 | 唯一实现(文件:行号) |
|
| 协议项 | 唯一实现(文件:行号) |
|
||||||
| ---- | ---- |
|
| ---- | ---- |
|
||||||
| 描述符 keyexpr / route_id 编码 / 发布摘除 | `utils/worker/src/chongming_worker/routes_publisher.py:23-89` |
|
| 描述符 keyexpr / route_id 编码 / 发布摘除 | `utils/worker/src/chongming_worker/routes_publisher.py:23-89` |
|
||||||
| 描述符字段定义与消费 | `src/chongming/app/core/route_generator.py:82-100`(RouteConfig) |
|
| 描述符字段定义与消费 | `utils/gateway/src/chongming/app/core/route_generator.py:82-100`(RouteConfig) |
|
||||||
| 描述符生成约定(TOML → dict) | `utils/worker/src/chongming_worker/route_config.py:25-103` |
|
| 描述符生成约定(TOML → dict) | `utils/worker/src/chongming_worker/route_config.py:25-103` |
|
||||||
| 网关订阅 / 防抖 / 幂等 / 冲突 / 筛选 | `src/chongming/app/core/route_registry.py:44-261` |
|
| 网关订阅 / 防抖 / 幂等 / 冲突 / 筛选 | `utils/gateway/src/chongming/app/core/route_registry.py:44-261` |
|
||||||
| liveliness 发现 / 负载均衡 / 超时重试 / 缓存 | `src/chongming/app/core/worker_manager.py:73-379` |
|
| liveliness 发现 / 负载均衡 / 超时重试 / 缓存 | `utils/gateway/src/chongming/app/core/worker_manager.py:73-379` |
|
||||||
| 请求信封构造(meta.method / user_info) | `src/chongming/app/core/route_generator.py:241-405` |
|
| 请求信封构造(meta.method / user_info) | `utils/gateway/src/chongming/app/core/route_generator.py:241-405` |
|
||||||
| 信封解析 / 分派 / 三种响应形态 | `utils/worker/src/chongming_worker/runtime.py:27-220` |
|
| 信封解析 / 分派 / 三种响应形态 | `utils/worker/src/chongming_worker/runtime.py:27-220` |
|
||||||
| 进程生命周期(10s 重发布 / 优雅退出) | `utils/worker/src/chongming_worker/process.py:35-197` |
|
| 进程生命周期(10s 重发布 / 优雅退出) | `utils/worker/src/chongming_worker/process.py:35-197` |
|
||||||
| 配置读取(json5_path / 插件配置) | `utils/worker/src/chongming_worker/process.py:39-136` |
|
| 配置读取(json5_path / 插件配置) | `utils/worker/src/chongming_worker/process.py:39-136` |
|
||||||
|
|||||||
+19
-51
@@ -27,9 +27,7 @@
|
|||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## 2. 运行原理
|
## 2. 工作机制(简述)
|
||||||
|
|
||||||
### 2.1 架构
|
|
||||||
|
|
||||||
```mermaid
|
```mermaid
|
||||||
graph TB
|
graph TB
|
||||||
@@ -37,7 +35,7 @@ graph TB
|
|||||||
REG[网关 RouteRegistry<br/>订阅 chongming/routes/**] -->|动态挂载/摘除| API
|
REG[网关 RouteRegistry<br/>订阅 chongming/routes/**] -->|动态挂载/摘除| API
|
||||||
API --> WM[WorkerManager<br/>负载均衡 + 超时重试]
|
API --> WM[WorkerManager<br/>负载均衡 + 超时重试]
|
||||||
|
|
||||||
subgraph GW[网关容器 chongming]
|
subgraph GW[网关容器]
|
||||||
API
|
API
|
||||||
REG
|
REG
|
||||||
WM
|
WM
|
||||||
@@ -54,36 +52,18 @@ graph TB
|
|||||||
WS -.->|SIGTERM 时 delete 摘除| REG
|
WS -.->|SIGTERM 时 delete 摘除| REG
|
||||||
```
|
```
|
||||||
|
|
||||||
### 2.2 Worker 进程启动流程(`chongming_worker.process.worker_serve`)
|
`chongming-worker --type demo` 启动后的流程:
|
||||||
|
|
||||||
```text
|
1. 读根 `config.toml` 的 `[zenoh]` 节打开会话,自读
|
||||||
chongming-worker --type demo
|
`config/workers/demo/config.toml` 的 `[workers]` 节(含 `[workers.config]` 运行时配置)
|
||||||
1. 读全局 config.toml(仅 [zenoh] 节)→ 打开 Zenoh 会话
|
2. import 插件模块触发 `@rpc_method` 注册 → **进程内契约校验**(不通过则退出码 1)
|
||||||
2. 自读 config/workers/demo/config.toml 的 [workers] 节(load_plugin_config 展平)
|
3. `create_workers` 创建副本(默认 `count` 个,`--count` 可覆盖),注册 liveliness
|
||||||
3. import 插件模块(plugins.<name> / workers.<name>)→ 触发 @rpc_method 注册
|
4. 经 Zenoh 把路由描述符 put 到 `chongming/routes/<type>/<route_id>`,网关动态挂载 HTTP 路由;
|
||||||
4. 进程内契约校验(TOML ↔ 注册表 ↔ 生成模型),硬失败则退出(退出码 1)
|
每 10s 重发布(网关后启动或样本丢失时自愈)
|
||||||
5. create_workers 创建 Worker 副本(默认 count 个,--count 可覆盖)
|
5. 收到 SIGTERM/SIGINT → delete 描述符(路由摘除)→ cleanup → 关闭会话
|
||||||
6. RoutePublisher 发布路由描述符 → 网关动态挂载 HTTP 路由
|
|
||||||
7. 等待退出信号;期间每 10s 周期性重发布(网关后启动 / 样本丢失时自愈)
|
|
||||||
8. SIGTERM/SIGINT → 摘除路由 → cleanup → 取消副本任务 → 关闭会话
|
|
||||||
```
|
|
||||||
|
|
||||||
### 2.3 路由注册与摘除协议
|
要点:网关容器**无需**放置 external 插件的配置文件(RouteRegistry 常驻订阅,与盘扫解耦);
|
||||||
|
描述符字段与 keyexpr 细节见 [线协议规范](plugin-wire-protocol.md)。
|
||||||
- **发布**:路由描述符 put 到 `chongming/routes/<type>/<route_id>`,其中
|
|
||||||
`route_id = <subject 的 . 替换为 _>__<METHOD>`(如 `demo_hello__GET`),
|
|
||||||
描述符为 TOML 兼容 dict + `schema_version=1` + `type=<插件名>`
|
|
||||||
- **元信息**:插件声明了 `[[workers.mqtt_topics]]` 时,额外发布
|
|
||||||
`chongming/routes/<type>/__meta__`(含 mqtt_topics),网关据此动态订阅外部主题
|
|
||||||
- **自愈**:每 10s 重发布一次全部描述符(`ANNOUNCE_INTERVAL`),网关后启动或样本丢失可恢复
|
|
||||||
- **摘除**:优雅退出时 delete 全部已发布 key,网关 2s 防抖后摘除 HTTP 端点(请求变 404)
|
|
||||||
- **网关侧**:RouteRegistry 常驻订阅 `chongming/routes/**`,与盘扫解耦——
|
|
||||||
**网关容器无需放置 external 插件的配置文件**
|
|
||||||
|
|
||||||
### 2.4 服务发现
|
|
||||||
|
|
||||||
Worker 副本经 liveliness 注册 `worker/<type>/<id>`(与 inline 副本同一机制),
|
|
||||||
WorkerManager 据此做负载均衡,网关侧无需任何改动。
|
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
@@ -114,8 +94,7 @@ workers/<name>/ # 项目根 = zip 根 = 容器工作目录
|
|||||||
└── start.sh # 容器入口(uv sync + uv run chongming-worker --type <name>)
|
└── start.sh # 容器入口(uv sync + uv run chongming-worker --type <name>)
|
||||||
```
|
```
|
||||||
|
|
||||||
> 参考示例:`workers/demo/`(完整项目,含 5 个端点,配合 `test_worker_isolation.py` /
|
> 参考示例:`workers/demo/`(完整项目,含 5 个端点)。
|
||||||
> `test_process_mode_e2e.py` 使用)。
|
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
@@ -291,16 +270,10 @@ uv run --project .. chongming-worker --type demo
|
|||||||
> 若配置了 `load_mode` 非 `"external"`,会输出告警:网关也会在进程内加载该插件,
|
> 若配置了 `load_mode` 非 `"external"`,会输出告警:网关也会在进程内加载该插件,
|
||||||
> 外部进程将与网关内副本**重复服务**。
|
> 外部进程将与网关内副本**重复服务**。
|
||||||
|
|
||||||
### 第 7 步:验证
|
### 第 7 步:联调验证
|
||||||
|
|
||||||
```bash
|
启动网关(`uv run serve`)后调用插件路由即可验证端到端:网关日志可见路由动态挂载,
|
||||||
# 1. 进程隔离性(阻断 chongming 导入 + AST 检查 import 语句;自动切到 demo 项目根)
|
worker 退出后路由自动摘除(请求变 404),重启 worker 后恢复。
|
||||||
uv run python test_worker_isolation.py
|
|
||||||
|
|
||||||
# 2. 端到端(需先启动网关 uv run serve):从 demo 项目根拉起 worker 进程,
|
|
||||||
# 路由动态注册 → 跨进程 RPC → 退出摘除(404) → 重启恢复
|
|
||||||
uv run python test_process_mode_e2e.py
|
|
||||||
```
|
|
||||||
|
|
||||||
### 第 8 步:打包
|
### 第 8 步:打包
|
||||||
|
|
||||||
@@ -442,11 +415,8 @@ API 一览(`chongming_worker.storage.StorageClient`):
|
|||||||
- `zenoh` / `pydantic` 等第三方库
|
- `zenoh` / `pydantic` 等第三方库
|
||||||
- 插件自身的扩展依赖(项目根 `pyproject.toml` 中声明)
|
- 插件自身的扩展依赖(项目根 `pyproject.toml` 中声明)
|
||||||
|
|
||||||
由 `test_worker_isolation.py` 双重保证:
|
隔离性由框架保证:SDK 导入链路中一旦出现 `chongming` / `chongming.*` 的 import,
|
||||||
|
运行时立即 `ImportError`(启动即暴露),无需等到部署阶段。
|
||||||
1. **运行时阻断**:`sys.modules["chongming"] = None` 后走完整链路
|
|
||||||
(SDK 导入 → 插件配置自读 → 插件 import → 契约校验),任何网关依赖立即 ImportError
|
|
||||||
2. **静态检查**:AST 解析 SDK 各模块源码,确认无 `chongming` / `chongming.*` import 语句
|
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
@@ -476,7 +446,7 @@ Worker 进程启动时自读 TOML 并**在自己进程内**做三方一致性校
|
|||||||
| 路由一直 404 | worker 是否已退出(优雅退出会摘除路由);进程被 SIGKILL 时描述符残留,重启 worker 后重发布自愈覆盖 |
|
| 路由一直 404 | worker 是否已退出(优雅退出会摘除路由);进程被 SIGKILL 时描述符残留,重启 worker 后重发布自愈覆盖 |
|
||||||
| 找不到插件配置 | `chongming-worker` 须在**插件项目根** `workers/<name>/` 下运行(读 `config/workers/<name>/config.toml`,`--config` 可指定全局配置路径) |
|
| 找不到插件配置 | `chongming-worker` 须在**插件项目根** `workers/<name>/` 下运行(读 `config/workers/<name>/config.toml`,`--config` 可指定全局配置路径) |
|
||||||
| storage.* 端点报「storage 未配置」 | 插件 config.toml 未配置 `[workers.config.storage]`(或 `enabled = false` / 连接失败降级)。补齐配置后重启 worker;启动日志可见「对象存储已就绪」或「storage 连接失败」告警 |
|
| storage.* 端点报「storage 未配置」 | 插件 config.toml 未配置 `[workers.config.storage]`(或 `enabled = false` / 连接失败降级)。补齐配置后重启 worker;启动日志可见「对象存储已就绪」或「storage 连接失败」告警 |
|
||||||
| 容器内 import `workers.demo` 失败 | 确认在容器工作目录(zip 根)启动,`start.sh` 内含 `cd "$(dirname "$0")"`,worker_serve 会把 CWD 加入 sys.path |
|
| 容器内 import `workers.demo` 失败 | 确认在容器工作目录(zip 根)启动,`start.sh` 内含 `cd "$(dirname "$0")"`,启动入口会把 CWD 加入 sys.path |
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
@@ -489,8 +459,6 @@ uv run chongming-worker --type <name> # 本地调试独立 Worker
|
|||||||
./start.sh # 与容器一致的启动方式(uv sync + 启动)
|
./start.sh # 与容器一致的启动方式(uv sync + 启动)
|
||||||
cd .. && uv run build_plugin <name> # 进程插件打包 → dist/<name>.zip
|
cd .. && uv run build_plugin <name> # 进程插件打包 → dist/<name>.zip
|
||||||
uv run gen_models workers/<name>/config/workers/<name> workers/<name>/workers/<name> # 生成模型
|
uv run gen_models workers/<name>/config/workers/<name> workers/<name>/workers/<name> # 生成模型
|
||||||
uv run python test_worker_isolation.py # 进程隔离性测试
|
|
||||||
uv run python test_process_mode_e2e.py # 端到端测试(先启动网关)
|
|
||||||
```
|
```
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|||||||
+9
-9
@@ -281,28 +281,28 @@ unzip myplugin.zip -d /app && cd /app && ./start.sh
|
|||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## 9. 本地验证
|
## 9. 本地联调
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
# Zenoh Router(替代 zenohd)
|
# 起一个本地 Zenoh Router(替代 zenohd)
|
||||||
.venv/bin/python utils/worker-rs/scripts/zenoh_router.py &
|
.venv/bin/python utils/worker-rs/scripts/zenoh_router.py &
|
||||||
|
|
||||||
# 协议级 15 项断言(描述符/RPC/流式/错误/摘除/自愈)
|
# 协议级端到端自检(描述符 / RPC / 流式 / 错误 / 摘除 / 自愈)
|
||||||
.venv/bin/python utils/worker-rs/scripts/verify_e2e.py \
|
.venv/bin/python utils/worker-rs/scripts/verify_e2e.py \
|
||||||
--type myplugin --bin workers/myplugin/target/debug/myplugin
|
--type myplugin --bin workers/myplugin/target/debug/myplugin
|
||||||
```
|
```
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## 10. 与 Python SDK 的差异(有意为之)
|
## 10. Rust 侧行为要点
|
||||||
|
|
||||||
| 差异 | 说明 |
|
| 要点 | 说明 |
|
||||||
| ---- | ---- |
|
| ---- | ---- |
|
||||||
| 契约校验 | Python 进程内三方校验(硬失败);Rust 为**发布前自检**:无 handler 端点告警跳过,不发布残缺路由 |
|
| 契约校验 | **发布前自检**:无 handler 的端点告警跳过,不发布残缺路由 |
|
||||||
| 模型生成 | Rust 用 gen_models 生成强类型结构体 + **启动时指纹校验**(TOML 变更未重生成即告警,比 Python 更强) |
|
| 模型生成 | `gen_models` 生成强类型结构体 + **启动时指纹校验**(TOML 变更未重生成即告警) |
|
||||||
| handler 注入 | worker_id 经 `Request.worker_id`、user_info 经 `Request.user_info` 注入(非函数参数) |
|
| handler 注入 | worker_id 经 `Request.worker_id`、user_info 经 `Request.user_info` 注入(非函数参数) |
|
||||||
| 配置缓存后端 | Rust 支持 sqlite(`local-cache`,本机与 Python 共享同一 .db)+ Redis(`redis-cache`,跨主机);缺省无缓存回退 |
|
| 配置缓存后端 | `local-cache`(sqlite,与 Python 共享同一 .db)/ `redis-cache`(跨主机);缺省无缓存回退 |
|
||||||
| 数据库 | Rust 走 sqlx(见 §7),配置键与 Python `chongming_database` 对齐 |
|
| 数据库 | 走 sqlx(见 §7),配置键与 `chongming_database` 对齐 |
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,163 @@
|
|||||||
|
# Worker 管理与多实例运维
|
||||||
|
|
||||||
|
网关(chongming-gateway)对 Worker 副本的运维能力:管理指令与 REST 端点、扩缩容语义、
|
||||||
|
实例定向调用,以及多网关/多实例部署时的收敛前提。inline 与 external(进程)插件共用同一套
|
||||||
|
管理协议。
|
||||||
|
|
||||||
|
> 使用入口速览见主 [README](../README.md#worker-管理与扩缩容)。
|
||||||
|
|
||||||
|
## 1. 管理协议
|
||||||
|
|
||||||
|
每个用 Worker 框架创建的副本都会注册管理 Queryable:
|
||||||
|
|
||||||
|
```text
|
||||||
|
worker/<type>/<worker_id>/__admin__
|
||||||
|
```
|
||||||
|
|
||||||
|
| 指令 | 说明 |
|
||||||
|
| ---- | ---- |
|
||||||
|
| `status` | 查询副本状态(含 `instance_id`,实例定向据此寻址) |
|
||||||
|
| `restart` | 重启该副本:副本槽位由管理循环驱动,重新生成新 `worker_id` 并更新 liveliness |
|
||||||
|
| `stop` | 停止该副本 |
|
||||||
|
| `drain` | **优雅排空**:摘除 liveliness/业务 Queryable 隔绝新请求,等待在途请求完成后退出,管理循环不重建 |
|
||||||
|
| `rescale` | 调整副本数:不带 `instance_id` 为「全局对称分摊」;带 `instance_id + absolute=true` 且命中本实例时为「逐实例精细扩缩」,target 非本实例则安全忽略 |
|
||||||
|
| `config_set` | 更新 `[workers.config]` 运行时配置(路由配置除外);成功则写回配置并重启该类型副本,任一失败返回失败明细 |
|
||||||
|
|
||||||
|
> 旧版运行时未声明 `__admin__` 管理能力时,管理指令会超时(HTTP 502,日志有提示)。
|
||||||
|
|
||||||
|
## 2. REST 管理端点(仅超管)
|
||||||
|
|
||||||
|
| 方法 | 路径 | 说明 |
|
||||||
|
| ---- | ---- | ---- |
|
||||||
|
| `GET` | `/api/v1/admin/workers` | 盘点全部副本(inline 本地注册 + external liveliness 发现) |
|
||||||
|
| `POST` | `/api/v1/admin/workers/restart` | 重启单个副本(`worker_type` / `worker_id` 查询参数) |
|
||||||
|
| `POST` | `/api/v1/admin/workers/<type>/stop` | 停止单个副本 / 某插件全部副本 |
|
||||||
|
| `GET` | `/api/v1/admin/workers/<type>/config` | 读取现有 `[workers.config]`(响应含 `source`/`mode` 标注取值来路) |
|
||||||
|
| `POST` | `/api/v1/admin/workers/<type>/config` | 更新配置(body `{updates:{k:v}}`),下发并重启 |
|
||||||
|
| `POST` | `/api/v1/admin/workers/<type>/scale` | 调整副本数(body `{count:<全局总量>}`),全局对称分摊 |
|
||||||
|
| `GET` | `/api/v1/admin/workers/<type>/instances` | 按进程实例(instance_id)分组读取副本 |
|
||||||
|
| `POST` | `/api/v1/admin/workers/<type>/scale_instance` | 逐实例精细扩缩(body `{instance_id, count}`) |
|
||||||
|
|
||||||
|
## 3. 配置热更新
|
||||||
|
|
||||||
|
- 回写插件 `config.toml` 的 `[workers]` 段(保留其他段),拒绝写保留键(`type` / `module` /
|
||||||
|
`count` 等路由与加载控制键)。
|
||||||
|
- 插件按新配置先 `on_cleanup()` 释放旧资源、再 `on_init()` 重建客户端(AI/DB client 等),
|
||||||
|
配置**无需重启网关即生效**。
|
||||||
|
- 仅 index0 副本落盘避免并发写冲突;其余副本同样应用新配置。
|
||||||
|
|
||||||
|
## 4. 扩缩容语义
|
||||||
|
|
||||||
|
- **扩容**:新增副本槽位并注册 liveliness,随即参与负载均衡。
|
||||||
|
- **缩容**:多余副本走 `drain` 优雅排空——先隔绝新请求,等在途请求完成后关闭,避免中断业务。
|
||||||
|
- **持久化**:目标数量写回插件 `config.toml` 的 `[workers].count`,重启后按新数量加载。
|
||||||
|
- **全局对称 vs 逐实例**:`/scale` 调整某类型的**全局总量**(多实例下由各实例均分);
|
||||||
|
需单独调整**某一个**外部实例时用 `/scale_instance`。
|
||||||
|
|
||||||
|
## 5. 多实例收敛(副本数分摊 / 领导选举 / MQTT Bridge 单例)
|
||||||
|
|
||||||
|
多网关或多进程部署时,以下三件事都以**共享 `[cache]` 为单一事实来源**:
|
||||||
|
|
||||||
|
```toml
|
||||||
|
[cache]
|
||||||
|
backend = "redis" # 多实例必须共享(redis 或共享卷 sqlite);单机 sqlite 即可
|
||||||
|
launch_worker_count = "toml" # 副本数/运行时配置以 toml(默认)还是 cache 为准
|
||||||
|
```
|
||||||
|
|
||||||
|
**前提**:所有网关与 external 进程指向同一 cache 后端。若各实例各用本机 cache,
|
||||||
|
副本数无法收敛、锁跨不了主机,多实例行为会退化。
|
||||||
|
|
||||||
|
### 5.1 副本数(count)按在线实例分摊
|
||||||
|
|
||||||
|
- `count` 语义为**该类 Worker 的全局目标总量**,各参与者按在线数量自动分摊。
|
||||||
|
- 在线清单与心跳:网关启动登记、每 15s 心跳续约,超 60s 未续约的实例自动清理;关闭时注销。
|
||||||
|
同宿主多网关需显式设置 `CHONGMING_GATEWAY_ID`(缺省回退 hostname)。
|
||||||
|
- external 进程实例同样登记参与分摊,避免多实例把副本数翻倍;实例标识用
|
||||||
|
`--instance` / `CHONGMING_WORKER_INSTANCE`(缺省 `{hostname}:{pid}`,跨重启持续命中请显式指定)。
|
||||||
|
- `/scale` 写全局 count 后向该类型全部存活实例广播 `rescale`,各实例按同一份数据计算 quota,
|
||||||
|
重复下发幂等。
|
||||||
|
|
||||||
|
### 5.2 inline 领导选举(多 worker 兼容)
|
||||||
|
|
||||||
|
- 同一网关的多个服务器 worker(gunicorn `-w` 等)中,同一时刻仅一个进程托管 inline worker
|
||||||
|
(副本数精确等于 `count`,而非 worker 数 × count);其余进程只做 HTTP 转发。
|
||||||
|
- 持有者崩溃后,其余进程经周期性巡检抢锁接管并拉起 worker(`set_nx` 唯一胜出,无双持有)。
|
||||||
|
- key 按 `gateway_id` 隔离,多网关各自选举、互不抢占。
|
||||||
|
|
||||||
|
### 5.3 MQTT Bridge 单例(多实例免重复响应)
|
||||||
|
|
||||||
|
- Bridge 启动采用共享缓存分布式锁(`set_nx` + 租约续约),全局仅持锁网关消费 MQTT 请求,
|
||||||
|
避免共享外部 Broker 时「一条请求被各消费一次、各回一份响应」。
|
||||||
|
- 持有者失联后租约到期,其余网关经周期巡检(`bridge_supervise_interval`,默认 15s)自动接管重建。
|
||||||
|
- 内嵌 Broker 单例按宿主隔离(`mqtt_broker:<hostname>`):同宿主多进程仅启动一个 Broker,
|
||||||
|
接管时会清理前任遗留的孤儿 Broker 子进程。
|
||||||
|
|
||||||
|
### 5.4 部署形态速查
|
||||||
|
|
||||||
|
| 部署形态 | MQTT Broker | `[cache]` 后端 |
|
||||||
|
| -------- | ----------- | -------------- |
|
||||||
|
| 单机(含多 worker) | 内嵌 amqtt | 本地 `sqlite` |
|
||||||
|
| 多实例分布式 | **外部 Broker(必须)** | **redis**(或共享卷 `sqlite`) |
|
||||||
|
|
||||||
|
## 6. 逐实例精细扩缩
|
||||||
|
|
||||||
|
同一类型跑多个外部进程(多机/多容器)时,除全局对称扩缩外,可只对**某一个实例**精确扩缩:
|
||||||
|
|
||||||
|
- **语义**:把目标实例的副本数设为绝对值 N,其它实例不动;与全局 `scale` 共存——
|
||||||
|
全局对称是默认/一键复位(`scale` 会清除整张覆盖表回到均分),逐实例是定制覆盖。
|
||||||
|
- **覆盖表**:cache 键 `worker:cm:instcounts:<type>` = `{instance_id: 绝对副本数}`;
|
||||||
|
无记录的实例回退到全局均分。
|
||||||
|
- **命中判断**:`rescale` 命中本实例(`instance_id` 与登记一致)才执行,目标是其它实例则忽略。
|
||||||
|
- **REST / UI**:`POST /admin/workers/<type>/scale_instance` 下发;
|
||||||
|
`GET /admin/workers/<type>/instances` 供前端按实例分组展示。
|
||||||
|
|
||||||
|
## 7. 实例定向调用
|
||||||
|
|
||||||
|
默认逐请求**随机负载均衡**,适合无状态插件。若插件持有只存在于某个进程本地的状态
|
||||||
|
(本地 SQLite/文件、串口、设备连接),可用实例定向把请求固定到该进程:
|
||||||
|
|
||||||
|
- **指定方式**(三入口语义一致):
|
||||||
|
- HTTP 请求头 `X-Chongming-Instance: <实例id>`
|
||||||
|
- MQTT 信封载荷顶层 `target_instance`(网关读后摘除,业务侧不可见)
|
||||||
|
- 程序内 `worker_manager.call(...)` / `call_raw(...)` / `call_stream(...)` 的 `instance_id=` 参数
|
||||||
|
- **实例 id 取值**:`--instance` → `CHONGMING_WORKER_INSTANCE` → `CHONGMING_GATEWAY_ID` →
|
||||||
|
`{hostname}:{pid}`;容器环境(K8s 注入 `CHONGMING_GATEWAY_ID`)下两侧自动一致,
|
||||||
|
裸机同宿主多网关请显式指定。可读列表见 `GET /api/v1/admin/workers/<type>/instances`。
|
||||||
|
- **粒度**:定向到**进程实例**;实例内有多个副本时,请求仍在这几个副本间随机均衡。
|
||||||
|
- **实例离线时严格报错**(HTTP 503 / MQTT `{"error": ...}`),**不静默回退**到其它实例。
|
||||||
|
- **重试不跨实例**;流式端点不重试,实例离线即失败。
|
||||||
|
- **缓存按实例隔离**:定向且 `cacheable=true` 时缓存键带实例前缀。
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# 把这次请求固定到实例 edge-01 的进程
|
||||||
|
curl -H 'X-Chongming-Instance: edge-01' http://127.0.0.1:8000/api/v1/demo/state
|
||||||
|
|
||||||
|
# MQTT:载荷顶层带 target_instance(网关转发前摘掉)
|
||||||
|
# topic: chongming/demo/state payload: {"meta":{}, "data":{}, "target_instance":"edge-01"}
|
||||||
|
```
|
||||||
|
|
||||||
|
## 8. 前端管理页
|
||||||
|
|
||||||
|
`Worker 管理`(`/workers`):
|
||||||
|
|
||||||
|
- **登录**:内置 `POST /auth/login` 领取管理 Token(不依赖 auth 插件),仅 `is_superuser` 可操作。
|
||||||
|
- **列表**:副本类型 / 副本 ID / 模式(inline|external)/ 状态,支持关键字、模式、副本数筛选与分页;
|
||||||
|
筛选同时作用于下方逐实例分组表。`worker_type == "auth"` 的配置项不展示、不可改。
|
||||||
|
- **操作**:单副本重启、批量重启、插件级重启、配置编辑(结构化 JSON 校验/格式化)、
|
||||||
|
**调整副本数量**(全局对称)、**逐实例精细扩缩**(按实例分组,仅调该实例)。
|
||||||
|
- **自动刷新**:每 10s 轮询列表与分组表,异步操作结果自动反映。
|
||||||
|
- 调小副本数会触发优雅排空,前端确认提示中会说明。
|
||||||
|
|
||||||
|
## 9. external 插件的可管理性前提
|
||||||
|
|
||||||
|
1. 插件容器运行含管理能力(`__admin__` + `reload` + `rescale`)的 `chongming-worker`;
|
||||||
|
2. `config_set` / `rescale` 由副本在**其进程 cwd** 下写 `config/workers/<type>/config.toml`,
|
||||||
|
容器需挂载与网关同款的配置目录才能写回(含 `[workers].count` 持久化);
|
||||||
|
3. 其它语言自行实现的进程插件需实现 `__admin__` Queryable + liveliness + 数量管理,
|
||||||
|
否则只能被发现,无法改配置或扩缩容。
|
||||||
|
|
||||||
|
## 10. 多 worker 与 worker 副本的分工
|
||||||
|
|
||||||
|
gunicorn `-w N` 提升的是**网关的 HTTP 接入/转发能力**;插件计算能力由 Worker **副本数**
|
||||||
|
(`count`)或 external 进程数决定。高并发场景按瓶颈扩:转发瓶颈加 `-w`,计算瓶颈加 `count`
|
||||||
|
或改跑 external。
|
||||||
Reference in New Issue
Block a user