# cloud-disk

<p align="center">
  <a href="https://github.com/<org>/cloud-disk/blob/main/LICENSE">
    <img src="https://img.shields.io/badge/License-MIT-E11311.svg" alt="MIT License">
  </a>
  <a href="#">
    <img src="https://img.shields.io/badge/Java-21-brightgreen" alt="Java 21">
  </a>
  <a href="#">
    <img src="https://img.shields.io/badge/Spring_Boot-3.3.7-brightgreen" alt="Spring Boot 3.3.7">
  </a>
  <a href="#">
    <img src="https://img.shields.io/badge/Spring_Cloud-2023.0.3-blue" alt="Spring Cloud 2023.0.3">
  </a>
  <br/>
  <a href="./README.md"><img alt="简体中文" src="https://img.shields.io/badge/简体中文-d9d9d9"></a>
  <a href="./README_EN.md"><img alt="README in English" src="https://img.shields.io/badge/English-d9d9d9"></a>
</p>

---

**S3 兼容对象存储服务**，基于 Spring Cloud Alibaba 微服务架构。支持多存储后端（MinIO / 本地 / 阿里云 OSS）、秒传、分片上传、回收站，Gateway 全链路非阻塞，9 项 JMeter 基准测试验证。

---

## ✨ 核心特性

- **[S3 API 兼容](docs/deployment.md#6-完整-api-接口文档)** — PUT/GET/DELETE Bucket & Object、Multipart Upload、ListObjectsV2、CopyObject，与 aws-cli、s3cmd 等工具直接集成
- **[多存储后端可插拔](docs/architecture.md#51-为什么使用标准-s3-协议而非-s3-express)** — MinIO / 本地文件系统 / 阿里云 OSS，每个 Bucket 独立选择存储类型，新增后端只需实现 `StorageBackend` 接口
- **[秒传 (Instant Upload)](docs/architecture.md#22-秒传instant-upload流程)** — 基于 MD5 + 文件大小联合去重，Redis O(1) 判定，1GB 文件重复上传仅需 ~15ms，加速比 4,666×
- **[分片上传](docs/architecture.md#21-分片上传全流程)** — UUID v7 上传 ID（时间有序，避免 B+ 树叶分裂），Redis 维护分片状态，MinIO 服务端 composeObject 合并
- **[软删除 + 回收站](docs/architecture.md#53-为什么使用软删除)** — `NORMAL → RECYCLED（30天）→ DELETED` 状态机，XXL-Job 每天凌晨 3 点自动清理过期对象
- **[响应式全链路非阻塞](docs/architecture.md#57-gateway-全链路非阻塞)** — Spring Cloud Gateway + `RedissonReactiveClient`，避免 Netty event loop 被 Redis 同步操作阻塞，尖峰 2,000 并发 0 错误
- **[流控熔断](docs/nacos-config.md)** — Sentinel Gateway 流控 + Nacos 动态数据源，规则热更新无需重启
- **[Outbox 模式](docs/architecture.md#54-为什么使用-outbox-模式)** — 数据面 → 控制面事件的至少一次投递，XXL-Job 定时重试，消费者天然幂等
- **[Redis Cluster 热点打散](docs/architecture.md#58-hotkeyutil-热点打散)** — `HotKeyUtil` 3-shard hash-tag 分布，读写均分到 3 个 master 节点，偏差 < 20%
- **[数据库分片](docs/architecture.md#3-数据库设计)** — ShardingSphere 5.5 读写分离 + `object_info` 按 `bucket_id % 2` 分片

---

## 🏗 架构

```
                            ┌──────────────────────────────────────────┐
                            │       Spring Cloud Gateway (38080)       │
                            │    Nacos 服务发现 + Sentinel 流控 + JWT 鉴权  │
                            └──────┬────────────────────┬─────────────┘
                                   │                    │
                        ┌──────────▼──────────┐ ┌───────▼──────────┐
                        │   object-storage    │ │  object-metadata │
                        │      (38081)        │ │     (38082)      │
                        │    数据面 / IO 密集型  │ │   控制面 / CPU 密集型 │
                        │  ┌───────────────┐  │ │  ┌────────────┐  │
                        │  │StorageBackend │  │ │  │   MySQL    │  │
                        │  │ MinIO/本地/OSS │  │ │  │  (元数据)    │  │
                        │  └───────────────┘  │ │  └────────────┘  │
                        │  ┌───────────────┐  │ │  ┌────────────┐  │
                        │  │Redis Cluster  │  │ │  │   Redis    │  │
                        │  │(秒传/分片状态)  │  │ │  │  Cluster   │  │
                        │  └───────────────┘  │ │  │  (缓存)     │  │
                        │  ┌───────────────┐  │ │  └────────────┘  │
                        │  │  RabbitMQ     │──┼─┼──▶ 消费者       │
                        │  │  (Outbox)     │  │ │                  │
                        │  └───────────────┘  │ │                  │
                        └─────────────────────┘ └──────────────────┘

 基础设施: Nacos(38848)  Sentinel(38858)  MySQL(33306/33307)
           Redis Cluster(37000-37005)  RabbitMQ(35672)  MinIO(39000/39001)  XXL-Job(39080)
```

| 模块 | 角色 | 核心技术 |
|------|------|----------|
| `gateway` | API 网关 | Spring Cloud Gateway + JWT + Sentinel + RedissonReactiveClient |
| `object-storage` | 数据面 | StorageBackend (MinIO/Local/OSS) + Redis + RabbitMQ Outbox |
| `object-metadata` | 控制面 | MySQL + MyBatis-Plus + Redis Cache + RabbitMQ Consumer |
| `admin-ui` | 管理后台 | React 19 + Vite + shadcn/ui + i18n |
| `common` | 公共库 | DTO、S3ErrorCode 异常枚举、StorageBackend 接口、HotKeyUtil |

## 🖥 界面预览

| 我的文件 | Bucket 文件浏览 |
|---------|---------------|
| ![我的文件](img/我的文件.png) | ![test-bucket](img/test-bucket.png) |

---

## 🚀 快速开始

### 环境要求

- Docker & Docker Compose (2.20+)
- Java 21 & Maven 3.9+（仅本地编译需要，运行时由容器提供）

### 1. 克隆仓库

```bash
git clone https://github.com/<org>/cloud-disk.git
cd cloud-disk
```

### 2. 编译

```bash
echo "JAVA_HOME=/path/to/jdk-21" > .env
make package-fast
```

### 3. 启动全部服务

```bash
docker compose up -d
```

首次启动约 2-3 分钟，等待所有容器 healthy：

```bash
docker compose ps
```

### 4. 验证

```bash
curl -s -w "\nHTTP:%{http_code}" -X POST http://localhost:38080/login \
  -H "Content-Type: application/json" \
  -d '{"username":"admin","password":"123456"}'
```

### 管理控制台

| 控制台 | 地址 | 默认凭证 |
|--------|------|----------|
| Nacos 注册中心 | http://localhost:38848/nacos | `nacos` / `nacos` |
| MinIO 对象存储 | http://localhost:39001 | `minioadmin` / `minioadmin` |
| RabbitMQ 消息队列 | http://localhost:45672 | `admin` / `admin123` |
| Sentinel 流控 | http://localhost:38858 | `sentinel` / `sentinel` |
| XXL-Job 定时任务 | http://localhost:39080/xxl-job-admin | `admin` / `123456` |
| Admin UI | http://localhost:5173 | `admin` / `123456` |

---

## 📖 API 示例

所有请求通过 Gateway (`http://localhost:38080`) 转发。完整 API 文档：[docs/deployment.md](docs/deployment.md#6-完整-api-接口文档)

```bash
TOKEN="<从 /login 获取>"

# 创建 Bucket
curl -X PUT http://localhost:38080/my-bucket \
  -H "Authorization: Bearer $TOKEN"

# 上传对象
curl -X PUT http://localhost:38080/my-bucket/hello.txt \
  -H "Authorization: Bearer $TOKEN" \
  -H "Content-Type: text/plain" \
  -d "Hello World!"

# 秒传（携带 MD5，命中则跳过实际上传）
MD5=$(md5sum large-file.bin | cut -d' ' -f1)
curl -X PUT http://localhost:38080/my-bucket/large-file.bin \
  -H "Authorization: Bearer $TOKEN" \
  -H "x-cloud-disk-md5: $MD5" \
  -T large-file.bin
# 秒传命中时返回: { "data": { "instant": true } }

# 下载对象（默认 302 跳转至预签名 URL，-L 跟随重定向）
curl -L -X GET http://localhost:38080/my-bucket/hello.txt \
  -H "Authorization: Bearer $TOKEN" -o hello.txt

# 强制代理模式（数据流经 Gateway）
curl -X GET "http://localhost:38080/my-bucket/hello.txt?proxy=true" \
  -H "Authorization: Bearer $TOKEN" -o hello.txt

# 列举对象 (S3 ListObjectsV2)
curl "http://localhost:38080/my-bucket?list-type=2&prefix=images/" \
  -H "Authorization: Bearer $TOKEN"
```

---

## 📋 S3 API 兼容性

| 功能 | 支持 |
|------|:----:|
| PUT / DELETE / HEAD Bucket | Y |
| GET Bucket (ListObjectsV2) | Y |
| PUT / GET / HEAD / DELETE Object | Y |
| Delete Multiple Objects | Y |
| CopyObject | Y |
| Multipart Upload (Init / Part / Complete / Abort) | Y |
| List Parts | Y |
| Pre-signed URL | Y |
| Bucket Policy / Object Lock / Versioning | N |

---

## 📚 文档

| 文档 | 内容 |
|------|------|
| [docs/architecture.md](docs/architecture.md) | 架构设计、数据流、数据库设计、关键设计决策 |
| [docs/deployment.md](docs/deployment.md) | 部署手册、配置参考、完整 API 文档、故障排查 |
| [docs/development.md](docs/development.md) | 开发环境、项目结构、编码规范、测试指南 |
| [docs/nacos-config.md](docs/nacos-config.md) | Nacos 动态配置 Sentinel 流控规则 |
| [benchmark/README.md](benchmark/README.md) | JMeter 基准测试套件（9 项测试，尖峰 2,000 并发 0 错误） |

---

## 📄 许可证

[MIT](LICENSE)
