Skip to content

MyBilibili 技术选型最终决定

🕒 Published at:

MyBilibili 技术选型最终决定 ​

1. 选型原则 ​

  1. 弱设备可跑:所有组件内存占用低,盒子/手机能运行
  2. CNCF 标准:优先采用 CNCF 毕业/孵化项目
  3. 云原生标准:采用 K8s 生态验证过的组件
  4. 跨语言:Go + Java 都能接入
  5. 可插拔:每个组件可通过配置切换实现

2. 组件选型表 ​

组件决定替代掉原因
服务发现/配置etcdNacosK8s 底层就是 etcd,Raft 强一致,Go 实现
消息队列NATSRocketMQCNCF 孵化项目,Go 单二进制 ~20MB,JetStream 持久化
缓存Redis-保留,部署在老电脑,内存管够
主数据库MySQL-保留,部署在老电脑,支付强一致
本地缓存SQLiteMongoDB盒子本地,零依赖,文件型
服务调用gRPCFeignCNCF 毕业项目,HTTP/2 + Protobuf,跨语言
网关TraefikSpring Cloud GatewayK8s Gateway API 官方实现,Go 单二进制 ~30MB
对象存储MinIO自建文件标准 S3 协议,可迁移云 OSS
搜索BleveElasticsearchGo 嵌入式,盒子用 Bleve;电脑可用 ES(抽象兜底)
任务调度NATS JetStreamXXL-Job复用 MQ 消费组
监控Prometheus + Grafana-CNCF 毕业项目,标准可观测性
日志LokiELKCNCF 毕业项目,轻量,与 Prometheus 同一套标签
链路追踪OpenTelemetry-CNCF 毕业项目,标准遥测 API

3. 各组件理由详述 ​

3.1 etcd(服务发现/配置) ​

为什么:

  • K8s/云原生事实标准,文档、生态、工具链成熟
  • Go 实现,单二进制,几十 MB 内存
  • Raft 协议,强一致,Leader 选举
  • 天然支持 watch(配置热更新、服务变更通知)
  • 支持租约(服务心跳 + 自动清理死实例)

部署: 老电脑单节点即可;需要高可用时 3 节点

3.2 NATS(消息队列) ​

为什么不用 RocketMQ:

  • RocketMQ 需独立几百 MB 内存 + 专用服务器
  • Java 应用,盒子跑不动

为什么不用 Redis Stream:

  • Redis 内存有限,队列积压会挤占缓存空间
  • 盒子 1GB RAM,Redis 既要缓存又要 MQ,两头不到岸

为什么用 NATS:

  • CNCF 孵化项目,云原生标准
  • Go 单二进制 ~20MB,盒子跑得动
  • JetStream 持久化落盘,积压不占内存
  • Queue Group 天然负载均衡
  • 延迟亚毫秒级

部署: 老电脑单节点

3.3 Traefik(网关) ​

为什么不用自写网关:

  • 自写网关需要维护路由/JWT/限流/服务发现等逻辑,成本高

为什么用 Traefik:

  • K8s Gateway API 官方实现(55k stars,CNCF 项目)
  • Go 单二进制 ~30MB,盒子跑得动
  • 原生支持 gRPC 路由(h2c 协议)
  • 直接对接 etcd 做服务发现
  • 内置 JWT 认证、限流、熔断中间件,配置即用
  • 自动 Let's Encrypt 证书管理

部署: 盒子1-2 做入口,多副本高可用

3.4 MySQL + SQLite 双库 ​

MySQL(主库 - 老电脑):

  • 存储:用户、内容、订单、支付、对账
  • 事务保障(支付/库存)
  • 所有盒子通过内网读写

SQLite(本地缓存 - 盒子):

  • 每台盒子本地文件
  • 存储:本地浏览历史、设置、断点缓存、离线数据
  • 网络断开时兜底,恢复后同步

3.5 gRPC(服务调用) ​

为什么不用 Feign:

  • Feign 绑定 Spring/Java,Go 服务无法调用
  • HTTP/1.1 + JSON,性能一般

为什么用 gRPC:

  • HTTP/2 多路复用,Protobuf 二进制序列化,性能高
  • Go/Java 官方支持
  • 自动生成客户端/服务端代码
  • 流式调用(SSE 替代、大文件传输)

实现细节:

  • 服务名 → 地址:对接 etcd
  • 负载均衡:round-robin
  • 超时重试:可配置

3.6 MinIO(对象存储) ​

为什么:

  • S3 兼容 API,未来可无缝切云 OSS/S3
  • 单二进制,几十 MB 内存
  • 自带 Web 管理台
  • 现有代码已有 MinioStorageService 实现

3.7 Bleve(搜索) ​

为什么不用 ES:

  • ES 是 Java 应用,500MB+ 内存,盒子跑不动

为什么用 Bleve:

  • 零部署,库级别集成进 search 服务
  • 支持中文分词
  • 内存占用几十 MB

备选: 企业级部署时可切 ES(SearchEngine 抽象)

3.8 可观测性(Prometheus + Loki + OpenTelemetry) ​

组件职责说明
Prometheus指标采集CNCF 毕业,监控 CPU/内存/QPS/延迟
Grafana可视化面板设备总览、服务健康、请求量
Loki日志聚合CNCF 毕业,轻量,与 Prometheus 同一套标签
OpenTelemetry链路追踪CNCF 毕业,标准遥测 API,跨服务追踪

部署: 老电脑/主力机跑 Prometheus + Loki,盒子只暴露 metrics 端点

4. 语言选型 ​

4.1 服务语言分配 ​

服务语言原因
gatewayTraefik(配置即用)不写代码,纯配置
coreGo大量 CRUD + 缓存,Go 足够
realtimeGoWebSocket 高并发,goroutine 天然适配
searchGoBleve 是 Go 库,天然集成
adsGo简单计数/计费,Go 足够
mediaJava (GraalVM)FFmpeg/Whisper 子进程 + AI 集成,Java 生态
storeJava (GraalVM)支付事务、Spring 生态成熟

4.2 为什么混合语言 ​

语言优势劣势
Go单二进制、内存小、并发强、编译快生态不如 Java 成熟(支付/安全库)
Java生态全(支付/安全/AI)、成熟启动慢、内存大(GraalVM 缓解)

策略: 在线轻量服务用 Go(跑盒子),复杂业务用 Java GraalVM(跑电脑)。

4.3 GraalVM 注意事项 ​

  • Lombok 反射需要 reflect-config.json
  • 动态代理需要 proxy-config.json
  • FFmpeg/Whisper 用子进程调用,避免 JNI 反射配置
  • Spring Boot 3 + GraalVM 支持良好(AOT)

5. 中间件汇总(最终清单) ​

组件版本/类型部署位置内存
etcdv3.x老电脑~50MB
Redis7.x老电脑~100MB
MySQL8.x老电脑~200MB
NATS2.x老电脑~20MB
MinIOlatest主力机~100MB
SQLite嵌入式每台设备0(文件)
BleveGo 库search 服务内~50MB
SRSv6主力机/盒子~50MB
Traefikv3盒子入口~30MB
Prometheuslatest老电脑/主力机~100MB
Lokilatest老电脑/主力机~50MB

全部基础设施内存合计:老电脑 ~520MB,盒子 ~230MB(含业务),1GB 盒子无压力。

6. 对比总结(旧 vs 新) ​

维度旧(Spring Cloud Alibaba)新(云原生标准)
服务发现Nacos(几百 MB)etcd(几十 MB)
消息队列RocketMQ(几百 MB)NATS(几十 MB,CNCF 孵化)
搜索ES(500MB+)Bleve(几十 MB,可切 ES)
文档库MongoDB(几百 MB)SQLite(0)
服务调用Feign(Java only)gRPC(跨语言,CNCF 毕业)
网关Spring Cloud GatewayTraefik(K8s Gateway API 实现)
语言Java 全栈Go + Java GraalVM
内存合计2GB+老电脑 ~520MB,盒子 ~230MB
弱设备不可行可行
云迁移绑定阿里云标准云原生,可上 K8s

7. 结论 ​

  1. 核心思路:CNCF 标准优先,Nacos→etcd,RocketMQ→NATS,Feign→gRPC,SC Gateway→Traefik
  2. Redis 只做缓存:不再当 MQ 用,避免两头不到岸
  3. Go 优先:在线服务全 Go,Java 只留给需要生态的 media/store
  4. 抽象兜底:所有组件通过抽象层(03 文档)可切换,不锁死
  5. 可观测性标配:Prometheus + Loki + OpenTelemetry
  6. 可上云:架构符合 K8s 标准,未来迁云只换配置