Files
chongming-core/README.md
T
chakcy ac8b563c7d fix: --encrypt/--license 构建链修复(网关收集项、[license] 注入点、契约测试)
- config.toml:--encrypt 会先 PyArmor 混淆 main.py / server.py,PyInstaller 静态
  分析跟不到里面的 `from chongming.app import app`,网关包整个漏收(打包"成功"、
  产物启动即报 No module named 'chongming')—— 收集清单显式列出 chongming /
  chongming.app 等入口包,注释也从"静态分析可跟到"改准;
- public/config.toml:补 [license] 段(public_key 留空),--license 的注入点,
  缺了会在 prepare 阶段报"未找到 [license] 段的 public_key";
- README:补 --license 的公钥前置条件(keygen → license_public.key 或环境变量);
- test_build_flags.py:4 条契约守卫 —— 真实模板/骨架可注入 [license]、收集清单含
  网关入口包、加密包密钥写进包内且按运行端口径(get_key())可导入。此前用例都
  自造 config.toml,这两类模板漂移测不出来。

实测(device-hub):修复前 --encrypt --license 产物启动报错;修复后网关启动、
授权校验通过、插件从加密 mbank 正常加载、HTTP 正常返回。
2026-10-14 15:29:29 +08:00

22 KiB
Raw Blame History

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
  • Rust 工具链(编译 jwt / password 扩展)
  • Node.js >= 22(前端开发)
  • ffmpeg(可选,stream_manager 需要)

首次克隆

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 源自动安装

安装与启动

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 的 [<env>.server] 段。

前端开发

cd src/chongming-web
npm install
npm run dev          # http://localhost:5173

项目结构

chongming-zenoh/
├── config.toml                   # 应用主配置(环境、服务器、MQTT、缓存等)
├── pyproject.toml                # 宿主项目依赖与脚本入口
├── plugins_deps.toml             # 内置插件专属依赖(打包收集用)
├── config/                       # 配置子仓库(chongming-config)
│   ├── zenoh_config.json5        # Zenoh 会话配置
│   └── workers/                  # 插件注册 + 路由定义(TOML)
│       ├── <type>/config.toml    # 插件注册与运行时配置
│       └── <type>/<op>.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/<type>/config.toml 的 [workers] 节声明:

load_mode = "inline"     # 默认:插件在网关进程内加载
load_mode = "external"   # 独立进程运行,经 Zenoh 注册路由
模式 源码位置 加载方式 适用
inline src/plugins/<name>/ 网关进程内 import,TOML 盘扫生成路由 与网关同生命周期的业务插件
external workers/<name>/(自包含项目) 独立进程,经 Zenoh 注册路由 独立扩缩、独立依赖/语言、跨容器部署
  • external 插件与网关零导入关系:只依赖 chongming-worker SDK + zenoh,代码结构与 inline 完全一致;本地调试 cd workers/<name> && uv run chongming-worker --type <name>。
  • 两种模式可同时存在(混合部署),网关对二者的路由挂载、服务发现与运维指令一致。

开发指南:内置插件开发 | 进程插件开发(external,从开发到部署) | Rust 插件

端点定义与路由

节类型 HTTP OpenAPI MQTT 模型命名
[GET] ✅ ✅ ✅ XxxGetRequest / XxxGetResponse
[POST] ✅ ✅ ✅ XxxPostRequest / XxxPostResponse
[MQTT] ❌ ❌ ✅ XxxMqttRequest

方式一:TOML 动态路由(标准 JSON 端点)——config/workers/<type>/*.toml:

# config/workers/example/add.toml
[GET]
summary = "加法运算"
params = ["a: float", "b: float"]

[GET.response_model]
result = { type = "float", required = true }

方式二:插件自定义路由(无法用 TOML 描述时,如 multipart 上传)——插件暴露 APIRouter,网关自动发现:

# src/plugins/<name>/routes.py
from fastapi import APIRouter, UploadFile, File

router = APIRouter(prefix="/<prefix>", tags=["<worker_type>"])

@router.post("/upload")
async def upload(file: UploadFile = File(...)):
    ...

约定:插件 __init__.py 导入该 router 实例,网关通过 getattr(module, "router", None) 发现。

流式端点(SSE)

方法节声明 stream = true,@rpc_method(..., stream=True) 返回生成器即可逐块输出:

[POST]
summary = "模拟 AI 流式对话(SSE,逐 token 输出)"
stream = true
timeout = 300.0             # 流总寿命上限(秒);流式端点必须配大
stream_idle_timeout = 10.0  # 连续无数据块的最大间隔(秒)
params = ["prompt: str", "tokens: int"]
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/<type>/<worker_id>/__admin__)。

方法 路径 说明
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 POST /api/v1/admin/workers/<type>/config 读取 / 更新 [workers.config](成功即下发并重启副本)
POST /api/v1/admin/workers/<type>/scale 调整该类型副本数(全局对称分摊)
GET /api/v1/admin/workers/<type>/instances 按进程实例分组查看副本(供逐实例扩缩)
POST /api/v1/admin/workers/<type>/scale_instance 逐实例精细扩缩(只调指定实例)

要点:

  • 扩缩容持久化到插件配置;缩容走优雅排空(先摘除服务发现隔绝新请求,等在途请求完成后退出)。
  • 多网关共享同一 [cache] 时,副本数按在线网关自动分摊,任一台操作即全局生效(需 redis 或共享卷 sqlite;CHONGMING_GATEWAY_ID 用于区分同宿主多网关)。
  • 无状态插件默认逐请求随机负载均衡;需要固定到某个进程实例的插件(本地状态/设备连接) 可用实例定向调用:HTTP 头 X-Chongming-Instance: <实例id>,或 MQTT 信封顶层 target_instance;实例离线时严格报错,不回退。
curl -H 'X-Chongming-Instance: edge-01' http://127.0.0.1:8000/api/v1/demo/state

细节(管理指令语义、多实例分摊与自愈、领导选举、逐实例扩缩)见 Worker 管理与多实例运维。

MQTT 通道

方向 Topic 说明
请求 chongming/<type>/<op> MQTT → Worker
响应 chongming/<type>/<op>/response Worker → MQTT(无响应时跳过)
[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"

请求负载为信封结构(控制字段与业务数据分离),非信封格式会被丢弃并告警:

{
  "meta": { "method": "GET", "timeout": 30, "cacheable": false },
  "data": { "a": 3, "b": 5 }
}

自定义订阅主题:把外部 MQTT 主题桥接到插件,推荐写在插件自己的配置里:

# 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/... 接口最后兜底。
  • 全局 [<env>.mqtt].custom_topics 可作通用兜底(不推荐,优先用插件配置)。

多实例部署要点(细节见 Worker 管理与多实例运维):

  • 单机:内嵌 Broker + 本地 sqlite 缓存即可。
  • 多实例:外部 Broker(必须) + 共享 [cache] 后端(redis 或共享卷 sqlite)。 共享缓存是收敛前提:MQTT Bridge 全局单例、副本数分摊、inline 领导选举都跑在它上面。

手动联调:

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/<type>/config.toml 的 [workers] 节——加载模式、 count 副本数、router_prefix、[workers.config] 运行时配置。
  • 路由定义:config/workers/<type>/*.toml,每个端点一个文件。
  • Zenoh:config/zenoh_config.json5。

字段速查(完整说明见 配置说明):

字段 位置 说明
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 为准

打包与部署

构建可执行文件

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=<base64 公钥> / CHONGMING_LICENSE_PUBKEY_FILE=<文件路径> 指定。 构建会把公钥注入产物的 [license] public_key 并烘焙进代码(现场改配置/环境变量关不掉强制校验)。

进程插件打包与容器部署

uv run build_plugin demo              # → dist/demo.zip(开发目录即产物)
uv run migrate_plugin <name>          # 内置插件一键迁移为进程插件

zip 根即容器工作目录:插件代码 + config/ + pyproject.toml + start.sh (uv sync + chongming-worker --type <name>);容器内 unzip <name>.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/<name>/ 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/<name> / 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/<name> 的可编辑安装会被替换回发布版——「改了包但产物行为没变」几乎都是它。打包前确认 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

常用命令

uv run serve                          # 启动开发服务
uv run granian                        # 或 uv run gunicorn(换服务器,参数仍读 config.toml)
uv run gen_plugin <name>              # 生成插件骨架(--process 生成进程插件项目)
uv run gen_models                     # 按 TOML 生成 Pydantic 模型
uv run build [pyinstaller|nuitka]     # 打包可执行文件
uv run build_plugin <name>            # 打包进程插件 → dist/<name>.zip
uv run migrate_plugin <name>          # 内置插件迁移为进程插件
uv run chongming-worker --type <name> # 独立 Worker 进程(external 插件本地调试/容器入口)
cd utils/gateway && uv build          # 构建网关包 wheel(发版用)
python utils/template/init.py ../new-proj   # 用模板初始化一个新项目

更多文档