为什么: 1) 产品已定名 FruitShorts,但后端仍是 reelshort 系列标识 —— Java 包 com.reelshort、 模块名与 jar 名 reelshort-*、spring.application.name、配置前缀 reelshort.*, 这些会出现在启动日志、命令行和 IDE 里,与产品名不一致。本次整体替换为 fruitshorts, 数据库连接名上一轮已改名,保持不变。 2) 第一阶段下一批第 1 步(05 离线下载)要求把下载授权(设备绑定/有效期/吊销)、 下载任务与断点续传、配额与过期清理、仅 Wi-Fi 规则做成完整接口,本提交一并落地。 3) 顺带修复:schema-phase1.sql 里 playback_ticket / order_event 仍按普通表定义唯一索引, 表分区化之后重跑脚本必定报错(分区表的唯一索引必须包含分区键)。改为在本脚本里就 按分区表定义,并验证脚本可重复执行。
293 lines
17 KiB
Markdown
293 lines
17 KiB
Markdown
# 后端服务设计(第一阶段 · 印尼市场 · 1 万并发观看)
|
||
|
||
> 版本 v1.0 · 配套 [第一阶段 PRD](./prd-phase1.md) · 现有实现见 [架构设计](./architecture-design.md)
|
||
> 目标口径:**10,000 路并发播放不劣化**(对应 DAU 约 25–35 万),首发印尼。
|
||
|
||
## 1. 目标与约束
|
||
|
||
**业务目标**:日活规模下 1 万路并发观看流畅(首帧 P95 < 1.5s、卡顿率 < 2%),分集收费闭环准确无误,创作者上传到可播放全自动。
|
||
|
||
**工程约束**:
|
||
|
||
1. 首发印尼,用户全部在东南亚,服务端与内容必须就近部署,不能用国内机房直连。
|
||
2. 团队是 14–16 人的 MVP 团队,运维只有 1 人,所以不做微服务拆分,保持模块化单体。
|
||
3. 数字商品必须走 Apple IAP / Google Play Billing,服务端只做校验与权益发放。
|
||
4. 成本敏感:视频流量是最大成本项,码率与缓存策略从一开始就要按成本设计。
|
||
|
||
## 2. 现状盘点与差距
|
||
|
||
当前实现:7 个 Maven 模块的模块化单体,`fruitshorts-admin-api`(8080) + `fruitshorts-app-api`(8081),
|
||
MyBatis-Plus + PostgreSQL,媒资落本地磁盘 `data/uploads`,静态文件直出,无转码、无 CDN、无缓存层、无消息队列。
|
||
|
||
| 能力 | 现状 | 目标 | 优先级 |
|
||
| --- | --- | --- | --- |
|
||
| 媒资存储 | 本地磁盘,随机器走 | 对象存储(S3 兼容)+ 生命周期分层 | P0 |
|
||
| 视频分发 | 源站直出原文件 | 转码成 HLS 多码率 + CDN 分发 + 签名 URL | P0 |
|
||
| 缓存 | 无 | Redis + 本地 Caffeine 二级缓存 | P0 |
|
||
| 异步 | 无 | 消息队列(转码任务、计数、埋点、对账) | P0 |
|
||
| 数据库 | 单实例单库 | 主库 + 只读副本,热点与日志表分区 | P1 |
|
||
| 播放鉴权 | 免费集直接给地址 | 票据 + 签名 URL + 有效期 + 吊销 | P0 |
|
||
| 限流熔断 | 无 | 网关级限流 + 按接口降级 | P0 |
|
||
| 支付 | 未实现 | 订单状态机 + 票据校验 + 对账 | P0 |
|
||
| 可观测 | 慢请求日志 | 指标 + 链路追踪 + 告警 + 看板 | P1 |
|
||
|
||
结论:现有代码是功能验证级,架构骨架(模块划分、鉴权、内容与媒资模型)可以直接复用,
|
||
但"视频从源站直出"这一点在 1 万并发下会立刻打爆,必须替换成 CDN + 签名 URL。
|
||
|
||
## 3. 容量规划
|
||
|
||
### 3.1 码率阶梯与带宽
|
||
|
||
印尼以 4G 为主、资费敏感,码率阶梯压到偏低档位:
|
||
|
||
| 档位 | 分辨率 | 视频 | 音频 | 合计 | 预期占比 |
|
||
| --- | --- | --- | --- | --- | --- |
|
||
| 360p | 640x360 | 400 kbps | 64 kbps | ~0.47 Mbps | 40% |
|
||
| 480p | 854x480 | 800 kbps | 64 kbps | ~0.87 Mbps | 45% |
|
||
| 720p | 1280x720 | 1600 kbps | 96 kbps | ~1.70 Mbps | 12% |
|
||
| 1080p | 1920x1080 | 3200 kbps | 128 kbps | ~3.30 Mbps | 3% |
|
||
|
||
按占比加权,平均码率约 **0.88 Mbps**。
|
||
|
||
- 10,000 路并发出口带宽约 **8.8 Gbps**
|
||
- 叠加首段突发、重连与预加载(+30%),峰值 **约 11–12 Gbps**
|
||
- CDN 命中率按 98% 计,回源带宽 **约 240 Mbps**
|
||
|
||
源站只需 2 台带 1 Gbps 出口的机器即可承载回源,不需要为每个观众准备源站带宽。
|
||
这条推导是整个设计的关键:**播放流量必须由 CDN 承担,源站只回源 2%**。
|
||
|
||
### 3.2 接口 QPS
|
||
|
||
| 链路 | 估算方式 | 平均 | 峰值(x3,20:00–23:00 WIB) |
|
||
| --- | --- | --- | --- |
|
||
| 首页/发现/详情 | 每活跃用户每分钟 2 次 | ~333 QPS | ~1,000 QPS |
|
||
| 播放票据签发 | 每集一次,平均 4 分钟/集 | ~42 QPS | ~125 QPS |
|
||
| 播放进度上报 | 每 10 秒一次 | 1,000 QPS | 3,000 QPS |
|
||
| 埋点/播放计数 | 客户端按 30s 合批 | ~330 QPS | ~1,000 QPS |
|
||
|
||
结论:
|
||
|
||
- **读接口** 经 Redis + 本地缓存后,4–8 个 API 副本足够(单副本可撑 1,500+ QPS)。
|
||
- **进度上报不直接打库**:写 Redis,后台任务按 `user+drama` 合并后批量 UPSERT,落库压力降到 100 QPS 量级。
|
||
- **埋点与计数走消息队列**,消费者聚合后写回,不用事务强一致。
|
||
|
||
### 3.3 存储与转码
|
||
|
||
按第一阶段 300 部 x 60 集 x 单集 2 分钟估算:
|
||
|
||
| 项 | 计算 | 容量 |
|
||
| --- | --- | --- |
|
||
| 原片(1080p) | 18,000 集 x ~50 MB | ~0.9 TB |
|
||
| 转码产物(4 档 HLS) | 18,000 集 x ~95 MB | ~1.7 TB |
|
||
| 封面/截图/字幕 | — | ~0.1 TB |
|
||
| **合计** | | **约 3 TB** |
|
||
|
||
转码算力:单集 1080p 转 4 档约 8 分钟机时,首月入库 18,000 集约 2,400 小时;
|
||
100 核并行约 24 小时跑完。**按需弹性即可,不需要常驻转码集群**,但必须排队限流,避免抢在线服务的 CPU。
|
||
|
||
### 3.4 成本与单位经济性
|
||
|
||
假设 DAU 30 万、人均日观看 40 分钟、平均码率 0.88 Mbps:
|
||
|
||
| 项 | 月度量/成本 | 说明 |
|
||
| --- | --- | --- |
|
||
| CDN 流量 | 约 79 TB/天,约 2,300 TB/月,**$50k–75k** | 按东南亚 $0.022–0.032/GB,**最大成本项** |
|
||
| 对象存储 | 3 TB,约 $60–100 | 冷热分层后更低 |
|
||
| 数据库 / Redis / MQ | $300–800 | 主从 + 只读副本 + 集群缓存 |
|
||
| API 与后台计算 | $400–1,200 | 4–8 副本 + 弹性转码 |
|
||
| 短信 OTP | $500–2,000 | 按 $0.02–0.08/条,必须频控 |
|
||
| 监控 / 日志 / APM | $100–300 | |
|
||
| **合计** | **约 $52k–79k/月** | CDN 占绝大部分可变成本 |
|
||
|
||
单位经济性(决定商业模型是否成立):
|
||
|
||
- 单集 2 分钟、0.88 Mbps,约 13 MB 流量,CDN 成本约 **$0.0004**
|
||
- 单集解锁(按 30 金币约 $0.03),视频成本约占解锁收入 **1.5%**
|
||
|
||
推论:
|
||
|
||
1. 金币解锁模式下视频成本可忽略;真正的压力来自第二阶段的广告模式(ARPU 低一个数量级时 CDN 会变成主要成本)。
|
||
2. 因此第一阶段就要把平均码率、缓存命中率、CDN 单价做成**看板与告警**(单位播放成本 $/千次播放)。
|
||
3. 把平均码率从 0.88 压到 0.6 Mbps(更多 360p、更激进的 ABR 起播策略)可直接省掉约 30% 的 CDN 账单,比任何架构优化都有效。
|
||
|
||
## 4. 部署与网络拓扑
|
||
|
||
```
|
||
印尼用户 (iOS / Android)
|
||
| 1. 接口请求(JSON, HTTPS)
|
||
v
|
||
CDN / 全球加速 --- 2. 视频分片与静态资源(HLS,签名 URL,命中率 >=98%)
|
||
| ^
|
||
| 回源(约 2%) |
|
||
v |
|
||
区域入口 (Jakarta) |
|
||
|- API 网关:TLS、限流、鉴权、WAF |
|
||
|- app-api x4-8(K8s HPA) |
|
||
|- admin-api x2(可放新加坡/内网,仅内部使用) |
|
||
|- worker(转码调度、计数聚合、对账、清理) |
|
||
|
|
||
|- PostgreSQL(主 + 只读副本)
|
||
|- Redis(集群:缓存 / 票据 / 进度缓冲 / 限流)
|
||
|- 消息队列(转码任务、埋点、计数、对账事件)
|
||
|- 对象存储 OSS(S3 兼容)--- 转码产物 -----------+
|
||
```
|
||
|
||
**区域选择**:首选**雅加达**(阿里云 / 腾讯云 / AWS 均有区域),退路是新加坡
|
||
(ap-southeast-1,到雅加达 RTT 约 20–30ms,仍可接受)。依据是到用户 RTT、CDN 覆盖与本地 PoP 价格、数据驻留要求。
|
||
|
||
**为什么要 API 网关**:限流、IP 黑名单、WAF、灰度都放在网关层,业务服务不必各自实现一遍。
|
||
|
||
**国内团队访问生产**:运营后台走独立入口 + 内网跳板,不要与 C 端出口混用。
|
||
|
||
## 5. 服务划分与演进
|
||
|
||
第一阶段**保持模块化单体**,按业务域划清边界,为后续拆分留缝:
|
||
|
||
| 模块 | 职责 | 未来拆分优先级 |
|
||
| --- | --- | --- |
|
||
| user | 账号、凭证、会话、资料、风控 | 中 |
|
||
| content | 短剧、剧集、分类、标签、榜单 | 低 |
|
||
| media | 上传、转码、媒资、播放票据 | **高**(与 CDN/转码强绑定) |
|
||
| playback | 进度、记录、收藏、下载授权 | 中 |
|
||
| interaction | 评论、点赞、举报、分享 | 低 |
|
||
| commerce | 商品、订单、钱包、权益、对账 | **高**(支付需审计隔离) |
|
||
| creator | 创作者入驻、内容提交、数据看板 | 中 |
|
||
| admin/system | RBAC、菜单、审计、配置 | 低 |
|
||
|
||
拆分只在这两个条件下发生:**独立扩缩容需求**或**合规隔离需求**。其余情况一律不加服务。
|
||
|
||
## 6. 数据模型与存储
|
||
|
||
核心表(现有 `sql/schema.sql` 已有基础,"新增"为需要补充的部分):
|
||
|
||
- 内容域:`drama`、`drama_episode`、`drama_category`、`content_banner`,已具备。
|
||
- 媒资域:`media_asset`、`media_upload_task` 已具备;**新增** `transcode_task`、`media_rendition`(多码率产物)。
|
||
- 用户域:**新增** `app_user`、`user_credential`、`user_session`、`user_profile`(当前只有后台管理员表)。
|
||
- 播放域:**新增** `watch_progress`(user+drama 唯一)、`watch_history`(按月分区)、`user_favorite`、`playback_ticket`。
|
||
- 互动域:**新增** `comment`、`comment_reply`、`user_like`、`report`、`blocklist`。
|
||
- 交易域:**新增** `product`、`order`、`order_event`、`wallet`、`coin_ledger`、`entitlement`、`receipt`。
|
||
- 创作者域:**新增** `creator`、`creator_document`、`content_submission`、`audit_record`。
|
||
|
||
存储策略:
|
||
|
||
- 大表分区:`watch_history`、`order_event`、`coin_ledger`、`playback_event` 按月分区,保留 12–24 个月。
|
||
- 冷热分离:长期不播放的媒资转低频/归档存储,源站只保留热内容。
|
||
- 读写分离:报表、导出、后台检索走只读副本,避免影响 C 端。
|
||
- 连接池:Hikari 每副本 20–30 连接,按副本数 x 连接数留足 PG `max_connections` 余量。
|
||
|
||
## 7. 关键链路设计
|
||
|
||
### 7.1 播放与地址签发(第一阶段最重要的改造)
|
||
|
||
1. 客户端请求 `/app/episodes/{id}/play`,服务端判定权益(已购 > 活动免费 > 免费集 > 试看 > 金币可扣 > 需充值)。
|
||
2. 可播放时签发**短时效签名 URL**(建议 15 分钟),签名绑定 `episodeId + userId + 清晰度 + 过期时间`。
|
||
3. 返回 HLS master playlist 地址(CDN 域名),CDN 校验签名后才回源。
|
||
4. 签名校验失败、过期或用户被吊销,直接 403,客户端提示重新获取。
|
||
5. 防盗链:Referer/UA 白名单 + 单账号同时在播设备数限制(建议 1–2 台)。
|
||
|
||
关键点:客户端拿不到可长期复用的裸地址,也不会出现"服务端扛视频"的情况。
|
||
|
||
### 7.2 上传与转码流水线
|
||
|
||
```
|
||
创作者分片直传 OSS(STS 临时凭证)
|
||
-> 合并校验(MD5 / 时长 / 编码探测)
|
||
-> 写 transcode_task(排队)
|
||
-> worker 拉起 FFmpeg 或云转码:4 档 HLS + 封面 + 字幕封装
|
||
-> 回调写 media_rendition
|
||
-> CDN 预热
|
||
-> 状态回写创作者中心(可发布)
|
||
```
|
||
|
||
失败重试 3 次并记录原因;转码队列与在线服务隔离(独立节点池或命名空间),避免抢占资源。
|
||
|
||
### 7.3 支付与权益
|
||
|
||
1. 客户端拿到商店票据后提交 `/app/orders/verify`。
|
||
2. 服务端**必须**再调 Apple / Google 校验接口二次验证,不信任客户端上报。
|
||
3. 订单状态机幂等:以 `(platform, transactionId)` 唯一键去重,重复回调只返回既有结果。
|
||
4. 权益发放与订单在同一事务;发放失败进补偿队列,T+1 内自动修复并留痕。
|
||
5. 退款与撤销事件(App Store Server Notifications / Google RTDN)触发权益回收或欠款标记,写 `coin_ledger`。
|
||
6. 每日对账:拉取商店账单与本地订单比对,差异进入人工处理队列并告警。
|
||
|
||
### 7.4 观看进度与记录
|
||
|
||
- 客户端每 10s 上报一次;服务端写 Redis(`progress:{user}:{drama}`),不直接写库。
|
||
- 后台任务每 30s 把 Redis 中的变更按剧合并,批量 UPSERT 到 `watch_progress`(每用户每剧一行)。
|
||
- `watch_history` 按事件追加、按月分区,用于"最近观看"与后续推荐。
|
||
- 冲突规则:以 `updatedAt` 较新者为准;同秒冲突取进度较大者,避免进度倒退。
|
||
|
||
### 7.5 计数与埋点
|
||
|
||
播放数、完播数、点赞数一律走消息队列,消费者按窗口聚合后定时写回计数表。
|
||
读取计数走缓存,允许秒级最终一致,不允许为了强一致把写压力压到主库。
|
||
|
||
## 8. 高并发与稳定性手段
|
||
|
||
| 手段 | 落点 |
|
||
| --- | --- |
|
||
| 多级缓存 | 客户端本地缓存 > CDN > Redis > Caffeine 本地缓存 > PG |
|
||
| 读缓存 | 首页/详情/分类结果缓存 30–300s,运营改动通过版本号主动失效 |
|
||
| 异步化 | 进度、计数、埋点、转码、对账全部异步 |
|
||
| 合批 | 进度上报合并写、计数聚合写、日志批量落盘 |
|
||
| 限流 | 网关按 IP/设备/用户三维限流,重点保护 OTP 与票据签发 |
|
||
| 熔断降级 | 短信通道、商店校验、审核供应商失败时降级并告警;播放链路的降级是"降码率"而不是"不可播" |
|
||
| 幂等 | 订单、回调、注册、上传合并全部幂等键化 |
|
||
| 游标分页 | 列表接口用 cursor 而非深 offset,避免大偏移查询 |
|
||
| 连接管理 | 视频分片走 CDN,API 走 HTTP/2 keep-alive,PG 连接池按副本配额 |
|
||
| 压测门禁 | 上线前通过 1 万并发播放 + 1,000 QPS 接口压测,错误率 < 0.1% |
|
||
|
||
## 9. 可观测性与 SLO
|
||
|
||
| 指标 | 目标 | 告警阈值 |
|
||
| --- | --- | --- |
|
||
| 接口可用性 | 99.9% | 5 分钟错误率 > 1% |
|
||
| 接口延迟 P95(客户端热点接口) | < 50ms | > 100ms 持续 5 分钟 |
|
||
| 首帧 P95 | < 1.5s | > 2.5s |
|
||
| 卡顿率 | < 2% | > 4% |
|
||
| CDN 命中率 | >= 98% | < 95% |
|
||
| 单位播放成本 | 有基线并有月度预算 | 超预算 20% |
|
||
| 支付成功率 | > 97% | < 95% |
|
||
|
||
埋点与看板分四层:接口层(QPS/延迟/错误码)、播放层(起播/卡顿/完播/ERROR_CODE)、
|
||
业务层(注册/首播/解锁/支付)、成本层(CDN 流量/存储/短信条数)。
|
||
链路追踪覆盖"支付"与"播放票据签发"两条关键链路。
|
||
|
||
## 10. 安全与合规(印尼)
|
||
|
||
- **合规**:Kominfo **PSE 注册**(未注册可能被整体封锁);UU 27/2022 个人数据保护(明示同意、删除权、导出权、跨境传输依据);内容分级(R13/R17/R21)与色情、宗教、政治敏感内容零容忍。
|
||
- **应用安全**:网关 WAF;接口越权(水平 + 垂直)全量校验;密钥集中托管并支持轮换;后台敏感操作二次确认 + 审计留痕。
|
||
- **支付安全**:服务端票据二次校验;防重放(nonce + 唯一键);金额与商品以服务端配置为准。
|
||
- **内容保护**:签名 URL + 短时效 + 并发设备限制;第一阶段不上 DRM,但 URL 结构与接口要为第四阶段 DRM 预留。
|
||
- **数据分级**:手机号、邮箱、设备标识、支付标识分级存储与脱敏;日志与埋点禁止写入 PII 明文。
|
||
|
||
## 11. 上线与扩容路径
|
||
|
||
1. **环境**:dev / test / staging / prod 四套,配置外置,密钥不入库不入 Git。
|
||
2. **压测**:阶梯加压到 1 万并发播放(走真实 CDN 链路)+ 1,000 QPS 接口,记录水位与瓶颈。
|
||
3. **灰度**:先 5% 流量,观察起播、卡顿、错误率与支付成功率,再逐步放量。
|
||
4. **回滚**:镜像与配置双版本共存,一键回滚;数据库变更必须向后兼容(先加字段、再切读写、最后清理)。
|
||
5. **扩容触发线**:API 副本 CPU > 60% 或 QPS 超过设计值 70%;转码队列积压 > 30 分钟;CDN 命中率 < 95%。
|
||
6. **灾难恢复**:数据库 PITR + 每日全量备份(跨可用区),目标 RPO <= 15 分钟、RTO <= 2 小时,季度演练一次。
|
||
|
||
## 12. 与第一阶段工作包的对应关系
|
||
|
||
| 评估表模块 | 后端承担的部分 | 优先级 |
|
||
| --- | --- | --- |
|
||
| 01 基础与交付准备 | 服务骨架、OpenAPI 契约、ERD、CI/CD、环境与监控 | P0 |
|
||
| 02 用户账号与登录 | 用户域、OTP 与风控、会话与令牌、注销与导出 | P0 |
|
||
| 03 内容发现与详情 | 内容目录、搜索、分类榜单、可见性规则、缓存 | P0 |
|
||
| 04 播放器与播放控制 | **票据与签名 URL、转码与 ABR 片源、进度与续播接口、播放事件** | P0 |
|
||
| 05 记录、收藏与下载 | 进度存储与合并、收藏服务、下载授权与设备绑定 | P0 |
|
||
| 06 评论、点赞与分享 | 点赞与评论服务、内容治理联动、分享落地页接口 | P1 |
|
||
| 07 创作者中心 | 入驻与审核流、分片上传、转码流水线、媒资与状态服务 | P0 |
|
||
| 08 平台运营后台 | RBAC 与审计、内容/用户/订单管理、大盘数据接口 | P0 |
|
||
| 09 分集收费与支付 | 钱包与订单、票据校验、权益决策、退款与对账 | P0 |
|
||
| 10 质量、数据与上线 | 压测与容量报告、安全测试、埋点验收、发布与 DR Runbook | P0 |
|
||
|
||
建议的开发顺序(后端视角):
|
||
基础骨架与契约 → 账号与鉴权 → 内容目录与缓存 → 媒资上传与转码 → 票据与签名播放 →
|
||
进度与收藏 → 支付与权益 → 运营后台与治理 → 压测与上线准备。
|
||
|
||
其中"票据 + 签名 URL + 转码"是第一阶段风险最高、最不该压缩的链路。
|