系统架构
YiYi Media 分成两层:控制面负责编排、元数据与账号,节点层负责存储与播放。 两层通过注册中心解耦——节点只需要知道 Config 服务地址,其余服务地址自动发现。
服务模块本身是独立的:Config、User、Media、Gateway、Storage 都作为独立进程运行, 只是可以被聚合到同一个容器里。单机版就是把它们收进一个 yiyi-app 容器, 分布式版则按角色把它们分散到多台机器上。
两种部署形态
八个服务收进一个容器,Storage 与 Play Agent 为内置节点,随主应用安装、启动、升级;用户只能新增手动反代地址,不能新增外部节点。
frontend 内置 nginx 把 /api/* 反代到 127.0.0.1:18086,因此 frontend 与 gateway 必须同机。
横向扩容单元:新增外部节点即扩容存储与播放能力,无需改动控制面配置,但受授权配额限制。
单机版(STANDALONE):
┌──────────────── yiyi-app 容器 ────────────────┐
│ nginx/frontend :18080 ← 唯一对外入口 │
│ gateway :18086 (仅容器内部) │
│ config :18085 (仅容器内部) │
│ user :18082 (仅容器内部) │
│ media :18083 (仅容器内部) │
│ storage :18084 (仅容器内部) │
│ play-agent :19090 (供本机反代使用) │
│ license-agent :18088 (仅容器内部) │
└───────────────────────────────────────────────┘
│ │
postgres 容器 redis 容器分布式版(DISTRIBUTED):控制面按 control / user / media / edge 拆到多台机器, Storage 与 Play Agent 作为外部工作节点部署在任意多台服务器上。
两种形态的功能模块完全相同,区别在部署模式、节点模型与许可证 Edition。 完整的模式对比见部署模式与版本选择。
服务职责
控制面
| 服务 | 默认端口 | 职责 |
|---|---|---|
frontend | 18080 | Web 界面。唯一的对外 Web 入口,同时把 /api/* 反代到本机 gateway |
gateway | 18086 | API 统一入口与反向代理,也是授权状态的查询入口 |
config | 18085 | 注册中心 + 节点管理。服务发现、节点登记都在这里;一键安装脚本与二进制分发仅分布式版提供 |
user | 18082 | 认证、账号、到期与续期、邀请、流量配额、活动日志、播放报表 |
media | 18083 | 媒体元数据:媒体库、条目聚合、同步、虚拟库、元数据覆盖、来源访问策略 |
节点层
| 节点 | 默认端口 | 职责 | 单机版 | 分布式版 |
|---|---|---|---|---|
storage | 18084 | 云盘与文件管理:挂载源、FUSE 挂载、传输任务、刮削、文件缓存、备份、WebDAV 出口 | 内置 1 个(node-local-storage) | 外部工作节点,可多实例 |
play-agent | 19090 | 播放代理:Emby 协议出口、视频流代理、VFS 启播缓存、限速与配额执行、CDN 优选 | 内置 1 个(node-local-play-agent) | 外部工作节点,可多实例 |
内置节点由 Config 启动组件幂等创建,随应用容器启动、停止和升级,禁止删除、卸载、独立升级与修改服务类型。 外部节点在「节点管理」里创建后自助部署,按授权配额横向扩容。
存储与依赖
| 组件 | 默认端口 | 说明 |
|---|---|---|
| PostgreSQL | 5432 | 按服务分库:yiyi_config、yiyi_user、yiyi_media、yiyi_storage |
| Redis | 6379 | 缓存依赖:媒体元数据缓存、播放入口列表缓存、登录失败计数共享 |
单机版由同一个 PostgreSQL 实例承载四个数据库,PostgreSQL 与 Redis 走 Compose 私有网络, 应用容器内用 postgres:5432 / redis:6379 访问。分布式版按部署手册配置。
frontend 与 gateway 必须同机
frontend 内置的反代把 /api/* 指向写死的 127.0.0.1:18086。 因此这两个服务不能拆开部署。单机版里它们在同一个容器内; 分布式部署时它们同属 edge 角色,由部署包一起拉起。
服务发现
节点启动时向 Config 服务报到,Config 维护注册表并做心跳与清理:
- 节点只需要配置注册中心地址
YIYI_CONFIG_HOST与节点 IDYIYI_NODE_ID。 - 节点不需要配置
YIYI_SERVER_HOST:启动时凭YIYI_NODE_ID向 Config 查询自己的对外地址, 也就是节点管理里登记的expectedHost。 - 注册表由两个调度任务维护:
registry.cleanup每 30 秒清理失效注册,infrastructure.heartbeat.check每 15 秒巡检服务注册、数据库与存储源心跳。
这样做的好处是:换机器、换 IP、加节点,都不用回去改一堆配置文件。
一次播放是怎么走完的
元数据集中在控制面,视频字节由播放节点直连输出,不经过控制面服务器。
为什么要缓存签名直链
一次播放有几百个分片请求,逐个去网盘换直链极易触发风控,所以播放节点会复用签名直链。 键规则、TTL、负向缓存与并发合并的完整机制见加载与播放速度。
元数据是怎么流动的
刮削与聚合是分开的两件事:
网盘文件变更
│
├─ 存储节点:scrape.queue(每 15 秒)执行刮削,产出元数据
│ file-change-log.scrape(每 15 秒)重放文件变更日志
│
▼
存储节点把变更推给媒体服务
│
▼
媒体服务:sync.queue.process(每 30 秒)消费变更队列
central-media.sync(每 300 秒)增量同步已启用的媒体库
│
▼
聚合成对外条目(Aggregate Item)
│
▼
播放节点从媒体服务取条目,播放时按挂载源回源到存储节点这些节奏都可以在调度中心里查看,也可以手动触发。
技术元数据(容器格式、时长、音视频轨道)走另一条链路:播放节点内嵌 ffprobe, 在播放时异步补全,不阻塞播放,也不替代存储侧的刮削。
另外还有一条外部触发链路:入站 Webhook 接收 NAS(如群晖)的文件变更通知, 按 replace_path 剥离挂载前缀后匹配媒体库并分发刮削任务,命中多个来源时全部下发。
客户部署
客户安装走独立部署包 YiYi-Product/YiYi-media-deploy。 两种部署模式用的是同一个仓库、同一个 install.sh。仓库按部署方式分成三个分支, 每个分支根目录都是一套完整部署文件:
| 部署方式 | 分支 | 拓扑 | 需要的角色变量 |
|---|---|---|---|
单机版(STANDALONE) | main | YiYi-media-standalone + -postgres + -redis 三容器 | 无,填服务器地址即可 |
| 分布式·控制面同机 | v2-all-in-one | 6 个应用服务全在一台机器(数据库/缓存可选外部) | 无,填服务器地址即可 |
| 分布式·按角色多机 | v3-multi-host | control → user → media → edge 四台 | 每台填自己的角色 |
两种分布式方式共用同一套控制面与同一份许可证,只是布置不同。
按角色多机时的四个角色:
| 角色 | 装什么 | 说明 |
|---|---|---|
control | postgres、redis、license-agent、config | 主服务器,只装一台 |
user | license-sync、user | 用户服务 |
media | license-sync、media | 媒体服务 |
edge | license-sync、gateway、frontend | 网页入口,两者必须同机 |
跨模式不能靠改角色原地互转
control、user、media、edge 是分布式部署内部的角色,不是独立的部署模式。 单机版与分布式版之间需要授权的专用版本升级操作 + 部署拓扑迁移; 不允许只改 .env 或角色变量切换。详见单机版迁移到分布式版。
节点模型
单机版:两个内置节点
单机版固定内置一个 Storage(node-local-storage)与一个 Play Agent(node-local-play-agent):
- 由应用容器监管,随主应用启动、停止、升级;
- 禁止删除、卸载、独立升级与修改服务类型;
- Storage 自动成为新浏览器会话的默认文件管理节点;
- 用户只能新增手动反代地址,不能新增外部节点。
分布式版:两条独立扩容轴
分布式版的 Storage 与 Play Agent 是外部工作节点,不由部署包安装—— 登录网页后进「节点管理」创建节点,页面会给出可复制的一键安装命令,复制到目标机执行即可。
它们是一组独立的横向扩展单元:
| 轴 | 扛什么 | 扩容信号 |
|---|---|---|
Storage 节点(18084) | 网盘配额、刮削算力、文件传输 | 想再接一个盘、刮削排队久、大批新文件入库慢 |
Play Agent 节点(19090) | 出口带宽、并发播放 | 晚高峰卡顿、并发上不去、想给不同地区走不同线路 |
两者都可以多实例、可以部署在任意位置的服务器上,而且加节点不需要改控制面任何配置: 节点凭 YIYI_NODE_ID 向注册中心报到,对外地址由注册中心下发,服务之间自动发现。
完整的扩容决策表见部署模式与版本选择,节点部署步骤见 分布式工作节点部署。
网络与端口
完整端口表与暴露建议见端口与网络。要点:
- 单机版把前端
18080与 Play Agent19090发布到宿主机;19090 是播放客户端直连端口,可收窄为回环配合反代。 - 分布式版里
frontend(18080)、storage(18084)、play-agent(19090)需要按需对外可达。 config(18085)建议只对内网开放,但节点所在机器必须能访问它。gateway、user、media不直接对外,都经 nginx 或 gateway 代理。license-agent默认只绑127.0.0.1:18088。
生产环境通常在前面再加一层反向代理(Caddy / nginx)做 HTTPS 与域名收敛, 播放端点会走独立域名。详见反向代理与域名。
授权在架构里的位置
授权状态由各服务在本地校验:每个服务读自己那份租约文件判断能否放行, 不需要每次请求都去问授权中心,所以授权中心断连不会立刻影响业务。
签名租约里的 Edition(STANDALONE / DISTRIBUTED)是最终授权依据, 由服务端强制执行能力边界。前端只消费后端返回的派生能力,不直接决定是否允许节点操作。
只有「已激活」与「宽限期」两种状态允许业务操作。越过宽限截止进入受限模式: 不删数据、不卸卷,保留管理员登录、激活、状态查询、日志导出与备份, 拒绝新播放会话、文件写入、调度任务与新节点注册。 授权不限制用户数,也不绑硬件。
完整规则、Edition 匹配与受限模式行为见授权与版本。