视频平台架构重构:从微服务到可插拔基础设施

一、现状与问题

典型的视频平台后端通常采用微服务架构,但普遍存在一个问题:业务代码与基础设施深度耦合。

组件 常见实现 问题
消息队列 RocketMQ / RabbitMQ(直接注入客户端) 换 MQ 必须改业务代码
服务调用 OpenFeign / Dubbo 语言锁定,只能 Java 调 Java
服务发现 Nacos / Eureka 弱设备无法运行
缓存 Redis(直接注入客户端) 换缓存要改业务代码

业务代码和基础设施强耦合,导致无法在弱设备部署、无法跨语言、无法切换实现。

1.1 代码质量问题

  • Service 类臃肿(部分超过 800 行),Controller 包含事务逻辑

  • DTO 与 Entity 混用

  • 异常处理不统一

  • 硬编码较多(Redis key、超时时间)

  • 测试覆盖不足

1.2 已有亮点:存储层已抽象

部分项目中存储层已经做了抽象:

text

复制代码
StorageService(接口)
 ├── MinioStorageService(MinIO 实现)
 ├── LocalStorageService(本地实现)

这说明抽象层思路已被验证可行,可以推广到其他基础设施组件。

二、重构思路:可插拔基础设施

2.1 设计目标

  1. 业务代码只依赖接口:不依赖具体实现

  2. 配置驱动:改配置就能切换实现

  3. 多实现:每个接口有轻量/企业级/测试多种实现

  4. 渐进迁移:一个模块一个模块切,可回滚

2.2 核心接口定义

ServiceDiscovery(服务发现)

go

复制代码
type ServiceDiscovery interface {
    Register(ctx context.Context, instance ServiceInstance) error
    Deregister(ctx context.Context) error
    Discover(ctx context.Context, serviceName string) ([]ServiceInstance, error)
    Watch(ctx context.Context, serviceName string) (<-chan []ServiceInstance, error)
}

实现:FileDiscovery(嵌入式单机)、EtcdDiscovery(K8s 标准)、NacosDiscovery(兼容现有)、MemoryDiscovery(单元测试)

MessageQueue(消息队列)

go

复制代码
type MessageQueue interface {
    Publish(ctx context.Context, topic string, msg Message) error
    Subscribe(ctx context.Context, topic string, group string) (<-chan Message, error)
    Ack(ctx context.Context, topic string, msg Message) error
}

实现:MemoryQueue(测试)、RedisStreamQueue(轻量)、RocketMQQueue(企业级)、NATSQueue(高性能)

CacheStore(缓存)

go

复制代码
type CacheStore interface {
    Get(ctx context.Context, key string) ([]byte, error)
    Set(ctx context.Context, key string, value []byte, ttl time.Duration) error
    Lock(ctx context.Context, key string, ttl time.Duration) (bool, error)
    Incr(ctx context.Context, key string, ttl time.Duration) (int64, error)
}

实现:MemoryCache(测试)、SQLiteCache(嵌入式持久化)、RedisCache(分布式)、BoltDBCache(嵌入式 KV)

ServiceCaller(服务间调用)

go

复制代码
type ServiceCaller interface {
    Call(ctx context.Context, target string, method string, req []byte) ([]byte, error)
    CallStream(ctx context.Context, target string, method string, req []byte) (<-chan []byte, error)
}

实现:MemoryCaller(测试)、HTTPClient(简单服务)、GRPCClient(跨语言标准)

2.3 配置驱动

yaml

复制代码
infrastructure:
  servicediscovery:
    type: etcd          # etcd / nacos / file / memory
    endpoints: ["http://localhost:2379"]
  messagequeue:
    type: redis-stream  # redis-stream / rocketmq / nats / memory
    redisAddr: localhost:6379
  cachestore:
    type: redis         # redis / sqlite / boltdb / memory
    addr: localhost:6379
  servicecaller:
    type: grpc          # grpc / http / memory
    timeout: 3s

切换环境只需改配置:

场景 配置组合
嵌入式设备 sqlite + memory + redis-stream
老旧服务器 sqlite + redis + file
云上生产环境 mysql + redis + etcd + rocketmq
单元测试 memory + memory + memory

三、无状态与高并发

3.1 无状态原则

服务不保存任何与请求绑定的状态,全部外置:

状态类型 存储位置
用户会话 Redis(JWT + 会话)
WebSocket 连接 Redis Pub/Sub(跨实例广播)
任务进度 Redis Stream
计数器 Redis
缓存 Redis
数据库 MySQL

3.2 WebSocket 跨实例广播

text

复制代码
客户端A ──WS──▶ realtime-1 ──┐
                              ├── Redis Pub/Sub ──▶ realtime-2 ──WS──▶ 客户端B
                              └──(弹幕/通知广播)

每个 realtime 实例订阅 Redis 主题,发消息只往 Redis Publish,所有实例收到后推给本地连接。实例增减不影响广播。

3.3 Redis Stream 作为消息队列

使用 Redis Stream 替代传统消息中间件,适合弱设备场景:

指标 Redis Stream RocketMQ
内存 复用 Redis 独立几百 MB
部署 已在跑 Redis 额外服务器
弱设备 可跑 不现实

核心用法:

  • XADD:入队

  • XREADGROUP:消费

  • XACK:确认

  • XCLAIM:认领超时任务

  • XTRIM:清理已 ACK 消息

四、API 规范

4.1 统一响应结构

json

复制代码
{
  "code": 0,
  "message": "ok",
  "data": {},
  "requestId": "req-xxxx"
}

4.2 错误码体系

全局错误码(0-999):

code 说明
0 成功
400 参数错误
401 未认证
403 无权限
404 资源不存在
429 限流
500 服务器错误

业务错误码(1000+):

模块 范围
用户 1000-1099
内容 1100-1199
互动 1200-1299
订单 1300-1399

4.3 分页与认证

  • 分页 :page(默认 1)、pageSize(默认 20,上限 100)

  • 认证 :Authorization: Bearer <jwt_token>

  • 幂等 :Idempotency-Key: <client_uuid>(写操作)

五、重构优先级

优先级 项 工作量 风险
P0 抽象 MessageQueue 中 高
P0 抽象 ServiceCaller 中 高
P0 抽象 ServiceDiscovery 低 中
P1 统一错误码/异常处理 低 低
P1 DTO/Entity 分层 中 低
P2 长类拆分 中 低
P2 硬编码配置化 低 低

策略: 先抽接口(改动集中在注入点),确认编译通过且功能不变后再逐个替换实现。每个服务独立迁移,先支持配置切换(保持原实现),再逐步替换。

六、总结

  1. 抽象层思路已被存储层验证可行,推广到 MessageQueue/ServiceDiscovery/CacheStore

  2. 配置驱动让同一套代码适应不同环境(嵌入式→企业级)

  3. 无状态设计让集群可水平扩展

  4. Redis 一鱼多吃:缓存 + MQ(Stream)+ 锁 + 计数器

  5. 渐进迁移保证可回滚,风险可控

相关推荐
程序猿追3 小时前
HarmonyOS 6 音视频实战:用 AVPlayer 做一个本地音乐播放器
华为·音视频·harmonyos
阿里云云原生4 小时前
云效工作项智能优化:先澄清,再开工
云原生
明月_清风4 小时前
面对陌生的 GitHub 项目无从下手?这 4 个网站帮你快速读懂源码
前端·后端·github
阿里云云原生4 小时前
阿里云日志服务 SLS 全新升级,打造 Agent 时代的智能数据引擎
云原生
bug菌4 小时前
🤔同事突然问我:Spring的注解 @Component 和 @Service 有何不同?
java·spring boot·后端
leobertlan4 小时前
痛苦系列 | DSP-02 频域切片:DTFT与DFT的探索
android·后端
Devlive 开源社区4 小时前
KnowForge 2026.0.8 发布:协作写作、团队空间、AI 朗读,这次更新有点大
架构
阿里云云原生4 小时前
从“治已病”到“治未病”:浩瀚能源与阿里云共建充电补能异地双活高可用体系实践
云原生
53488736abcdefg4 小时前
Hive 入门&架构原理
hive·hadoop·架构
加我攻城狮4 小时前
当大模型把「景区」定位到附近公共厕所:我的 AI 旅行小程序踩坑实录
后端·微信