263 lines
19 KiB
Markdown
263 lines
19 KiB
Markdown
# Blackbird 架构文档(UniServe Framework)
|
||
|
||
> 权威架构参考。本文以 `core/services.yaml`(实际运行清单)与 `git HEAD` 为事实来源。
|
||
> 与根目录 `README.md` 的偏差见文末「§10 与 README 的偏差」。
|
||
> 最后核对于:2026-08-17。
|
||
|
||
---
|
||
|
||
## 1. 文档目的与范围
|
||
|
||
Blackbird 是一套 **进程托管式轻量微服务** 系统(自研 UniServe Framework),用于支撑一个带内容审核的社区/笔记类 Web 应用及其周边 AI/嵌入式子系统。本文描述:
|
||
|
||
- 系统上下文与边界
|
||
- 部署拓扑(容器级)
|
||
- 关键请求链路
|
||
- 服务清单(含语言、端口、职责、当前状态)
|
||
- 共享基础库与横切关注点
|
||
- 关键架构决策(ADR 摘要)
|
||
- 演进策略与已知漂移
|
||
|
||
**不在本文范围**:单模块内部算法、前端页面细节、硬件机器人/帧缓冲子系统的实现。
|
||
|
||
---
|
||
|
||
## 2. 系统上下文(System Context)
|
||
|
||
```
|
||
浏览器 / 客户端
|
||
│ HTTPS :443
|
||
▼
|
||
┌─────────────────────────────────────────────────────────────┐
|
||
│ Blackbird 系统边界 │
|
||
│ waf → ca → gateway → {fsys, dnfs2, core} │
|
||
│ 内部:db2 / memdb / ai_review / smtp / captcha2 / core(confs) │
|
||
│ 独立:aibox(+BotOps) / bot4cj+pybot / lci │
|
||
└─────────────────────────────────────────────────────────────┘
|
||
│ pymsg/TCP (内部 RPC) │ SMTP (外部邮件)
|
||
▼ ▼
|
||
db2 / ai_review / smtp 邮件服务商 (注册验证码)
|
||
```
|
||
|
||
**参与者**:
|
||
- 终端用户:经 `:443` 访问社区首页、登录/注册、树状笔记。
|
||
- 运维/开发者:经 `core:5200` 管理接口启停子进程。
|
||
- 外部依赖:SMTP 邮件服务商(注册验证码)、DeepSeek API(ai_review)。
|
||
|
||
---
|
||
|
||
## 3. 部署拓扑(Container Diagram)
|
||
|
||
```
|
||
┌──────────────────────────────────────────────────┐
|
||
│ core (PMC / Nidus) │
|
||
│ 进程托管 :5200 (HTTP 管理) + 内嵌 confs :8010 │
|
||
│ 读 services.yaml 拉起并守护下列子进程 │
|
||
└───────────────────────┬──────────────────────────┘
|
||
│ 启动 / 守护
|
||
┌────────────────┬───────────────────────┼──────────────────┬─────────────────┐
|
||
▼ ▼ ▼ ▼ ▼
|
||
┌────────────┐ ┌────────────┐ ┌───────────┐ ┌───────────┐ ┌───────────┐
|
||
│ redirect80 │ │ waf │ │ ca │ │ gateway │ │ fsys │
|
||
│ :80 │ │ :443 │ │ :8443 │ │ :8445 │ │ :9200 │
|
||
│ Python │ │ Go │ │ C++20 │ │ C++20 │ │ C++20 │
|
||
└─────┬──────┘ └─────┬──────┘ └─────▲─────┘ └─────▲─────┘ └─────▲─────┘
|
||
│ 301/308 │ TLS+coraza │ auth+forward │ loopback │ proxy
|
||
└───────────────►│ │ │ pure-fwd │ notes
|
||
└────────────────────►│ │ └────┬────┘
|
||
│ │ │ pymsg
|
||
┌────────┴────────┐ ┌─────┴────────┐ ┌────────▼─────┐
|
||
│ dnfs2 :8006 │ │ core :5200 │ │ dnfs2 :8006 │
|
||
│ C++20 │ │ (管理/守护) │ │ (经 fsys 代理)│
|
||
└───────▲────────┘ └──────────────┘ └──────────────┘
|
||
│ pymsg
|
||
┌────────────┼─────────────┐
|
||
▼ ▼ ▼
|
||
┌────────────┐ ┌───────────┐ ┌───────────┐
|
||
│ db2 :8005 │ │ai_review │ │ smtp │
|
||
│ C++20 pymsg│ │ :8007 Py │ │ :8008 Py │
|
||
│ +Logs 表 │ │ RAG+LLM │ │ pymsg │
|
||
└─────▲──────┘ └─────▲─────┘ └─────▲─────┘
|
||
│ │ 审核 │ 发码
|
||
│ └─────────────┘
|
||
┌─────┴──────────────────────────────┐
|
||
│ captcha2 :8009 (C++20, pymsg) │
|
||
└─────────────────────────────────────┘
|
||
|
||
独立 / 外设(不在主链路):
|
||
aibox :8765 (Python WebSocket, 独立产品; --once CLI 单次对话 + models 多模型档案含 fallback 失败转移)
|
||
BotOps (aibox 的 TUI 前端)
|
||
bot4cj (Cangjie + 树莓派 I2C, 硬件)
|
||
pybot (Python, bot4cj 的迁移版: .mt 解析/录制/实时闭环)
|
||
mtc (Python, 命令式动作脚本 → .mt 编译器, 开发期工具)
|
||
lci (C++ /dev/fb0, 嵌入式 GUI)
|
||
actpred / libtry (实验 / 支撑库)
|
||
|
||
日志链路: 各服务 log_client ──pymsg──► db2:8005 Logs 表 (fail-safe, db 不可用时静默丢弃)
|
||
配置链路: gateway ──pymsg get──► core 内嵌 confs :8010 (confs/configs/gateway.ini, mtime 自动重载;其余服务配置纯 CLI 传递)
|
||
内存缓存: memdb :8011 (libmemdb + libmsg RPC, TTL 惰性驱逐;暂无业务调用方,预留热点数据/会话缓存)
|
||
```
|
||
|
||
> 注:`core` 启动参数为 `./nidus.out -i . --serve 5200 --confs 8010`(`-i .` 指向 services.yaml 所在目录),`--confs` 启用内嵌配置服务(由 confs2 合并而来,与 Python confs 协议兼容)。
|
||
|
||
---
|
||
|
||
## 4. 关键请求链路
|
||
|
||
### 4.1 正常访问(已登录)
|
||
1. 客户端若以 HTTP 访问 `:80`,`redirect80` 重定向到 HTTPS `:443`(GET/HEAD→301,其余→308 保留 body)。
|
||
2. 请求打到 `waf:443`(Go/coraza):终止 TLS,运行 SecLang 规则;命中→403。
|
||
3. 未命中规则 → 明文 HTTP 转发到 `ca:8443`,注入 `X-Forwarded-For` / `X-Forwarded-Proto`。
|
||
4. `ca` 验签 Token,注入 `X-Auth-User` / `X-Auth-Admin`,转发到 `gateway:8445`(仅 loopback)。
|
||
5. `gateway` 检查:显式 `Target-Url` 必须带 `X-Auth-User`(否则 401),且必须在 `Proxy.opened` 白名单(否则 403);转发到 `fsys:9200` / `dnfs2:8006` / `core:5200`。
|
||
6. `fsys` 处理前端资源,笔记类请求代理到 `dnfs2:8006`。
|
||
|
||
### 4.2 注册 / 登录(ca 负责)
|
||
- `/login`、`/register`、`/reset_*`、`/captcha/image`、`/whoami` 由 `ca:8443` 直接处理(不再经 gateway 鉴权层)。
|
||
- 密码:PBKDF2-HMAC-SHA256(10 万轮 + 16B 随机盐),常数时间比较。
|
||
- Token:`Base64(payload) + "_" + RSA-Sign(payload)`,默认 1h 过期;RSA 密钥内存生成,重启即全失效;jti 黑名单 + 用户版本号双轨吊销。
|
||
- 注册邮件验证码:ca 经 pymsg/TCP 调 `smtp:8008` 发送与校验;登录强制 CAPTCHA(`captcha2:8009`)。
|
||
- 用户数据:ca 经 pymsg/TCP 调 `db2:8005` 存取(`Users` 表,首位注册者即管理员)。
|
||
- 速率限制:登录 5/5min、注册 3/10min、发码 IP+邮箱双窗口,按 `X-Forwarded-For` 计数。
|
||
|
||
### 4.3 笔记写入(含内容审核)
|
||
`fsys → dnfs2:8006` → 新增节点前 `dnfs2` 调 `ai_review:8007 /review`(fail-closed,异常一律拦截)→ 审核通过后经 pymsg 调 `db2:8005 execute` 持久化。
|
||
|
||
### 4.4 日志 / 配置(横切)
|
||
- 各服务内置 `log_client`(异步队列 + 后台批量 flush)→ pymsg 写 `db2:8005` 的 `Logs` 表;`db2` 自带 30 天/100 万条清理线程。
|
||
- `gateway` 启动时经 pymsg `get` 从 `core:8010` 拉取 `gateway.ini`;`confs/configs/` 下 mtime 变化自动重载。其余服务配置纯 CLI 传递(CLI > 环境变量 > 默认值),不依赖 confs。
|
||
|
||
---
|
||
|
||
## 5. 服务清单(当前事实状态)
|
||
|
||
| 服务 | 语言 | 端口 | 绑定 | 职责 | 状态 |
|
||
|------|------|------|------|------|------|
|
||
| **core** | C++20 | 5200 / 8010 | 5200 管理 / 8010 loopback | 进程托管(PMC) + 内嵌 confs 配置服务 | 生产 |
|
||
| **redirect80** | Python | 80 | 0.0.0.0 | HTTP→HTTPS 重定向 | 生产 |
|
||
| **waf** | Go | 443 | 0.0.0.0 | 边缘网关:TLS 终止 + coraza WAF | 生产 |
|
||
| **ca** | C++20 | 8443 | 127.0.0.1 | 中央认证 + 身份头注入 + 前置转发 | 生产 (v1.1.0) |
|
||
| **gateway** | C++20 | 8445 | 127.0.0.1 | 纯转发反向代理(loopback 强制自检) | 生产 (v0.9.0) |
|
||
| **fsys** | C++20 | 9200 | 127.0.0.1 | 前端托管 + dnfs 代理(static 的 C++ 重写) | 生产 |
|
||
| **dnfs2** | C++20 | 8006 | 127.0.0.1 | 树状笔记 CRUD | 生产(dnfs 的 C++ 重写) |
|
||
| **db2** | C++20 | 8005 | 127.0.0.1 | 通用 DB(pymsg execute) + 日志汇聚 | 生产(db 的 C++ 重写) |
|
||
| **memdb** | C++20 | 8011 | 127.0.0.1 | 内存缓存数据库(libmemdb + libmsg RPC,TTL 惰性驱逐,9 路由) | 生产(2026-08 合入 services.yaml) |
|
||
| **ai_review** | Python | 8007 | 127.0.0.1 | AI 内容审核(DeepSeek + ChromaDB RAG) | 生产 |
|
||
| **smtp** | Python | 8008 | 127.0.0.1 | 邮件验证码后端(pymsg) | 生产(smtp3 C++ 重写待上线) |
|
||
| **captcha2** | C++20 | 8009 | 127.0.0.1 | 图形验证码(pymsg) | 生产(captcha 的 C++ 重写) |
|
||
| **aibox** | Python | 8765 | 127.0.0.1 | 独立 AI 智能体(WebSocket) | 独立产品 |
|
||
| **BotOps** | Python | — | — | aibox 的 TUI 前端 | 独立 |
|
||
| **bot4cj** | Cangjie | — | 树莓派 I2C | 机器人脚本解析器 | 独立硬件 |
|
||
| **pybot** | Python | — | 树莓派 | bot4cj 的 Python 迁移版(.mt 解析、actpred episode 录制、--loop 实时预测闭环;HEF 后端自动异步,推理与执行上一步动作并行) | 独立硬件 |
|
||
| **mtc** | Python | — | — | 命令式动作脚本(Python DSL)→ .mt 编译器(非阻塞时间轴 IR + 10ms 栅格化),pybot/bot4cj 的脚本 authoring 工具 | 开发期工具 |
|
||
| **lci** | C++ | — | /dev/fb0 | 嵌入式帧缓冲 GUI | 独立硬件 |
|
||
| **actpred** | Python | — | — | 动作预测(图像+动作历史→下一动作),bot4cj/pybot 的「大脑」配套 | 实验 |
|
||
| **libtry** | C | — | — | 嵌入式脚本解释器 | 支撑 |
|
||
|
||
### 已被替代、不应再部署的遗留模块(不在 services.yaml)
|
||
| 遗留 | 替代 | 说明 |
|
||
|------|------|------|
|
||
| `static` (Python) | `fsys` | 前端托管已 C++ 重写 |
|
||
| `db` (Python) | `db2` | DB 服务已 C++ 重写 |
|
||
| `dnfs` (Python) | `dnfs2` | 笔记服务已 C++ 重写 |
|
||
| `captcha` (Python) | `captcha2` | 验证码已 C++ 重写 |
|
||
| `confs` (Python) | 并入 `core` | 配置服务内嵌进 core(ConfsTask) |
|
||
| `smtp3` (C++) | (预备) | 代码就绪,services.yaml 仍用 Python `smtp`,尚未平替 |
|
||
|
||
---
|
||
|
||
## 6. 共享基础库
|
||
|
||
| 库 | 语言 | 说明 |
|
||
|----|------|------|
|
||
| libth | C++20 | 线程 / 条件变量 / FSM / 任务接口 |
|
||
| libhttp | C++20 | HTTP/WebSocket(Boost.Beast,服务端 + 客户端) |
|
||
| libcrypt | C++20 | AES_CBC / RSA / SHA256 / Base64 |
|
||
| libsqlite3 | C++20 | SQLite3 header-only 封装 |
|
||
| libmsg | C++20 | pymsg 协议 C++ 实现(4B 大端长度前缀 + UTF-8 JSON),与 Python pymsg 字节级兼容,提供 RpcClient/RpcServer/AsyncQueue |
|
||
| libmemdb | C++20 | header-only 内存数据库模板库:三索引存储(list 主存 + 键索引 + TTL 索引),TTL 惰性驱逐;memdb 服务基于此库 |
|
||
| liblogc | C++20 | 统一集中式日志客户端(`us/LogC.hpp`,对应 Python `log_client.py`):pymsg/TCP 异步批量写 db2 Logs 表,队列上限 1 万条 + 级别过滤 + fail-safe,C++ 服务共用 |
|
||
| libtry | C | 嵌入式脚本解释器(递归下降,中文标识符) |
|
||
| pymsg | Python | 自研 RPC 基础库(`[4B 大端长度][UTF-8 JSON]`),Python 侧 db/log_client 依赖 |
|
||
|
||
**协议约定**:内部 RPC 统一用 pymsg(TCP 长度前缀帧 + JSON);对外 HTTP;ca↔gateway 之间为明文 HTTP(TLS 已由 waf 终止)。
|
||
|
||
---
|
||
|
||
## 7. 横切关注点
|
||
|
||
### 7.1 安全分层(纵深防御)
|
||
- **边缘**:`waf` 终止 TLS + coraza 规则(本地自定义 100000–199999 / OWASP CRS 900000–999999),命中即 403。
|
||
- **应用层**:`ca` 负责鉴权(PBKDF2 + RSA Token + CAPTCHA + 速率限制);`gateway` 仅 loopback,身份完全信任 ca 注入头。
|
||
- **网络隔离**:`db2/ai_review/smtp/captcha2/confs` 仅监听 loopback,不经 gateway 直访(`Proxy.closed`)。
|
||
- **fail-closed 文化**:`ai_review` 异常即拦截;`dnfs2` Token 验签失败返 503;`gateway` 错误响应脱敏;SMTP 内部错误对外统一通用码(如 3103)。
|
||
|
||
### 7.2 可观测性
|
||
- 集中式日志:所有服务经 `log_client` 异步写 `db2` 的 `Logs` 表(service/level/message/trace_id/extra)。
|
||
- **缺口**:无分布式追踪(trace_id 仅记录未串联)、无指标/监控中间件、无告警。
|
||
|
||
### 7.3 配置管理
|
||
- `core` 内嵌 confs(`:8010`)统一分发 `confs/configs/*.ini`;内存缓存 + mtime 重载;路径遍历防护。
|
||
- 三层覆盖:confs 默认值 → 环境变量 → 命令行参数。
|
||
|
||
### 7.4 弹性 / 容错
|
||
- 启动顺序敏感:`core` 拉起顺序为 db2→memdb→ai_review→dnfs2→smtp→fsys→waf→ca→gateway→captcha2→aibox→redirect80(共 12 个);pymsg 客户端带 `retry_on_refused` 等依赖就绪。
|
||
- 日志 fail-safe:db2 不可用时静默丢弃,不影响业务。
|
||
- 有状态单点:`smtp` / `captcha2` 验证码存进程内存,**不支持多实例水平扩展**(需换 Redis 才能扩)。
|
||
|
||
---
|
||
|
||
## 8. 关键架构决策(ADR 摘要)
|
||
|
||
**ADR-1|认证从 gateway 剥离为独立 ca 模块(v0.9.0 / ca v1.0.0)**
|
||
- 背景:gateway 同时承担鉴权与转发,职责过重、RSA 密钥与业务逻辑耦合。
|
||
- 决策:ca 独立负责 Token 签发/验签、登录/注册/限流/CAPTCHA,gateway 退居 `127.0.0.1:8445` 纯转发。
|
||
- 后果:+1 网络跳数;ca 成为安全边界(loopback + strip-then-set);gateway 不再依赖 libcrypt/OpenSSL。
|
||
|
||
**ADR-2|核心服务 C++20 重写(db→db2, dnfs→dnfs2, static→fsys, captcha→captcha2, confs→core;smtp3 就绪)**
|
||
- 背景:Python 服务依赖 venv/pip/Flask,且部分存在 P0/P1 安全问题。
|
||
- 决策:用 libhttp+libmsg+boost 重写,接口字节级兼容(pymsg/HTTP 路由不变),可平替。
|
||
- 后果:去掉 Python 运行时依赖、修复安全缺陷、提升吞吐;过渡期需双版本并存维护;ai_review/aibox/smtp/redirect80 仍保留 Python。
|
||
|
||
**ADR-3|confs 配置服务内嵌进 core**
|
||
- 背景:独立 confs 进程增加编排复杂度。
|
||
- 决策:confs2 合并为 `core` 的 `ConfsTask`,由 `--confs <port>` 启用。
|
||
- 后果:少一个守护进程;core 略微变重;配置协议与目录结构不变。
|
||
|
||
**ADR-4|pymsg 作为内部 RPC 标准**
|
||
- 决策:内部数据访问统一 `[4B 大端长度][UTF-8 JSON]` over TCP,C++/Python 双实现字节级兼容。
|
||
- 后果:业务服务不直接连数据库,数据访问层解耦;引入轻量自研协议(无现成生态)。
|
||
|
||
---
|
||
|
||
## 9. 演进策略与已知缺口
|
||
|
||
- **无服务注册发现 / 容器编排**:服务地址在 `waf/config.yaml` 与 `gateway` 的 `Proxy.*` 中静态配置,由 `core` 进程托管。适合个人/中小规模 DIY,但扩展需手工。
|
||
- **smtp3 待上线**:C++ 重写已完成并通过安全审计,但 services.yaml 仍指向 Python `smtp`,建议择机平替以消除最后一块 Python 有状态服务。
|
||
- **可观测性缺口**:缺 tracing/metrics/告警,建议后续引入轻量方案(或复用 db2 Logs + 定时聚合)。
|
||
- **水平扩展限制**:smtp/captcha 内存态为单点,若需多实例需引入 Redis 等共享存储。
|
||
- **panel 面板微服务(规划中)**:设计稿见 [docs/panel_service.md](panel_service.md)——背景视图 + 各服务自持控件片段(iframe 沙箱 + BFF 转发),解决整页应用互相改前端代码的耦合问题;落地后需三处同步(本文件 / MODULES.md / 根 README)。
|
||
|
||
---
|
||
|
||
## 10. 与 README.md 的偏差(已同步,2026-08-10)
|
||
|
||
以下为本文相对于根 `README.md` 的纠正点。2026-08-10 已按本清单回填 README 并同步 MODULES.md,状态如下:
|
||
|
||
1. **端口**:✅ 已修正。README 端口表原写 `gateway 8443`,现已改为 `ca:8443`、`gateway:8445`。
|
||
2. **前端模块**:✅ 已修正。README 现以 `fsys`(C++ 重写)为主表,`static` 标注遗留。
|
||
3. **C++ 重写版未进主表**:✅ 已修正。README 模块表现以 `db2`/`dnfs2`/`captcha2` 为主,`db`/`dnfs`/`captcha` 标注遗留,`confs` 标注已并入 `core`。
|
||
4. **init.txt**:✅ 已同步。core 已于 2026-08 移除 init.txt 兼容,仅支持 `services.yaml`(提交 b0d71a4);README/本文件均已更新为 `-i . --serve 5200 --confs 8010`。
|
||
5. **fsys README 图示滞后**:✅ 已修正(2026-08-10)。`fsys/README.md` 架构图与配置章节已更新为 `ca:8443 → gateway:8445`、纯 CLI 配置。
|
||
6. **工作记忆偏差**:✅ 已同步。`ca:8443 → gateway:8445`、`dnfs2`、confs 内嵌 core 均已写入各文档。
|
||
7. **dnfs2 README 图示滞后**:✅ 已修正(2026-08-10)。`dnfs2/README.md` 架构图与验签描述已更新为 ca:8443(CLI 参数 `--gateway-url` 指向 ca)。
|
||
8. **memdb / pybot / mtc 未进根 README**:✅ 已回填(2026-08-15)。`memdb:8011`、`pybot`(bot4cj Python 迁移版)、`mtc`(Python DSL → .mt 编译器,2026-08-15 落地)连同 `actpred` 已入根 `README.md` 模块表与端口一览;本文件与 MODULES.md 已同步。
|
||
|
||
---
|
||
|
||
## 11. 文档管理约定(遵循既有规则)
|
||
|
||
- 全局架构文档置于 `docs/`(本文件)。
|
||
- 模块细节写在各模块目录 `README.md`。
|
||
- 审计/审查/报告类文档分别入 `docs/security`、`docs/reviews`、`docs/reports`。
|