Files
raisfast/docs/storage-system.md
T
2026-04-19 14:45:37 +08:00

5.5 KiB
Raw Blame History

分布式文件存储系统设计

需求分析

当前项目存储场景

场景 文件类型 频率 大小
博客封面图 jpg/png/webp 100KB-5MB
编辑器内嵌图片 jpg/png/gif 50KB-2MB
编辑器内嵌视频 mp4 10MB-500MB
附件下载 pdf/zip 1MB-50MB
电商 Extension 商品图 jpg/png 50KB-2MB
用户头像 jpg/png 50KB-500KB

核心诉求

单机部署 → 多机扩展,代码零改动

三种方案对比

方案 A:直接集成 RustFS(外部服务)

[Blog Server] → S3 API → [RustFS Cluster]
  • 单机也要跑 RustFS 容器,最小编译 40MB+ 二进制
  • 引入外部运维复杂度(单机博客跑个分布式存储,过度工程)
  • 调试链路长

方案 B:从零造轮子(自研分布式存储)

  • 工作量巨大(一致性协议、数据分片、副本、故障恢复)
  • 不现实,也没必要

方案 C:抽象存储层 + 可插拔后端(推荐)

                    ┌─────────────────┐
                    │  StorageService  │  ← 统一 API
                    │  (trait Storage) │
                    └────────┬────────┘
                             │
                ┌────────────┼────────────┐
                ▼            ▼            ▼
          LocalFS       RustFS/S3    <future>
         (单机)        (分布式)      OSS/R2

Phase 1 — 单机:文件存本地磁盘,零外部依赖,零运维

Phase 2 — 扩展:改一行配置切换到 RustFS/S3,代码零改动

推荐架构(方案 C 详细设计)

存储 Trait

// src/storage/mod.rs
#[async_trait]
pub trait Storage: Send + Sync {
    async fn put(&self, key: &str, data: &[u8], content_type: &str) -> AppResult<()>;
    async fn get(&self, key: &str) -> AppResult<Vec<u8>>;
    async fn delete(&self, key: &str) -> AppResult<()>;
    async fn url(&self, key: &str) -> AppResult<String>;       // 读取 URL
    async fn presigned_upload(&self, key: &str, ttl: Duration) -> AppResult<String>;
}

两个实现

LocalFS S3Storage
存储 {DATA_DIR}/uploads/ RustFS / MinIO / S3 / R2
URL /uploads/{bucket}/{key} (Axum 静态文件) presigned URL 或 CDN
配置 STORAGE_DRIVER=local STORAGE_DRIVER=s3
依赖 aws-sdk-s3 (feature gate)

上传流程

前端 ──POST /api/v1/upload──▶ Axum Handler
                                    │
                              StorageService.put()
                                    │
                              ┌─────┴──────┐
                              │  LocalFS   │  S3Storage
                              │  写磁盘     │  PUT object
                              └─────┬──────┘
                                    │
                              返回 { url: "/uploads/blog/xxx.jpg" }

文件组织

{bucket}/{year}/{month}/{uuid}.{ext}
  blog/    /2026/  /04/  /a1b2c3d4.jpg      ← 博客图片
  avatar/  /2026/  /04/  /e5f6g7h8.png      ← 头像
  product/ /2026/  /04/  /i9j0k1l2.webp     ← 电商商品图
  attachment/ ...                              ← 附件

Feature Flags

[features]
storage-local = []           # 默认
storage-s3 = ["aws-sdk-s3"]  # 分布式时启用

环境变量配置

# 通用
STORAGE_DRIVER=local          # local | s3

# S3 模式(RustFS / MinIO / AWS S3 / Cloudflare R2
S3_ENDPOINT=http://rustfs:9000
S3_ACCESS_KEY=xxx
S3_SECRET_KEY=xxx
S3_BUCKET=blog
S3_REGION=us-east-1

扩展路径

现在:  STORAGE_DRIVER=local    → 本地磁盘
       ↓
将来:  STORAGE_DRIVER=s3       → RustFS 单节点
       S3_ENDPOINT=http://rustfs:9000
       S3_ACCESS_KEY=xxx
       S3_SECRET_KEY=xxx
       S3_BUCKET=blog
       ↓
集群:  RustFS 多节点 + Nginx/CDN → 全球分发

实施计划

阶段 内容 工作量
P1 Storage trait + LocalFS 实现 + 上传/下载 API + 前端对接 1-2 天
P2 S3Storage 实现 + feature flag + presigned URL 1 天
P3 图片处理(缩略图、WebP 转换) 1 天
P4 docker-compose 加 RustFS + 生产配置 半天

外部存储引擎评估

RustFS

  • Rust 编写,与项目同语言
  • S3 兼容 API
  • Apache 2.0 许可,商业友好
  • 比 MinIO 快 2.3x4KB 小文件)
  • 26k+ GitHub Stars,社区活跃
  • Docker 一行部署
  • 支持分布式模式、Bitrot 保护、版本控制、桶复制
  • 部分功能(生命周期管理、分布式模式)仍在测试中

MinIO

  • Go 编写,成熟稳定
  • S3 兼容 API,生态最全
  • AGPL v3 许可,商用有法律风险
  • 生产级分布式部署经验丰富

Garage

  • Rust 编写,轻量级 S3 兼容
  • 去中心化架构,适合资源有限场景
  • 社区规模较小

托管服务

服务 特点
Cloudflare R2 S3 兼容,无出站流量费,自带 CDN
AWS S3 行业标准,生态最全
阿里云 OSS / 腾讯 COS 国内延迟低

结论

推荐 方案 C(抽象存储层)+ RustFS 作为 S3 后端

  1. 抽象层解耦,单机用 LocalFS 零依赖,集群切 S3 零改代码
  2. RustFS 技术栈统一(Rust)、许可证友好(Apache 2.0)、性能领先
  3. 分阶段实施,P1 即可投入使用