modify: 说明文档

This commit is contained in:
2026-08-05 18:19:01 +08:00
parent ed8550ddc3
commit f1e6c52311
+111 -48
View File
@@ -1,38 +1,49 @@
# Worker 插件开发指南 # Worker 插件开发指南
每个子目录代表一个独立的 Worker 插件,封装特定领域的业务逻辑。插件通过 Zenoh RPC 对外提供服务, 每个 `src/plugins/<name>/` 子目录是一个独立的 Worker 插件,封装特定领域的业务逻辑。插件通过 Zenoh RPC 对外提供服务,HTTP 路由由框架根据 TOML 配置自动生成。
HTTP 路由由框架根据 TOML 配置自动生成。
--- ---
## 目录结构约定 ## 目录结构约定
```text ```text
plugin_name/ <plugin_name>/
├── __init__.py # 入口:create_workers() 工厂 + cleanup() ├── __init__.py # 入口:create_workers() 工厂 + cleanup() + auto_bind_models()
├── global_var.py # 模块级全局状态(数据库连接、API 客户端等) ├── global_var.py # 模块级全局状态(Database、API client 等)
├── router/ # RPC 端点实现 ├── router/ # RPC 端点实现
│ ├── __init__.py # 导入所有端点模块,__all__ 导出 │ ├── __init__.py # 导入所有端点模块(`from . import *` 或显式导入)
│ └── <操作名>.py # 每个端点一个文件,使用 @rpc_method 装饰器 │ └── <操作名>.py # 每个端点一个文件,使用 @rpc_method 装饰器
├── models/ # 数据模型 ├── models/ # 数据模型
│ ├── __init__.py # 模型导出(自动生成,勿手动修改) │ ├── __init__.py # 模型导出(由 gen_models 自动生成,勿手动修改)
│ ├── pydantic.py # Pydantic 请求/响应模型(由 gen_models.py 自动生成) │ ├── pydantic.py # Pydantic 请求/响应模型(由 gen_models 自动生成)
│ └── <表名>.py # SQLModel ORM 模型(如需要数据库) │ └── <表名>.py # SQLModel ORM 模型(如需要数据库)
└── README.md # 插件说明文档 ├── routes.py # 自定义 FastAPI 路由(可选,用于特殊端点)
└── README.md # 插件说明文档(可选)
``` ```
--- ---
## 已有插件概览
| 插件 | 特点 | 适合参考 |
| ------------------------ | ------------------------------------------------- | ---------- |
| **example** | 最简示例,`auto_bind_models` + Database + cleanup | 入门学习 |
| **auth** | SQLModel ORM + 管理员初始化 + 多进程幂等 | 数据库操作 |
| **doubao** | 外部 API 客户端(Ark/OpenAI)+ AICC 安全通道 | 外部集成 |
| **aibox_sophon_adapter** | 自定义 `APIRouter` + MQTT 转发 + 后台任务 | 高级模式 |
---
## 开发新插件:完整流程 ## 开发新插件:完整流程
### 第一步:创建目录结构 ### 第一步:创建目录结构
```bash ```bash
mkdir -p src/plugins/<plugin_name>/router mkdir -p src/plugins/<name>/router
mkdir -p src/plugins/<plugin_name>/models mkdir -p src/plugins/<name>/models
touch src/plugins/<plugin_name>/__init__.py touch src/plugins/<name>/__init__.py
touch src/plugins/<plugin_name>/global_var.py touch src/plugins/<name>/global_var.py
touch src/plugins/<plugin_name>/router/__init__.py touch src/plugins/<name>/router/__init__.py
``` ```
### 第二步:注册插件 ### 第二步:注册插件
@@ -41,20 +52,20 @@ touch src/plugins/<plugin_name>/router/__init__.py
```toml ```toml
[[workers]] [[workers]]
type = "<plugin_name>" type = "<name>"
module = "plugins.<plugin_name>" module = "plugins.<name>"
factory = "create_workers" factory = "create_workers"
cleanup = "cleanup" cleanup = "cleanup"
count = 3 count = 3
[workers.config] [workers.config]
type = "<plugin_name>" type = "<name>"
# 自定义配置项... data_dir = "./data"
``` ```
### 第三步:编写端点 TOML 配置 ### 第三步:编写端点 TOML 配置
创建 `config/workers/<plugin_name>/config.toml`(分组配置): 创建 `config/workers/<name>/config.toml`(分组配置):
```toml ```toml
[worker] [worker]
@@ -62,9 +73,10 @@ defaults = { timeout = 3.0, auth_required = true, cacheable = false, internal =
[worker.registration] [worker.registration]
router_prefix = "/<prefix>" router_prefix = "/<prefix>"
tags = ["<name>"]
``` ```
创建每个操作的 `.toml` 文件,如 `config/workers/<plugin_name>/my_action.toml`: 创建每个操作的 `.toml` 文件,如 `config/workers/<name>/my_action.toml`:
```toml ```toml
[POST] [POST]
@@ -82,30 +94,30 @@ message = { type = "str", default = "" }
uv run python -m chongming.scripts.gen_models uv run python -m chongming.scripts.gen_models
``` ```
这会自动在 `src/plugins/<plugin_name>/models/` 下生成 `pydantic.py` 和更新 `__init__.py`。 自动在 `src/plugins/<name>/models/` 下生成 `pydantic.py` 和 `__init__.py`。
### 第五步:实现 RPC 端点 ### 第五步:实现 RPC 端点
`src/plugins/<plugin_name>/router/my_action.py`: `src/plugins/<name>/router/my_action.py`:
```python ```python
from chongming_worker.decorators import rpc_method from chongming_worker.decorators import rpc_method
from ..models.pydantic import MyActionPostRequest, MyActionPostResponse from ..models.pydantic import MyActionPostRequest, MyActionPostResponse
@rpc_method( @rpc_method(
subject="<plugin_name>.my_action", subject="<name>.my_action",
method="POST", method="POST",
response_model=MyActionPostResponse, response_model=MyActionPostResponse,
) )
async def my_action(request: MyActionPostRequest) -> MyActionPostResponse: async def my_action(request: MyActionPostRequest) -> MyActionPostResponse:
"""业务逻辑实现""" """业务逻辑"""
result = await do_something(request) result = await do_something(request)
return MyActionPostResponse(result="success", message="done") return MyActionPostResponse(result="success", message="done")
``` ```
### 第六步:更新 router 导出 ### 第六步:更新 router 导出
`src/plugins/<plugin_name>/router/__init__.py`: `src/plugins/<name>/router/__init__.py`:
```python ```python
from . import my_action from . import my_action
@@ -114,23 +126,27 @@ __all__ = ["my_action"]
### 第七步:实现工厂函数 ### 第七步:实现工厂函数
`src/plugins/<plugin_name>/__init__.py`: `src/plugins/<name>/__init__.py`:
```python ```python
from pathlib import Path
from chongming_worker import auto_bind_models
from chongming_worker.runtime import create_workers as _create_workers from chongming_worker.runtime import create_workers as _create_workers
from .router import * # 触发 @rpc_method 注册
def create_workers(session, config, count, worker_manager): auto_bind_models("<name>") # 自动绑定生成的 Pydantic 模型
"""插件入口:初始化全局资源,启动 Worker"""
from . import router # 触发 @rpc_method 注册 async def create_workers(session, config, count, worker_manager):
"""插件入口:初始化资源,启动 Worker"""
from chongming_database import Database
from .global_var import set_db from .global_var import set_db
# 初始化数据库、客户端等 db_path = Path(config.get("data_dir", "./data")) / "<name>.db"
from chongming_database import Database db = Database(db_path)
db = Database(config["data_dir"] / "mydb.db") await db.initialize()
# await db.initialize() -- 在异步环境中
set_db(db) set_db(db)
_create_workers(session, config, count, worker_manager)
return _create_workers(session, config, count, worker_manager)
async def cleanup(worker_manager): async def cleanup(worker_manager):
"""清理资源""" """清理资源"""
@@ -139,13 +155,45 @@ async def cleanup(worker_manager):
await db.dispose() await db.dispose()
``` ```
> **注意**:`create_workers` 必须是 `async` 函数,因为 `Database.initialize()` 是异步的。框架在 `asyncio.gather` 中并发调用所有插件的工厂函数。
---
## 自定义 FastAPI 路由(高级)
当端点无法用 TOML 描述时(如文件上传),插件可暴露自定义 `APIRouter`。
`src/plugins/<name>/routes.py`:
```python
from fastapi import APIRouter, UploadFile, File, Form
router = APIRouter(prefix="/<prefix>", tags=["<name>"])
@router.post("/upload")
async def upload(
file: UploadFile = File(...),
description: str = Form(...),
):
# 处理文件上传
return {"status": "ok"}
```
在 `__init__.py` 中暴露 `router`:
```python
from .routes import router # 网关通过 getattr 自动发现
```
网关在 `load_workers()` 阶段自动检测并注册该 router,无需修改网关代码。
--- ---
## 为现有插件添加新端点 ## 为现有插件添加新端点
1. 在 `config/workers/<plugin_name>/` 下创建 `<新操作>.toml` 1. 创建 `config/workers/<name>/<new_op>.toml`
2. 运行 `uv run python -m chongming.scripts.gen_models` 2. 运行 `uv run python -m chongming.scripts.gen_models`
3. 在 `router/` 下创建 `<新操作>.py`,引用生成的模型 3. 创建 `router/<new_op>.py`,引用生成的模型
4. 更新 `router/__init__.py` 4. 更新 `router/__init__.py`
5. 重启服务 5. 重启服务
@@ -153,13 +201,15 @@ async def cleanup(worker_manager):
## 全局变量模式 ## 全局变量模式
每个插件通过 `global_var.py` 管理模块级共享资源,避免跨请求的状态冲突: 每个插件通过 `global_var.py` 管理模块级共享资源:
```python ```python
# global_var.py # global_var.py
db: "Database | None" = None from chongming_database import Database
def set_db(database: "Database"): db: Database | None = None
def set_db(database: Database):
global db global db
db = database db = database
``` ```
@@ -169,35 +219,48 @@ def set_db(database: "Database"):
```python ```python
from ..global_var import db from ..global_var import db
@rpc_method(subject="plugin.action", method="GET", ...) @rpc_method(subject="<name>.action", method="GET", ...)
async def handler(request: RequestModel): async def handler(request):
async with db.get_session() as session: async with db.get_session() as session:
# 执行数据库操作
... ...
``` ```
--- ---
## 装饰器说明 ## @rpc_method 装饰器
```python ```python
@rpc_method( @rpc_method(
subject="<plugin>.<operation>", # Zenoh 主题(必需) subject="<plugin>.<operation>", # Zenoh 主题(必需)
method="GET", # HTTP 方法:GET 或 POST method="GET", # HTTP 方法:GET / POST / MQTT
response_model=ResponseCls, # Pydantic 响应模型类 response_model=ResponseCls, # Pydantic 响应模型类
) )
async def handler(request: RequestCls) -> ResponseCls: async def handler(request: RequestCls) -> ResponseCls:
... ...
``` ```
- **GET 端点**:参数通过 URL query string 传递,在 TOML 中用 `params` 定义 | method | 参数来源 | 请求绑定 |
- **POST 端点**:参数通过 JSON body 传递,自动绑定到 Pydantic 请求模型 | ------ | ----------------- | ---------------------------- |
- **internal 端点**:设置 `internal = true` 后,不注册 HTTP 路由,仅可通过 Zenoh RPC 内部调用 | `GET` | URL query string | TOML `params` 定义 |
| `POST` | JSON body | 自动绑定到 Pydantic 请求模型 |
| `MQTT` | MQTT payload JSON | 同 POST |
- 设置 `internal = true` 后,端点不注册 HTTP 路由,仅可通过 Zenoh RPC 或 MQTT 内部调用
- MQTT 方法返回 `None` 时,Bridge 自动跳过响应发布
--- ---
## 命名约定 ## 命名约定
| 概念 | 约定 | 示例 |
| ------------- | ------------------------------ | ---------------------------- |
| 插件名 | 小写下划线 | `aibox_sophon_adapter` |
| 操作名 | 小写下划线 | `board_ping`, `list_devices` |
| Zenoh subject | `<插件>.<操作>` | `example.add` |
| TOML 文件名 | 与操作名一致 | `add.toml` |
| Python 文件名 | 与操作名一致 | `add.py` |
| 模型类名 | `<操作><方法>Request/Response` | `AddGetRequest` |
| 概念 | 约定 | 示例 | | 概念 | 约定 | 示例 |
| ----------------- | --------------- | ----------------------------------- | | ----------------- | --------------- | ----------------------------------- |
| 插件名 | 小写下划线 | `doubao`, `auth`, `example` | | 插件名 | 小写下划线 | `doubao`, `auth`, `example` |