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

一、现状与问题

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

组件 常见实现 问题
消息队列 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. 渐进迁移保证可回滚,风险可控

相关推荐
Thneonl17 分钟前
同一资源、不同 ID:多源拓扑的 Identity Resolution
架构·rust
汤姆yu17 分钟前
基于Django的学习资料共享与智能推荐系统
后端·python·django·毕业设计·学习资料共享
Freak嵌入式18 分钟前
树莓派 Pico GPIO 深度解析:从 MCU 架构到寄存器控制,底层原理与实践指南
java·开发语言·科技·单片机·嵌入式硬件·架构
Jesse_EC21 分钟前
从 getStore() 到 execute() —— 单连接 IMAP 管理器的五次迭代记录
后端
摇滚侠21 分钟前
《SpringBoot 3:入门与应用实战》第 9 章 使用 WebMvc 开发应用 阅读笔记 20
spring boot·笔记·后端
AI多Agent协作实战派31 分钟前
AI多Agent协作系统实战(五十三):同一套系统,Linux沉默,Windows刷屏
后端
那咋乎吧36 分钟前
TCP状态机11个状态全梳理(以java netty 结合bio,nio, io多路复用模型为线索在linux上梳理)
后端
用户78136671144544 分钟前
RGW 对象多版本功能系统架构与代码解析
后端
Z思学44 分钟前
Nest 第一步 · 第 3 篇:理解 Controller / Service / Module 三层架构
后端