# Chongming-Zenoh 基于 **FastAPI + Zenoh + MQTT** 的**配置驱动微服务网关平台**。 用 TOML 定义 RPC 端点,自动生成 Pydantic 模型与 FastAPI 路由,同时暴露 **HTTP** 与 **MQTT** 双通道;底层经 Zenoh 做 Worker 服务发现与 RPC 调用。插件(Worker)可独立开发、 按副本数水平扩展,既可跑在网关进程内(inline),也可作为独立进程/容器部署(external)。 ## 核心特性 | 特性 | 说明 | | ---- | ---- | | **双通道接入** | HTTP(FastAPI,自动 OpenAPI 文档)与 MQTT(内嵌或外部 Broker) | | **配置驱动路由** | 扫描 `config/workers/*/*.toml` 自动注册 HTTP 路由与 Pydantic 模型 | | **Zenoh RPC** | 基于 liveliness 的 Worker 发现与调用:负载均衡、超时重试、实例定向 | | **混合部署** | inline(网关进程内)与 external(独立进程)按插件粒度共存 | | **Worker 框架** | `@rpc_method` 装饰器 + `Plugin` 基类生命周期 + 启动契约校验 | | **流式端点** | `@rpc_method` 返回生成器即得 SSE(`text/event-stream`)逐块输出 | | **Worker 运维** | REST/前端管理:副本重启、配置热更新、扩缩容(全局对称 / 逐实例) | | **鉴权与安全** | 网关内置登录与 JWT 签发(Rust 实现);Rust Argon2id 密码哈希 | | **插件脚手架** | `gen_plugin` 生成统一骨架,`gen_models` 生成模型,`build_plugin` 打包进程插件 | | **交付打包** | PyInstaller / Nuitka 一键打独立可执行文件;`.mbank` 模块银行支持插件加密与热替换 | | **Vue 3 前端** | 管理台(Worker 管理 + 登录)与自动生成的 API 客户端 | ## 快速开始 ### 环境要求 - Python >= 3.12、[uv](https://docs.astral.sh/uv/) - Rust 工具链(编译 `jwt` / `password` 扩展) - Node.js >= 22(前端开发) - ffmpeg(可选,`stream_manager` 需要) ### 首次克隆 ```bash git clone http://124.71.68.6:3000/chongming/chongming-core.git chongming-zenoh cd chongming-zenoh git clone http://124.71.68.6:3000/chongming/chongming-plugins.git src/plugins git clone http://124.71.68.6:3000/chongming/chongming-config.git config git clone http://124.71.68.6:3000/chongming/chongming-web.git src/chongming-web uv sync # 网关(chongming-gateway)与 utils/* 工具包按 pyproject 源自动安装 ``` ### 安装与启动 ```bash uv sync # 同步依赖(自动编译 Rust 扩展) uv run serve # 启动服务(开发模式,热重载) ``` 启动后: | 通道 | 地址 | 说明 | | ---- | ---- | ---- | | HTTP | `http://localhost:8000` | REST API | | Swagger | `http://localhost:8000/docs` | OpenAPI 交互文档(仅调试模式开启) | | MQTT | `mqtt://localhost:1883` | 内嵌 Broker(`use_embedded_broker` 控制) | 服务器可切换(`uv run gunicorn` / `uv run granian`,或设 `APP_SERVER` 环境变量), 启动参数统一读 `config.toml` 的 `[.server]` 段。 ### 前端开发 ```bash cd src/chongming-web npm install npm run dev # http://localhost:5173 ``` ## 项目结构 ```text chongming-zenoh/ ├── config.toml # 应用主配置(环境、服务器、MQTT、缓存等) ├── pyproject.toml # 宿主项目依赖与脚本入口 ├── plugins_deps.toml # 内置插件专属依赖(打包收集用) ├── config/ # 配置子仓库(chongming-config) │ ├── zenoh_config.json5 # Zenoh 会话配置 │ └── workers/ # 插件注册 + 路由定义(TOML) │ ├── /config.toml # 插件注册与运行时配置 │ └── /.toml # 端点定义(每个端点一个文件) ├── src/ │ ├── plugins/ # 内置插件源码(chongming-plugins 子仓库) │ └── chongming-web/ # Vue 3 前端(chongming-web 子仓库) ├── utils/ # 独立工具包(各自独立仓库,uv 安装) │ ├── gateway/ # 网关本体(chongming-gateway,可独立发包) │ ├── cli/ # chongming-cli:build / gen_plugin / build_plugin ... │ ├── worker/ # Worker 框架(chongming-worker) │ └── ... # config / database / jwt / password / request / ... ├── workers/ # 进程插件本地开发目录(不入库;生产在独立容器) ├── public/ # 可执行文件打包骨架(main.py / server.py / 打包资源) ├── resources/ # 模块银行产物(plugins.mbank 等) ├── data/ logs/ # 数据库、上传文件、日志 └── docs/ # 专题文档(见文末「更多文档」) ``` ## 使用指南 ### 插件:两种运行模式 按插件粒度在 `config/workers//config.toml` 的 `[workers]` 节声明: ```toml load_mode = "inline" # 默认:插件在网关进程内加载 load_mode = "external" # 独立进程运行,经 Zenoh 注册路由 ``` | 模式 | 源码位置 | 加载方式 | 适用 | | ---- | -------- | -------- | ---- | | **inline** | `src/plugins//` | 网关进程内 import,TOML 盘扫生成路由 | 与网关同生命周期的业务插件 | | **external** | `workers//`(自包含项目) | 独立进程,经 Zenoh 注册路由 | 独立扩缩、独立依赖/语言、跨容器部署 | - external 插件与网关**零导入关系**:只依赖 `chongming-worker` SDK + zenoh,代码结构与 inline 完全一致;本地调试 `cd workers/ && uv run chongming-worker --type `。 - 两种模式可同时存在(混合部署),网关对二者的路由挂载、服务发现与运维指令一致。 开发指南:[内置插件开发](src/plugins/README.md) | [进程插件开发(external,从开发到部署)](docs/process-plugin.md) | [Rust 插件](docs/rust-plugin.md) ### 端点定义与路由 | 节类型 | HTTP | OpenAPI | MQTT | 模型命名 | | ------ | ---- | ------- | ---- | -------- | | `[GET]` | ✅ | ✅ | ✅ | `XxxGetRequest / XxxGetResponse` | | `[POST]` | ✅ | ✅ | ✅ | `XxxPostRequest / XxxPostResponse` | | `[MQTT]` | ❌ | ❌ | ✅ | `XxxMqttRequest` | **方式一:TOML 动态路由**(标准 JSON 端点)——`config/workers//*.toml`: ```toml # config/workers/example/add.toml [GET] summary = "加法运算" params = ["a: float", "b: float"] [GET.response_model] result = { type = "float", required = true } ``` **方式二:插件自定义路由**(无法用 TOML 描述时,如 multipart 上传)——插件暴露 `APIRouter`,网关自动发现: ```python # src/plugins//routes.py from fastapi import APIRouter, UploadFile, File router = APIRouter(prefix="/", tags=[""]) @router.post("/upload") async def upload(file: UploadFile = File(...)): ... ``` > 约定:插件 `__init__.py` 导入该 `router` 实例,网关通过 `getattr(module, "router", None)` 发现。 ### 流式端点(SSE) 方法节声明 `stream = true`,`@rpc_method(..., stream=True)` 返回生成器即可逐块输出: ```toml [POST] summary = "模拟 AI 流式对话(SSE,逐 token 输出)" stream = true timeout = 300.0 # 流总寿命上限(秒);流式端点必须配大 stream_idle_timeout = 10.0 # 连续无数据块的最大间隔(秒) params = ["prompt: str", "tokens: int"] ``` ```bash curl -N -X POST localhost:8000/api/v1/calc/stream_chat \ -H 'Content-Type: application/json' -d '{"prompt":"你好","tokens":3}' # data: {"token":"你好·1","index":0} # data: {"token":"你好·2","index":1} # data: {"done":true} ``` - 请求与普通端点一致(含鉴权);响应 `text/event-stream`,每块一个 `data: {json}` 事件。 - 错误语义:无可用 worker 时 HTTP 503;流中异常/空闲超时以 `data: {"error": "..."}` 收尾; 客户端断开即终止迭代。 - 经 nginx 反代时需 `proxy_buffering off;`,否则失去逐块即时性。 - 流式能力由 `chongming-worker` 提供,需 venv 中为支持 `stream` 的版本 (本地改框架后按「[仓库结构与开发工作流](#仓库结构与开发工作流)」刷新 pin)。 ### Worker 管理与扩缩容 网关提供 REST 管理端点与 Vue 管理页(需超级管理员),inline 与 external 插件共用同一套 管理协议(Zenoh `worker///__admin__`)。 | 方法 | 路径 | 说明 | | ---- | ---- | ---- | | `GET` | `/api/v1/admin/workers` | 盘点全部副本(inline 本地注册 + external liveliness 发现) | | `POST` | `/api/v1/admin/workers/restart` | 重启单个副本(`worker_type` / `worker_id`) | | `POST` | `/api/v1/admin/workers//stop` | 停止单个副本 / 某插件全部副本 | | `GET` `POST` | `/api/v1/admin/workers//config` | 读取 / 更新 `[workers.config]`(成功即下发并重启副本) | | `POST` | `/api/v1/admin/workers//scale` | 调整该类型副本数(全局对称分摊) | | `GET` | `/api/v1/admin/workers//instances` | 按进程实例分组查看副本(供逐实例扩缩) | | `POST` | `/api/v1/admin/workers//scale_instance` | 逐实例精细扩缩(只调指定实例) | **要点**: - 扩缩容持久化到插件配置;缩容走**优雅排空**(先摘除服务发现隔绝新请求,等在途请求完成后退出)。 - 多网关共享同一 `[cache]` 时,副本数按在线网关自动分摊,任一台操作即全局生效(需 `redis` 或共享卷 `sqlite`;`CHONGMING_GATEWAY_ID` 用于区分同宿主多网关)。 - 无状态插件默认逐请求随机负载均衡;**需要固定到某个进程实例**的插件(本地状态/设备连接) 可用实例定向调用:HTTP 头 `X-Chongming-Instance: <实例id>`,或 MQTT 信封顶层 `target_instance`;实例离线时严格报错,不回退。 ```bash curl -H 'X-Chongming-Instance: edge-01' http://127.0.0.1:8000/api/v1/demo/state ``` 细节(管理指令语义、多实例分摊与自愈、领导选举、逐实例扩缩)见 [Worker 管理与多实例运维](docs/worker-management.md)。 ### MQTT 通道 | 方向 | Topic | 说明 | | ---- | ----- | ---- | | 请求 | `chongming//` | MQTT → Worker | | 响应 | `chongming///response` | Worker → MQTT(无响应时跳过) | ```toml [development.mqtt] enabled = true host = "127.0.0.1" # 内嵌 Broker 时为监听地址;外部 Broker 时为对方地址 port = 1883 use_embedded_broker = true # true(默认)网关内嵌 amqtt Broker;false 连接外部 Broker # username = "chongming" # 可选:Broker 认证(内嵌/外部均生效) # password = "change-me" ``` 请求负载为**信封结构**(控制字段与业务数据分离),非信封格式会被丢弃并告警: ```json { "meta": { "method": "GET", "timeout": 30, "cacheable": false }, "data": { "a": 3, "b": 5 } } ``` **自定义订阅主题**:把外部 MQTT 主题桥接到插件,推荐写在插件自己的配置里: ```toml # config/workers/aibox_sophon_adapter/config.toml [[workers.mqtt_topics]] topic = "box/aibox_sophon/board_ping" # 支持 + / # 通配符 subject = "aibox_sophon_adapter.board_ping" # 可选,{1} {2}... 对应捕获值 response_topic = "/board_ping/response" # 可选,默认 "<实际主题>/response" ``` - 多条自定义主题按顺序匹配,先匹配者优先;内置 `chongming/...` 接口最后兜底。 - 全局 `[.mqtt].custom_topics` 可作通用兜底(不推荐,优先用插件配置)。 **多实例部署要点**(细节见 [Worker 管理与多实例运维](docs/worker-management.md)): - 单机:内嵌 Broker + 本地 `sqlite` 缓存即可。 - 多实例:**外部 Broker(必须)** + 共享 `[cache]` 后端(`redis` 或共享卷 `sqlite`)。 共享缓存是收敛前提:MQTT Bridge 全局单例、副本数分摊、inline 领导选举都跑在它上面。 手动联调: ```bash uv run serve # 终端 1:启动服务 uv run python test/test_mqtt_e2e.py # 终端 2:端到端 uv run python test/publisher.py example add '{"a":3,"b":5}' # 单次调用 uv run python test/subscriber.py # 监听所有响应 ``` ### 配置 - **应用配置**:根 `config.toml`(环境 `[default].env` → `[development]` / `[production]` 选段;服务器、安全、MQTT、缓存等)。 - **插件注册**:`config/workers//config.toml` 的 `[workers]` 节——加载模式、 `count` 副本数、`router_prefix`、`[workers.config]` 运行时配置。 - **路由定义**:`config/workers//*.toml`,每个端点一个文件。 - **Zenoh**:`config/zenoh_config.json5`。 字段速查(完整说明见 [配置说明](config/README.md)): | 字段 | 位置 | 说明 | | ---- | ---- | ---- | | `count` | `<插件>/config.toml` | Worker 副本数(`[workers]` 节) | | `load_mode` | `<插件>/config.toml` | `inline`(默认)/ `external` | | `router_prefix` | `<插件>/config.toml` | API 路由前缀 | | `defaults.timeout` / `defaults.cacheable` | `<插件>/config.toml` | 端点默认超时与是否缓存 | | `params` | `<操作>.toml` | 参数列表(`"name: type"`) | | `auth_required` | `<操作>.toml` | 是否需要 JWT 鉴权 | | `internal` | `<操作>.toml` | 仅限内部 RPC,不注册 HTTP 路由 | | `cacheable` | `<操作>.toml` | 端点是否可缓存 | | `stream` / `stream_idle_timeout` | `<操作>.toml` | 流式输出(SSE)与其空闲超时 | | `launch_worker_count` | 根 `config.toml` `[cache]` | 副本数/运行时配置以 `toml`(默认)还是 `cache` 为准 | ## 打包与部署 ### 构建可执行文件 ```bash uv run build pyinstaller # PyInstaller 打包 uv run build nuitka # Nuitka 打包(Linux 需宿主 patchelf,已在 dev 依赖) uv run build --encrypt --license # 对外分发:加密模块(PyArmor 混淆)+ 注入授权公钥 uv run build --only mbank # 只重新生成 plugins.mbank(插件更新只需替换该文件) uv run build --only mbank,prepare,vue # 只跑指定步骤 ``` 构建流程:模块打包(`plugins.mbank`)→ 资源复制 → PyInstaller/Nuitka 打包 → 数据库初始化 → 前端编译 → VFS 挂载。`--only` 可选步骤:`mbank` / `prepare` / `obfuscate` / `pack` / `clean` / `license_guard` / `vue`。 **网关更新与插件更新的路径不同**: - 网关本体(`chongming-gateway` 包)是普通依赖,由 PyInstaller/Nuitka 从 venv 收集进 exe → 改网关必须重新 `uv run build`; - 插件代码在 `plugins.mbank` 里 → `uv run build --only mbank` 后替换该文件即可热更新。 ### 打包产物的依赖收集 exe 内的依赖来自三处,新增第三方包时对号入座: | 依赖归属 | 声明位置 | | -------- | -------- | | 网关本体(`fastapi` / `granian` / `chongming_*` 等) | `chongming-gateway` 包自己的 `pyproject.toml`(消费者自动获得,打包沿静态导入收集) | | 动态导入、静态分析看不到的(granian 子模块、amqtt 插件、`sqlite_vfs.contrib`、`uvloop`)+ 骨架自身 | 根 `config.toml` 的 `[pyinstaller_build_import]` | | 内置插件专属(如 doubao 的 `openai` / `bytedance`) | 根 `plugins_deps.toml`,按插件分节 | 新增插件依赖时,venv 安装清单(根 `pyproject.toml`)与 `plugins_deps.toml` 需同步维护; 依赖变更后必须重跑 `pack`(`--only mbank` 带不动第三方依赖)。 `--encrypt` / `--license` 默认**都关**(不加参数即内部调试产物:明文模块、不校验授权); 对外分发必须同时加这两个开关。 `--license` 需要厂商授权公钥(未提供直接构建失败,避免打出名不副实的"授权版")。首次: `uv run chongming-license keygen -o license_private.pem` 生成密钥对(一次性;私钥离线保管, 绝不进仓库/产物),把输出的公钥写入根目录 `license_public.key`,或用 `CHONGMING_LICENSE_PUBKEY=` / `CHONGMING_LICENSE_PUBKEY_FILE=<文件路径>` 指定。 构建会把公钥注入产物的 `[license] public_key` 并烘焙进代码(现场改配置/环境变量关不掉强制校验)。 ### 进程插件打包与容器部署 ```bash uv run build_plugin demo # → dist/demo.zip(开发目录即产物) uv run migrate_plugin # 内置插件一键迁移为进程插件 ``` zip 根即容器工作目录:插件代码 + `config/` + `pyproject.toml` + `start.sh` (`uv sync` + `chongming-worker --type `);容器内 `unzip .zip -d /app && cd /app && ./start.sh`。 生产部署时进程插件与网关**分别打包到不同容器**:网关容器装 `chongming-gateway`, 插件容器装插件代码 + `chongming-worker`。 ## 仓库结构与开发工作流 本仓库(**chongming-core**)是宿主项目:骨架(`public/`)、打包配置、构建与交付; 业务资产分布在独立仓库中,通过 `uv` 依赖与子目录 clone 组合: | 目录 / 包 | 仓库 | 说明 | | --------- | ---- | ---- | | `utils/gateway/` | `chongming-gateway` | 网关本体(独立包,发布到私有源;本仓按发布版依赖) | | `utils/template/` | 项目模板(独立维护) | 新项目模板:`python utils/template/init.py <目录>` 初始化出「骨架 + 插件 + 配置」的网关项目 | | `utils//` | `chongming-utils-*` | 工具包(config / worker / jwt / cache / ...) | | `utils/cli/` | `chongming-utils-cli` | `chongming-cli`:build / gen_plugin / build_plugin / migrate_plugin | | `src/plugins/` | `chongming-plugins` | 内置插件源码 | | `config/` | `chongming-config` | 应用与 Worker 路由配置 | | `src/chongming-web/` | `chongming-web` | Vue 3 前端 | **修改 `utils/` / `utils/gateway`(可发布包)**: 1. 在包目录内提交并 push,`uv build` 后用 twine 发布新版本到私有源 2. 回主仓库 `uv lock --upgrade-package <包名>` 刷新 pin,`uv sync` 3. 主仓库提交 `uv.lock` > ⚠️ **打包陷阱**:`uv run build` 会先按锁文件同步环境,本地 `uv pip install -e utils/` > 的可编辑安装会被替换回发布版——「改了包但产物行为没变」几乎都是它。打包前确认 > venv 指向目标版本:`python -c "import chongming_worker, inspect; print(inspect.getsourcefile(chongming_worker))"`。 **修改 `src/plugins/` / `config/` / `src/chongming-web/`**:在对应子仓库内提交并 push, 回主仓库启动验证。 **发布前总检查**: - [ ] 所有子仓库 `git status` 干净且已 push;`uv.lock` 与各源一致 - [ ] 打包前确认 venv 内网关与工具包为目标版本(见上方打包陷阱) - [ ] 配置中无硬编码凭据——密钥走环境变量: `VOLC_ACCESSKEY`(豆包 Ark Key)、`AIBOX_MQTT_URI`(AI Box 外部 Broker URI) - [ ] 相关测试通过(见 `test/`,如 `uv run python test/test_mqtt_e2e.py`) ## 内置内容一览 ### 内置插件(chongming-plugins 仓库,inline) | 插件 | HTTP 前缀 | MQTT 主题前缀 | 说明 | | ---- | --------- | ------------- | ---- | | **auth** | `/auth` | `auth.*` | 用户管理:注册/资料/改密(登录与 token 签发内置网关) | | **auth_oauth** | `/auth/oauth` | `auth_oauth.*` | 身份源示例:GitHub OAuth + 手机验证码登录 | | **doubao** | `/doubao` | `doubao.*` | 豆包视觉理解(支持 AICC 安全通道) | | **aibox_sophon_adapter** | `/aibox` | `aibox_sophon_adapter.*` | AI Box 设备心跳、离线检测、告警、视频上传 | | **example** | `/calc` | `example.*` | 四则运算 + 心跳演示(含 MQTT 端点示例) | | **demo**(进程插件示例) | — | — | `workers/demo/`(本地自包含项目,external 模式) | ### 工具包(uv 依赖,各自独立仓库) | 包 | 路径 | 语言 | 说明 | | -- | ---- | ---- | ---- | | **chongming-gateway** | `utils/gateway/` | Python | 网关本体(FastAPI + Zenoh) | | **chongming-auth** | `utils/auth/` | Python | auth 域共享契约:Users 模型、建库/种子、登录校验与 JWT 签发 | | **chongming-config** | `utils/config/` | Python | TOML 配置加载(TypedDict 类型安全) | | **chongming-database** | `utils/database/` | Python | aiosqlite + SQLModel 异步数据库封装 | | **chongming-jwt** | `utils/jwt/` | Rust + PyO3 | JWT 编解码(HS/RS 系列) | | **chongming-password** | `utils/password/` | Rust + PyO3 | Argon2id 密码哈希与验证 | | **chongming-request** | `utils/request/` | Python | Zenoh RPC 直连客户端 | | **chongming-worker** | `utils/worker/` | Python | Worker 框架:装饰器、运行时、契约校验、独立进程入口 | | **chongming-stream-manager** | `utils/stream_manager/` | Python | ffmpeg + MediaMTX RTSP 流代理管理 | | **bytedance** | `utils/bytedance/` | Python | 字节跳动 AICC 安全通信 SDK | ### 常用命令 ```bash uv run serve # 启动开发服务 uv run granian # 或 uv run gunicorn(换服务器,参数仍读 config.toml) uv run gen_plugin # 生成插件骨架(--process 生成进程插件项目) uv run gen_models # 按 TOML 生成 Pydantic 模型 uv run build [pyinstaller|nuitka] # 打包可执行文件 uv run build_plugin # 打包进程插件 → dist/.zip uv run migrate_plugin # 内置插件迁移为进程插件 uv run chongming-worker --type # 独立 Worker 进程(external 插件本地调试/容器入口) cd utils/gateway && uv build # 构建网关包 wheel(发版用) python utils/template/init.py ../new-proj # 用模板初始化一个新项目 ``` ## 更多文档 - [配置说明](config/README.md) - [内置插件开发指南](src/plugins/README.md) - [进程插件开发指南(external,从开发到部署)](docs/process-plugin.md) - [Worker 管理与多实例运维](docs/worker-management.md) - [Rust 插件编写说明](docs/rust-plugin.md) - [插件线协议规范(语言无关)](docs/plugin-wire-protocol.md) - [网关包说明(chongming-gateway)](utils/gateway/README.md) - [Worker 框架](utils/worker/README.md) - [前端开发指南](src/chongming-web/README.md)