跳到主内容

PRJ-02 LIVE

Vistack Vistack 视频平台

distributed live-streaming & vod platform

直播 + 点播一体化的分布式云原生视频平台

  • Go
  • 分布式
  • 音视频
  • 云原生
YEAR
2025–2026
ROLE
独立开发 · 架构设计
STATUS
LIVE
TIER
旗舰项目

4

个可独立扩容的服务角色(api / worker / transcoder / auth)

240p–4K

FFmpeg 按源分辨率智能选择的 DASH 码率档位

3 件套

缓存穿透 / 击穿 / 雪崩防护全部落地(布隆 + singleflight + 随机 TTL)

1 条命令

docker compose up 起全栈(Traefik + PG + Redis + MinIO + Kafka + etcd)

// 01

问题背景

做这个项目的目标是把「视频平台」这个经典高并发场景真正做成分布式形态,而不是单体能跑的玩具:上传、转码、分发、互动,每个环节都要能独立扩容、能讲清楚取舍。

点播侧的核心矛盾是转码——CPU 密集、耗时长、不能阻塞 API;直播侧需要一个轻量高并发的 WebRTC 分发节点。同时把面试高频的「缓存三件套、限流、计数与榜单」做成真实接入业务的通用组件,而非 Demo 级代码。

// 02

系统架构

接入层

统一入口与协议分发

Traefik 网关(/api/v1 分流 auth 与 api,/vistack 直通 MinIO 媒体)live777 SFU(Rust WebRTC 直播分发,OBS 推流 → 多端拉流)Vue3 用户端 / web-admin 管理端(dash.js 自适应播放)

应用服务层

单二进制 + VISTACK_ROLE 角色分发,四进程独立部署扩容

api(Gin HTTP:分片上传、预签名、业务接口)worker(Kafka 消费者:转码编排、事务写库)transcoder(gRPC ProcessVideo:FFmpeg 转码)auth(RS256 签发 + JWKS + gRPC 用户查询)

中间件与队列层

解耦、重试与服务发现

Kafka 多 Topic(transcode / delete_file / danmaku / comment)Redis ZSet 指数退避重试 + Watchdog 超时兜底etcd 注册保活 + gRPC round_robin 负载均衡etcd 领导选举(单例任务防重)

数据与存储层

元数据、缓存与对象存储

PostgreSQL(GORM,migrations 自动迁移)Redis Cache-Aside 通用组件(空值缓存 + 布隆过滤器 + 互斥锁 + 随机 TTL)MinIO(Multipart 直传 + 文件哈希秒传 + 预签名防盗链)

// 03

关键技术决策

每一条都包含「为什么这样选」与「代价是什么」——这是我理解这个项目的方式。

Kafka 解耦转码任务,Redis ZSet 做退避重试

转码是分钟级的重任务,绝不能在 HTTP 请求里同步执行。选择 Kafka 而非 Redis Stream,是因为转码、删除、弹幕落库共用一套队列语义,Kafka 的消费组与分区模型更贴合水平扩容。失败任务不直接丢弃,而是进 Redis ZSet 按指数退避重投,另有 Watchdog 兜底「processing 超时」的僵尸任务。代价是要自己保证重投幂等——worker 写库全部包在事务里,用启动与重试路径的确定性换运行期故障的可恢复。

FFmpeg 隔离为独立 transcoder,gRPC + etcd 服务发现

FFmpeg 转码吃 CPU 且依赖宿主环境,放在 api 进程里会拖累接口稳定性、一次崩溃就是整个服务不可用。拆成独立容器后天然无状态(输入输出都走 MinIO),可 docker compose --scale 任意副本。实例向 etcd 注册保活,worker 通过自研 etcd→gRPC resolver 动态发现并 round_robin 负载均衡;同时保留静态地址兜底配置,etcd 不可用时转码链路不至于全瘫。

单二进制多角色,而非四个仓库

api / worker / transcoder / auth 共享同一套配置、模型与基础设施封装,拆成四个仓库会造成依赖地狱。最终用 cmd/vistack 一个入口按 VISTACK_ROLE 分发角色,internal/role 做启动引导。代价是二进制体积略大、任一模块改动触发整体发版,但对独立开发者来说部署与版本一致性的收益远大于成本。

独立 Auth 服务:RS256 + JWKS,api 本地验签

认证拆成独立服务(HTTP :8081 + gRPC :50052),api 通过 JWKS 拉公钥本地验签,鉴权路径零网络跳转;用户详情这类必须查库的请求才走 gRPC 用户查询。对称密钥 HS256 更简单,但无法把验签能力安全地下发给多个服务;RS256 的代价是要管理 kid 与 JWKS 缓存。

互动计数写 Redis,事件队列异步批量落库

点赞 / 收藏 / 播放量直接打库会把热点行锁死。改为 Redis Set 去重 + INCR 计数、Lua 保证 toggle 原子性,变更事件进队列异步批量幂等落库;videos 表冗余计数列 + 明细表双写,热门榜单直接用 ZSet。代价是缓存与 DB 存在秒级不一致,用「落库间隔 / 批量大小」配置在实时性与写压力之间调平。

// 04

我的职责与产出

RESPONSIBILITIES

  • 设计并实现四角色进程拆分与单二进制角色分发机制
  • 实现 Kafka 转码任务队列、Redis ZSet 退避重试与 Watchdog 超时兜底
  • 实现 etcd 服务注册 / 发现与 gRPC resolver 负载均衡
  • 实现 DASH 自适应码率转码流水线(ffprobe 探测 → 档位选择 → 分片 → MinIO 回传)
  • 实现 Redis 缓存三件套通用组件与分布式限流中间件(令牌桶 + Lua 滑动窗口)
  • 设计 RBAC 权限模型与独立 Auth 服务(RS256 / JWKS / gRPC)

OUTCOMES

  • 转码能力与 API 完全解耦,transcoder 可用 docker compose --scale 水平扩容
  • 上传支持分片 + 秒传 + 断点续传,DASH 240p–4K 自适应播放落地
  • 缓存穿透 / 击穿 / 雪崩防护封装为通用组件,接入视频详情与推荐列表
  • 一键 Compose 起全栈(含 Traefik / PG / Redis / MinIO / Kafka / etcd),附 Kubernetes 清单
  • 用户端与管理端产物可一键发布至 Cloudflare Pages / S3(deploy/cdn-publish.sh)

// 05

踩坑与复盘

最大的坑在分布式任务的一致性:Kafka 消费成功但写库失败、gRPC 转码超时但任务实际仍在跑,这类「中间态」逼着我把重试、幂等、超时兜底拆成三层各自负责。下一步计划接入 Prometheus / Grafana 补齐可观测性(目前只有 zap 结构化日志预留),并把 HLS 与 DASH 双协议输出补齐。

// 06

技术栈

  • Go 1.26
  • Gin
  • gRPC
  • etcd
  • Kafka
  • MinIO
  • FFmpeg

视觉风格