modify:进程插件开发环境目录结构变更
This commit is contained in:
+80
-61
@@ -13,7 +13,7 @@
|
||||
| 模式 | 运行位置 | 目录 | 路由来源 | 契约校验位置 |
|
||||
| ---- | -------- | ---- | -------- | ------------ |
|
||||
| **inline**(默认) | 网关进程内 import | `src/plugins/<name>/`(chongming-plugins 仓库) | 网关盘扫 TOML 生成 | 网关启动时 |
|
||||
| **external(进程插件)** | 独立进程 | `workers/<name>/`(本地开发目录,**不入库**) | Worker 经 Zenoh 发布路由描述符,网关动态挂载 | **Worker 进程内** |
|
||||
| **external(进程插件)** | 独立进程 | `workers/<name>/`(本地开发目录,**自包含项目**,不入库) | Worker 经 Zenoh 发布路由描述符,网关动态挂载 | **Worker 进程内** |
|
||||
|
||||
进程插件的特点:
|
||||
|
||||
@@ -89,40 +89,48 @@ WorkerManager 据此做负载均衡,网关侧无需任何改动。
|
||||
|
||||
## 3. 目录结构约定
|
||||
|
||||
进程插件代码放在本地开发目录 `workers/<name>/`(**gitignore,不入库**):
|
||||
进程插件是**自包含项目**:`workers/<name>/` 即项目根,同时也是打包 zip 的根
|
||||
(**gitignore,不入库**)。开发目录与容器工作目录完全一致,开发即产物:
|
||||
|
||||
```text
|
||||
workers/<name>/
|
||||
├── __init__.py # Plugin 子类 + create_workers / cleanup 模块级入口
|
||||
├── global_var.py # 模块级共享状态(Database、API client 等)
|
||||
├── router/ # RPC 端点实现
|
||||
│ ├── __init__.py # 导入所有端点模块
|
||||
│ └── <操作名>.py # 每个端点一个文件,@rpc_method 装饰器
|
||||
├── models/ # 数据模型(gen_models 自动生成,勿手动修改)
|
||||
│ ├── __init__.py
|
||||
│ └── pydantic.py # 请求/响应 Pydantic 模型
|
||||
├── requirements.txt # 扩展依赖(可选,每行一个包,打包时并入 pyproject.toml)
|
||||
└── README.md # 插件说明(可选)
|
||||
workers/<name>/ # 项目根 = zip 根 = 容器工作目录
|
||||
├── workers/<name>/ # 插件代码(模块 workers.<name>)
|
||||
│ ├── __init__.py # Plugin 子类 + create_workers / cleanup 模块级入口
|
||||
│ ├── global_var.py # 模块级共享状态(Database、API client 等)
|
||||
│ ├── router/ # RPC 端点实现
|
||||
│ │ ├── __init__.py # 导入所有端点模块
|
||||
│ │ └── <操作名>.py # 每个端点一个文件,@rpc_method 装饰器
|
||||
│ ├── models/ # 数据模型(gen_models 自动生成,勿手动修改)
|
||||
│ │ ├── __init__.py
|
||||
│ │ └── pydantic.py # 请求/响应 Pydantic 模型
|
||||
│ └── README.md # 插件说明(可选)
|
||||
├── config/
|
||||
│ ├── zenoh_config.json5 # Zenoh 会话配置
|
||||
│ └── workers/<name>/ # 插件注册 + 端点配置
|
||||
│ ├── config.toml # [workers] 节注册 + [worker] 元信息 + [workers.config]
|
||||
│ └── <操作名>.toml # 每个端点一个定义文件
|
||||
├── config.toml # 最小根配置(仅 [zenoh] 节,worker 启动必需)
|
||||
├── pyproject.toml # uv 依赖清单(chongming-worker + 扩展依赖)
|
||||
└── start.sh # 容器入口(uv sync + uv run worker_serve --type <name>)
|
||||
```
|
||||
|
||||
> 参考示例:`config/workers/demo/` 中的注册与端点配置(`demo` / `hello.toml`),
|
||||
> 以及 `workers/demo/`(本地目录,配合 `test_worker_isolation.py` 使用)。
|
||||
> 参考示例:`workers/demo/`(完整项目,含 5 个端点,配合 `test_worker_isolation.py` /
|
||||
> `test_process_mode_e2e.py` 使用)。
|
||||
|
||||
---
|
||||
|
||||
## 4. 开发流程
|
||||
|
||||
### 第 1 步:创建插件代码
|
||||
### 第 1 步:生成项目骨架
|
||||
|
||||
```bash
|
||||
mkdir -p workers/demo/router
|
||||
mkdir -p workers/demo/models
|
||||
touch workers/demo/__init__.py
|
||||
touch workers/demo/global_var.py
|
||||
touch workers/demo/router/__init__.py
|
||||
uv run gen_plugin demo --process # 生成完整进程插件项目(含示例操作)
|
||||
uv run gen_plugin demo hello storage --process # 指定初始操作
|
||||
uv run gen_plugin demo add --process # 为已有项目增量添加操作
|
||||
```
|
||||
|
||||
`workers/demo/__init__.py`(与 inline 插件完全一致,推荐 Plugin 基类风格):
|
||||
生成的代码位于 `workers/demo/workers/demo/`,其中 `__init__.py`(与 inline 插件
|
||||
完全一致,推荐 Plugin 基类风格):
|
||||
|
||||
```python
|
||||
import logging
|
||||
@@ -154,15 +162,16 @@ cleanup = _plugin.cleanup
|
||||
```
|
||||
|
||||
> 注意:`module` 注册为 `workers.<name>` 时,`sys.path` 需包含项目根目录——
|
||||
> `worker_serve` 已自动处理(console script 启动时会显式把 CWD 加入 `sys.path`)。
|
||||
> `worker_serve` 已自动处理(console script 启动时会显式把 CWD 加入 `sys.path`),
|
||||
> 只需在项目根 `workers/<name>/` 下运行。
|
||||
|
||||
### 第 2 步:插件注册配置
|
||||
|
||||
创建 `config/workers/<name>/config.toml`,`[workers]` 节注册插件
|
||||
创建 `workers/<name>/config/workers/<name>/config.toml`,`[workers]` 节注册插件
|
||||
(**关键:`load_mode = "external"`**):
|
||||
|
||||
```toml
|
||||
# config/workers/demo/config.toml
|
||||
# workers/demo/config/workers/demo/config.toml
|
||||
[workers]
|
||||
type = "demo" # 插件类型(worker type)
|
||||
module = "workers.demo" # Python 模块路径(进程插件用 workers.<name>)
|
||||
@@ -188,7 +197,7 @@ greeting_prefix = "Hello"
|
||||
|
||||
### 第 3 步:端点 TOML 定义
|
||||
|
||||
每个端点一个文件,如 `config/workers/<name>/hello.toml`:
|
||||
每个端点一个文件,如 `workers/<name>/config/workers/<name>/hello.toml`:
|
||||
|
||||
```toml
|
||||
[GET]
|
||||
@@ -207,14 +216,14 @@ timestamp = { type = "float", required = true }
|
||||
### 第 4 步:生成 Pydantic 模型
|
||||
|
||||
```bash
|
||||
uv run gen_models config/workers/demo workers/demo
|
||||
uv run gen_models workers/demo/config/workers/demo workers/demo/workers/demo
|
||||
```
|
||||
|
||||
在 `workers/<name>/models/` 下生成 `pydantic.py` 与 `__init__.py`。
|
||||
在 `workers/<name>/workers/<name>/models/` 下生成 `pydantic.py` 与 `__init__.py`。
|
||||
|
||||
### 第 5 步:实现 RPC 端点
|
||||
|
||||
`workers/<name>/router/hello.py`:
|
||||
`workers/<name>/workers/<name>/router/hello.py`:
|
||||
|
||||
```python
|
||||
import time
|
||||
@@ -232,7 +241,7 @@ async def hello(request: HelloGetRequest) -> HelloGetResponse:
|
||||
)
|
||||
```
|
||||
|
||||
`workers/<name>/router/__init__.py`:
|
||||
`workers/<name>/workers/<name>/router/__init__.py`:
|
||||
|
||||
```python
|
||||
from . import hello
|
||||
@@ -253,17 +262,23 @@ __all__ = ["hello"]
|
||||
|
||||
### 第 6 步:本地调试
|
||||
|
||||
在**插件项目根** `workers/<name>/` 下运行(worker 以 CWD 为工作目录读配置/import 代码):
|
||||
|
||||
```bash
|
||||
uv run worker_serve --type demo # 本地调试(开发环境)
|
||||
uv run worker_serve --type demo --count 1 # 覆盖副本数
|
||||
chongming-worker --type demo # 容器入口(console script)
|
||||
cd workers/demo
|
||||
uv sync # 首次:安装依赖(项目独立 venv,与容器一致)
|
||||
uv run worker_serve --type demo # 本地调试
|
||||
uv run worker_serve --type demo --count 1 # 覆盖副本数
|
||||
./start.sh # 与容器一致的启动方式
|
||||
# 开发期复用仓库 venv(已装好 SDK)时,用 --project 指向仓库根:
|
||||
uv run --project .. worker_serve --type demo
|
||||
```
|
||||
|
||||
启动后预期日志:
|
||||
|
||||
```text
|
||||
[ demo ] 路由发布完成: 1 端点 + 无元信息
|
||||
[ demo ] Worker 进程就绪,等待退出信号...
|
||||
[demo] 路由发布完成: 5 端点 + 无元信息
|
||||
[demo] Worker 进程就绪,等待退出信号...
|
||||
```
|
||||
|
||||
> 若配置了 `load_mode` 非 `"external"`,会输出告警:网关也会在进程内加载该插件,
|
||||
@@ -272,10 +287,10 @@ chongming-worker --type demo # 容器入口(console script
|
||||
### 第 7 步:验证
|
||||
|
||||
```bash
|
||||
# 1. 进程隔离性(阻断 chongming 导入 + AST 检查 import 语句)
|
||||
# 1. 进程隔离性(阻断 chongming 导入 + AST 检查 import 语句;自动切到 demo 项目根)
|
||||
uv run python test_worker_isolation.py
|
||||
|
||||
# 2. 端到端(需先启动网关 uv run serve,且被测插件 load_mode = "external"):
|
||||
# 2. 端到端(需先启动网关 uv run serve):从 demo 项目根拉起 worker 进程,
|
||||
# 路由动态注册 → 跨进程 RPC → 退出摘除(404) → 重启恢复
|
||||
uv run python test_process_mode_e2e.py
|
||||
```
|
||||
@@ -287,26 +302,28 @@ uv run build_plugin demo # → dist/demo.zip
|
||||
uv run build_plugin demo --output /tmp/pkg # 指定输出目录
|
||||
```
|
||||
|
||||
打包前校验(任一不满足即失败退出):
|
||||
打包 = 项目根直接压缩(开发目录即产物,zip 根即容器工作目录),自动排除
|
||||
`__pycache__` / `*.pyc` / `.venv` / `*.egg-info` / `.git`,`start.sh` 权限强制 0o755。
|
||||
|
||||
- `workers/<name>/` 插件代码目录存在
|
||||
- `config/workers/<name>/config.toml` 存在且含 `[workers]` 节
|
||||
- `config/zenoh_config.json5` 存在
|
||||
- `load_mode` 非 `"external"` 时**告警**(不阻断,但会造成重复服务)
|
||||
|
||||
产物布局(**zip 根即容器工作目录**):
|
||||
产物布局与 §3 完全一致(**zip 根即容器工作目录**):
|
||||
|
||||
```text
|
||||
workers/<name>/ 插件代码(模块 workers.<name>)
|
||||
config/workers/<name>/ 插件注册 + 端点配置
|
||||
config/zenoh_config.json5 Zenoh 会话配置
|
||||
config.toml 最小根配置(自动生成,仅 [zenoh] 节,不泄露网关配置)
|
||||
config.toml 最小根配置(仅 [zenoh] 节,worker 启动必需)
|
||||
pyproject.toml uv 依赖清单(chongming-worker 内网 git 源 + orjson + 扩展依赖)
|
||||
start.sh 容器入口:uv sync --no-dev + uv run worker_serve --type <name>
|
||||
```
|
||||
|
||||
扩展依赖:`workers/<name>/requirements.txt` 中逐行声明(`#` 开头为注释),
|
||||
打包时自动合并进 `pyproject.toml` 的 dependencies。
|
||||
打包前校验(任一不满足即失败退出):
|
||||
|
||||
- `workers/<name>/` 项目根存在
|
||||
- `workers/<name>/config/workers/<name>/config.toml` 存在且含 `[workers]` 节
|
||||
- `workers/<name>/config/zenoh_config.json5` 与 `workers/<name>/config.toml`(含 `[zenoh]`)存在
|
||||
- `load_mode` 非 `"external"` 时**告警**(不阻断,但会造成重复服务)
|
||||
|
||||
扩展依赖:直接在项目根 `pyproject.toml` 的 `dependencies` 中追加(如 `"minio"`)。
|
||||
|
||||
### 第 9 步:容器部署
|
||||
|
||||
@@ -321,7 +338,7 @@ cd /app
|
||||
- 生产部署时进程插件与网关**分别打包到不同容器**:
|
||||
- 网关容器:`chongming` + 依赖
|
||||
- 进程插件容器:插件代码 + `chongming-worker` + 依赖
|
||||
- `config/workers/<name>/` 仅部署在**插件容器**(worker 自读);网关侧无需放置
|
||||
- 插件配置随 zip 部署在**插件容器**(worker 自读);网关侧无需放置
|
||||
(RouteRegistry 常驻订阅 Zenoh 路由描述符,与盘扫解耦)
|
||||
|
||||
---
|
||||
@@ -335,7 +352,7 @@ cd /app
|
||||
### 5.1 配置
|
||||
|
||||
```toml
|
||||
# config/workers/<name>/config.toml
|
||||
# workers/<name>/config/workers/<name>/config.toml
|
||||
[workers.config.storage]
|
||||
endpoint = "192.168.1.10:9000" # Minio 地址 host:port(不带协议前缀)
|
||||
access_key = "minioadmin"
|
||||
@@ -395,7 +412,7 @@ API 一览(`chongming_worker.storage.StorageClient`):
|
||||
|
||||
### 5.3 参考示例
|
||||
|
||||
`workers/demo` 演示了 4 个文件传输端点(`config/workers/demo/` 下对应 TOML):
|
||||
`workers/demo` 演示了 4 个文件传输端点(项目内 `config/workers/demo/` 下对应 TOML):
|
||||
|
||||
- `POST /demo/presign_upload` — filename → object_key + 预签名 PUT URL(设备直传)
|
||||
- `GET /demo/presign_download` — object_key → 预签名 GET URL(设备直取)
|
||||
@@ -405,7 +422,7 @@ API 一览(`chongming_worker.storage.StorageClient`):
|
||||
配置后重启 worker 即可用;未配置时这些端点返回「storage 未配置」错误(降级演示)。
|
||||
|
||||
> 依赖:minio SDK 随 `chongming-worker` 一起安装(utils/worker pyproject 已声明),
|
||||
> 进程插件无需额外 `requirements.txt`。SDK 侧 `chongming_worker.storage` 不依赖网关包,
|
||||
> 进程插件无需额外依赖。SDK 侧 `chongming_worker.storage` 不依赖网关包,
|
||||
> 隔离性约束与打包流程均不受影响。
|
||||
|
||||
---
|
||||
@@ -416,7 +433,7 @@ API 一览(`chongming_worker.storage.StorageClient`):
|
||||
|
||||
- `chongming-worker`(worker SDK:`chongming_worker.*`)
|
||||
- `zenoh` / `pydantic` 等第三方库
|
||||
- 插件自身的扩展依赖(requirements.txt)
|
||||
- 插件自身的扩展依赖(项目根 `pyproject.toml` 中声明)
|
||||
|
||||
由 `test_worker_isolation.py` 双重保证:
|
||||
|
||||
@@ -445,12 +462,12 @@ Worker 进程启动时自读 TOML 并**在自己进程内**做三方一致性校
|
||||
|
||||
| 现象 | 原因 / 处理 |
|
||||
| ---- | ----------- |
|
||||
| 启动告警「load_mode 不是 external」 | 进程插件必须在 `config/workers/<name>/config.toml` `[workers]` 节设置 `load_mode = "external"`,否则网关内副本与外部进程重复服务 |
|
||||
| 契约校验失败、进程退出 | 检查 TOML 端点(subject/方法节)与 `@rpc_method(subject=..., method=...)` 是否一致;模型缺失时重跑 `uv run gen_models config/workers/<name> workers/<name>` |
|
||||
| 启动告警「load_mode 不是 external」 | 进程插件必须在 `workers/<name>/config/workers/<name>/config.toml` `[workers]` 节设置 `load_mode = "external"`,否则网关内副本与外部进程重复服务 |
|
||||
| 契约校验失败、进程退出 | 检查 TOML 端点(subject/方法节)与 `@rpc_method(subject=..., method=...)` 是否一致;模型缺失时重跑 `uv run gen_models workers/<name>/config/workers/<name> workers/<name>/workers/<name>` |
|
||||
| `TypeError: rpc_method() got an unexpected keyword argument 'stream'` | venv 里是旧版 chongming-worker(git 源拉取),本地未 push 的框架改动不生效;联调时 `uv pip install -e utils/worker` |
|
||||
| 路由没出现 | worker 是否就绪(日志「路由发布完成」);网关 RouteRegistry 是否订阅(启动流程 5.1);每 10s 自愈重发布,稍等或重启 worker |
|
||||
| 路由一直 404 | worker 是否已退出(优雅退出会摘除路由);进程被 SIGKILL 时描述符残留,重启 worker 后重发布自愈覆盖 |
|
||||
| 找不到插件配置 | `worker_serve` 须在项目根目录下运行(读 `config/workers/<name>/config.toml`,`--config` 可指定全局配置路径) |
|
||||
| 找不到插件配置 | `worker_serve` 须在**插件项目根** `workers/<name>/` 下运行(读 `config/workers/<name>/config.toml`,`--config` 可指定全局配置路径) |
|
||||
| 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 |
|
||||
|
||||
@@ -459,12 +476,14 @@ Worker 进程启动时自读 TOML 并**在自己进程内**做三方一致性校
|
||||
## 9. 相关命令速查
|
||||
|
||||
```bash
|
||||
uv run worker_serve --type <name> # 本地调试独立 Worker 进程
|
||||
chongming-worker --type <name> # 容器入口(console script)
|
||||
uv run build_plugin <name> [--output <dir>] # 进程插件打包 → dist/<name>.zip
|
||||
uv run gen_models config/workers/<name> workers/<name> # 生成模型到进程插件目录
|
||||
uv run python test_worker_isolation.py # 进程隔离性测试
|
||||
uv run python test_process_mode_e2e.py # 端到端测试(先启动网关)
|
||||
uv run gen_plugin <name> --process # 生成进程插件项目骨架
|
||||
cd workers/<name> # 以下均在插件项目根下执行
|
||||
uv run worker_serve --type <name> # 本地调试独立 Worker 进程
|
||||
./start.sh # 与容器一致的启动方式(uv sync + 启动)
|
||||
cd .. && uv run build_plugin <name> # 进程插件打包 → dist/<name>.zip
|
||||
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 # 端到端测试(先启动网关)
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
Reference in New Issue
Block a user