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

193 lines
5.5 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 分布式文件存储系统设计
## 需求分析
### 当前项目存储场景
| 场景 | 文件类型 | 频率 | 大小 |
|---|---|---|---|
| 博客封面图 | 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
```rust
// 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
```toml
[features]
storage-local = [] # 默认
storage-s3 = ["aws-sdk-s3"] # 分布式时启用
```
### 环境变量配置
```bash
# 通用
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 即可投入使用