Files
chongming-core/docs/worker-management.md
T

164 lines
9.8 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.
# 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。