Skip to content

MyBilibili 重构:职责划分与技术选型

🕒 Published at:

MyBilibili 重构:职责划分与技术选型 ​

1. 背景与动机 ​

1.1 当前架构 ​

当前 MyBilibili 微服务版本基于 Spring Cloud Alibaba 构建:

组件用途问题
Nacos服务发现 + 配置管理Java 实现,重量级,需要独立服务器
RocketMQ消息队列Java 实现,重量级,需要独立服务器
MySQL关系数据库需要独立服务器
Redis缓存需要独立服务器
MongoDB文档存储需要独立服务器
Elasticsearch搜索引擎Java 实现,重量级,需要独立服务器
Feign服务间调用Spring 生态绑定

1.2 问题 ​

  1. 重量级组件:Nacos、RocketMQ、ES 都是 Java 应用,内存占用大,不适合弱设备
  2. 生态锁定:Spring Cloud Alibaba 绑定 Java 生态,无法使用 Go 等轻量语言
  3. 资源浪费:为百万级并发设计的组件,用来处理几千几万的量级
  4. 部署限制:无法部署在 Android 盒子、老电脑等弱设备上

1.3 目标 ​

  1. 云原生标准:CNCF 项目优先,K8s 生态对齐
  2. 轻量化:所有组件都可以在弱设备上运行
  3. 可插拔:抽象基础设施层,支持多种实现
  4. 二进制化:Go 原生二进制 + Java GraalVM Native Image
  5. 双模式:支持嵌入式部署(盒子)和企业级部署(云服务器)

2. 服务职责划分 ​

2.1 设计原则 ​

  1. 按资源需求分:不同资源需求的服务分开部署
  2. 职责单一:每个服务只做一件事
  3. 语言适配:轻量服务用 Go,复杂业务用 Java
  4. 独立部署:每个服务可以独立启动、停止、扩容

2.2 服务列表(7个服务) ​

服务语言职责资源需求
gatewayTraefik(配置)路由、认证、限流、证书低 CPU、低内存
coreGo用户、关注、内容 CRUD、评论、点赞、收藏、分享低 CPU、低内存
realtimeGo弹幕、消息、通知(WebSocket)低 CPU、中内存、高网络
searchGo搜索、推荐、热榜、数据分析中 CPU、高内存
mediaJava视频转码、直播、AI 字幕(Whisper)、AI 总结(DeepSeek)高 CPU、高内存
adsGo广告投放、展示计费、点击计费低 CPU、低内存
storeJava商城、虚拟商品、订单、支付、退款、对账低 CPU、低内存

2.3 分类说明 ​

在线轻量服务(Go):

  • core、realtime、search、ads
  • 资源占用低,可以部署在 Android 盒子上
  • Go 原生二进制,启动快,内存小

网关(Traefik):

  • 不写代码,纯配置
  • Go 单二进制,~30MB 内存
  • K8s Gateway API 标准实现

业务复杂服务(Java):

  • store
  • 支付链路需要事务保证
  • Spring 生态对支付、安全支持更好
  • GraalVM 编译成二进制,不需要 JVM

批处理服务(Java):

  • media
  • 视频转码、AI 处理需要 CPU/GPU
  • FFmpeg/Whisper 用子进程调用
  • GraalVM 编译成二进制

搜索服务(Go):

  • search
  • 用 Bleve(Go 实现)替代 Elasticsearch
  • 内存占用从 500MB 降到 50MB

3. 技术选型 ​

3.1 网关:Traefik vs Spring Cloud Gateway ​

指标TraefikSpring Cloud Gateway
语言GoJava
内存占用~30MB~200MB
配置方式YAML 配置代码/配置
gRPC 支持原生(h2c)不原生
etcd 集成原生需插件
JWT 认证内置中间件需代码
自动证书Let's Encrypt 内置需 Nginx 配合
K8s 标准Gateway API 官方实现非标准

选择 Traefik:

  • K8s Gateway API 标准实现,55k stars
  • Go 单二进制,盒子跑得动
  • 配置即用,不写代码

3.2 消息队列:NATS vs RocketMQ ​

指标NATSRocketMQ
语言GoJava
内存占用~20MB几百 MB
吞吐量百万级/秒十万级/秒
延迟亚毫秒级毫秒级
持久化JetStream 落盘磁盘
CNCF 标准孵化项目非 CNCF
部署复杂度单文件 ~20MB需要独立服务器

选择 NATS:

  • CNCF 孵化项目,云原生标准
  • 盒子友好,20MB 内存
  • JetStream 持久化,积压不占内存

3.3 缓存:Redis ​

指标Redis
语言C
类型独立服务器
内存占用~100MB(老电脑,管够)
持久化RDB/AOF

选择策略:

  • 老电脑部署,内存管够
  • 只做缓存/锁/计数,不做 MQ
  • 盒子无状态,读远端 Redis

3.4 搜索引擎:Bleve vs Elasticsearch ​

指标BleveElasticsearch
语言GoJava
类型嵌入式独立服务器
内存占用几十 MB几百 MB
功能基础搜索全文搜索、聚合、分析
部署复杂度无(库)需要独立服务器

选择策略:

  • 盒子部署:Bleve(零依赖,嵌入 search 服务)
  • 企业级部署:Elasticsearch(功能更全,通过 SearchEngine 抽象切换)

3.5 文档存储:SQLite vs MongoDB ​

指标SQLiteMongoDB
类型嵌入式独立服务器
内存占用几 MB几百 MB
部署复杂度无(文件)需要独立服务器

选择策略:

  • 盒子本地缓存:SQLite(零依赖,文件型)
  • 主库:MySQL(老电脑)

3.6 服务间通信:gRPC vs Feign ​

指标gRPCFeign
协议HTTP/2HTTP/1.1
序列化ProtobufJSON
跨语言支持Java Only
性能高中
CNCF 标准毕业项目非 CNCF

选择 gRPC:

  • CNCF 毕业项目,云原生标准
  • Go 和 Java 都支持
  • 性能更高(Protobuf 序列化)
  • 跨语言通信标准

3.7 可观测性 ​

组件职责标准
Prometheus指标采集CNCF 毕业
Grafana可视化面板事实标准
Loki日志聚合CNCF 毕业
OpenTelemetry链路追踪CNCF 毕业

4. 抽象基础设施层 ​

4.1 设计原则 ​

  1. 接口定义:每个基础设施组件定义标准接口
  2. 多实现:每个接口有多个实现(嵌入式/企业级)
  3. 配置驱动:通过配置文件选择实现
  4. 运行时切换:改配置就能切换实现,不用改代码

4.2 接口定义 ​

接口职责嵌入式实现企业级实现
ServiceDiscovery服务注册/发现文件配置etcd / Nacos / Consul
MessageQueue消息发布/订阅NATS / 内存Kafka / RocketMQ
CacheStore缓存读写本地内存Redis
SearchEngine索引/搜索Bleve / SQLite FTSElasticsearch
DocumentStore文档存储SQLiteMongoDB
StorageService文件上传/下载本地文件系统MinIO / S3 / OSS

4.3 配置示例 ​

yaml
infrastructure:
  servicediscovery:
    type: etcd      # etcd / nacos / file
    endpoint: http://localhost:2379
  messagequeue:
    type: nats      # nats / kafka / rocketmq / memory
    url: nats://localhost:4222
  cachestore:
    type: redis     # redis / memory
    endpoint: localhost:6379
  searchengine:
    type: bleve     # bleve / elasticsearch
    indexpath: /var/data/search.bleve
  documentstore:
    type: sqlite    # sqlite / mongodb
    path: /var/data/app.db
  storageservice:
    type: minio     # local / minio / oss
    endpoint: http://localhost:9000

5. 二进制化策略 ​

5.1 Go 服务 ​

  • 天然编译成原生二进制
  • 交叉编译:GOOS=linux GOARCH=arm64 go build
  • 无运行时依赖,单文件部署

5.2 Java 服务 ​

  • 使用 GraalVM Native Image 编译
  • 启动时间:毫秒级(vs JVM 秒级)
  • 内存占用:几十 MB(vs JVM 几百 MB)
  • 无 JVM 依赖,单文件部署

5.3 FFmpeg/Whisper ​

  • 使用子进程调用(ProcessBuilder / exec.Command)
  • 不使用 JNI,避免反射配置问题
  • FFmpeg/Whisper 本身是原生二进制
  • Java/Go 只是"指挥"它们工作

6. 部署策略 ​

6.1 按设备能力分配 ​

设备类型适合的服务原因
Android 盒子(1GB RAM)Traefik + core + realtime + ads轻量,无状态,可扩缩
老电脑(2-4GB RAM)MySQL + Redis + etcd + NATS有状态基础设施
主力机media + store + MinIO需要 CPU 算力和存储

6.2 集群管理 ​

  • 老电脑集中管理数据,盒子只跑业务
  • 业务服务无状态,任意盒子可扩缩
  • 支付/订单强一致链路走 MySQL 事务
  • 网关多副本(Traefik)高可用

6.3 扩容策略 ​

场景方案
流量增大加 Traefik 实例 / 加 core 实例
转码慢加 media worker,NATS 队列分发
搜索慢加 search 实例,Bleve 分片
WebSocket 连接多加 realtime 实例,NATS 广播

7. 最终架构 ​

用户请求 → 公网
    ↓
Traefik 网关(盒子1-2,多副本高可用)
    ├── JWT 认证(内置中间件)
    ├── 限流(内置中间件)
    ├── 自动证书(Let's Encrypt)
    └── gRPC 路由转发
            ↓
    ┌───────┼───────┐
    ▼       ▼       ▼
  core   realtime  ads
  (Go)   (Go)      (Go)
    │       │       │
    └───┬───┘       │
        │           │
        ▼           ▼
    ┌───────┐   ┌───────┐
    │ 老电脑 │   │ 主力机 │
    │ MySQL  │   │ media │
    │ Redis  │   │ store │
    │ etcd   │   │ MinIO │
    │ NATS   │   │       │
    └───────┘   └───────┘

8. 技术栈汇总 ​

层组件标准部署位置
网关TraefikK8s Gateway API 实现盒子
服务发现etcdK8s 底层老电脑
RPCgRPCCNCF 毕业服务间
消息队列NATSCNCF 孵化老电脑
缓存Redis事实标准老电脑
主库MySQL事实标准老电脑
本地库SQLite嵌入式盒子
搜索BleveGo 嵌入式search 服务内
存储MinIOS3 兼容主力机
监控PrometheusCNCF 毕业老电脑/主力机
日志LokiCNCF 毕业老电脑/主力机
追踪OpenTelemetryCNCF 毕业全链路

9. 总结 ​

  1. CNCF 标准优先:etcd / gRPC / NATS / Prometheus / Loki / OpenTelemetry
  2. K8s 生态对齐:Traefik(Gateway API)
  3. 数据集中:MySQL + Redis + etcd 放老电脑,好管
  4. 业务无状态:盒子随便扩缩,挂了换一个
  5. 抽象接口:所有组件可切换,不锁死
  6. 二进制化:Go 原生 + Java GraalVM
  7. 双模式部署:盒子(轻量)和云(企业级)都能跑