3.9 KiB
3.9 KiB
系统架构
本文描述直播组件服务的运行边界、数据流和扩展点。实现代码分别位于 apps/server-rust 与
apps/overlay。
核心目标
- 每个账户固定绑定一个 Bilibili 直播间和一个独立直播源。
- 一个直播源只建立一条上游连接,但可以把事件投递给多个组件实例。
- 平台原始命令先转换成稳定的领域事件,组件不直接依赖 Bilibili
CMD。 - HTTP 会话、直播源、组件、OBS token 和实时通道均以租户为边界。
- 新组件可以增加设置、投影和持久化副作用,而不修改直播连接核心。
运行时数据流
flowchart LR
CC[CookieCloud] --> PF[ProviderFactory]
BL[Bilibili Live] <--> BP[BilibiliProvider]
PF --> BP
BP --> LE[LiveEvent]
LE --> SR[SourceEventRouter]
PG[(PostgreSQL)] --> CR[Component repository/cache]
CR --> SR
SR --> EH[Durable handlers]
SR --> PJ[Passive projection]
PJ --> HUB[Component-scoped EventHub]
HUB --> WS[Authenticated WebSocket]
WS --> OBS[OBS overlay]
SourceSupervisor为每个source_id保持至多一个 provider task。BilibiliProvider使用该用户加密保存的 CookieCloud 凭据获取 Cookie,并把原始消息转换成LiveEvent。SourceEventRouter同时使用owner_id与source_id查找启用的组件,并再次检查组件归属。- 匹配订阅后,路由器先执行可持久化的
EventHandler,再执行无副作用的EventProjection。 - 投影结果只发布到该
component_id的广播通道。没有全局 WebSocket 事件总线。 - OBS 使用组件级只读 token 订阅一个组件,不能读取控制台 API。
状态所有权
| 状态 | 权威来源 | 内存副本 | 说明 |
|---|---|---|---|
| 用户、TOTP、会话 | PostgreSQL | 无 | Secret 加密,token 只保存摘要 |
| CookieCloud 凭据 | PostgreSQL | provider 构建期间解密 | 不返回浏览器 |
| 直播源和房间 | PostgreSQL | SourceSupervisor |
每用户固定一个房间 |
| 组件实例与设置 | PostgreSQL | InMemoryComponentStore |
写入成功后刷新热路径缓存 |
| 礼物/表情目录 | Bilibili API | provider catalog | 刷新失败保留最近成功快照 |
| 实时消息 | provider | EventHub 有界广播 |
不作为业务持久化机制 |
| PWA 静态壳层 | Docker 镜像 | Cache Storage | 不包含 API 或用户数据 |
启动顺序
AppState::build 按以下顺序启动,避免事件进入未完成的运行时:
- 创建 PostgreSQL 连接池并执行嵌入式迁移。
- 构造加密和 passwordless 鉴权服务。
- 注册内建组件定义。
- 从 PostgreSQL hydrate 组件热路径缓存。
- 创建有界源事件队列、路由器和每组件广播中心。
- 为数据库中所有启用的直播源启动 provider。
- 最后由
main.rs绑定 HTTP 监听端口。
关机时先停止接收 HTTP,再取消所有 provider task,防止容器退出期间继续拉取上游事件。
进程与部署边界
- Node 只存在于 Docker 的前端构建阶段。
- 最终镜像仅运行 Rust 可执行文件,并从
/app/web同域托管静态资源。 - 容器使用 host network,但默认只监听
127.0.0.1:9719。 - Nginx 负责公网 TLS、域名和 WebSocket upgrade。
- CookieCloud 与 PostgreSQL 是外部服务,不由本项目 Compose 创建。
代码导航
- 后端模块职责:
apps/server-rust/README.md - 前端路由和状态:
apps/overlay/README.md - 组件扩展指南:
components/README.md - WebSocket 协议:
protocol.md - 安全边界:
security.md