Skip to content

Latest commit

 

History

1 Commit

Folders and files

NameName
Last commit message
Last commit date
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

多租户对象存储服务

一个面向 SaaS 和内部平台的轻量对象存储服务,提供对象版本、内容寻址去重、租户配额、分片上传、预签名下载、软删除、审计和异步垃圾回收。

客户端
  |
  v
对象存储 API
  |-- API Key 鉴权
  |-- 租户配额
  |-- 对象版本
  |-- 分片上传
  |-- ETag / 预签名下载
  |
  +--> SQLite WAL:元数据、引用计数、上传状态、回收任务
  |
  +--> Blob 文件系统:SHA-256 内容寻址
                              |
                              v
                         GC Worker

数据库保存对象元数据,文件系统保存内容。相同内容使用同一个 SHA-256 Blob,覆盖和删除只更新引用关系,不在请求线程中直接做危险的物理删除。

核心能力

  • 多租户 API Key 鉴权和对象隔离
  • 对象上传、下载、列表和软删除
  • 对象版本历史,覆盖写入不会丢失旧版本
  • SHA-256 内容寻址和 Blob 去重
  • 租户配额校验,超过配额时事务回滚
  • ETag 条件下载,内容未变化时返回 304
  • HMAC 预签名下载令牌和过期校验
  • 分片上传、分片覆盖和完整性检查
  • SQLite WAL 元数据持久化
  • 无引用 Blob 的异步垃圾回收 Worker
  • 租户级固定窗口限流、审计日志和 Prometheus 指标
  • Docker Compose 分离 API 和 GC Worker

环境要求

  • Node.js 24+
  • 或 Docker / Docker Compose

项目使用 Node.js 内置 HTTP、SQLite、Crypto 和文件系统模块,无第三方运行时依赖。

本地运行

npm test
npm run test:load
npm start

另开终端启动垃圾回收 Worker:

npm run worker

服务默认监听 http://localhost:8800

快速使用

1. 创建租户

curl.exe -X POST http://localhost:8800/admin/tenants `
  -H "Authorization: Bearer local-admin-token" `
  -H "Content-Type: application/json" `
  -d '{"name":"内容平台","quotaBytes":104857600}'

响应中的 apiKey 只展示一次,应当安全保存。

2. 上传对象

curl.exe -X PUT http://localhost:8800/v1/objects/media/logo.txt `
  -H "X-API-Key: obj_xxx" `
  -H "Content-Type: text/plain" `
  --data-binary "hello object storage"

响应包含 etag 和对象 version

3. 下载对象

curl.exe http://localhost:8800/v1/objects/media/logo.txt `
  -H "X-API-Key: obj_xxx"

使用上次响应的 ETag:

curl.exe http://localhost:8800/v1/objects/media/logo.txt `
  -H "X-API-Key: obj_xxx" `
  -H 'If-None-Match: "sha256..."'

对象未变化时返回 304 Not Modified

4. 生成预签名下载

curl.exe -X POST http://localhost:8800/v1/presigned-downloads `
  -H "X-API-Key: obj_xxx" `
  -H "Content-Type: application/json" `
  -d '{"bucket":"media","objectKey":"logo.txt","expiresIn":300}'

调用 /download?token=... 时不需要 API Key,但令牌包含租户、对象版本和过期时间,并使用 HMAC 校验。

5. 分片上传

curl.exe -X POST http://localhost:8800/v1/uploads `
  -H "X-API-Key: obj_xxx" `
  -H "Content-Type: application/json" `
  -d '{"bucket":"media","objectKey":"large.bin","contentType":"application/octet-stream","totalParts":2}'

curl.exe -X PUT http://localhost:8800/v1/uploads/UPLOAD_ID/parts/1 `
  -H "X-API-Key: obj_xxx" `
  --data-binary "part one"

curl.exe -X PUT http://localhost:8800/v1/uploads/UPLOAD_ID/parts/2 `
  -H "X-API-Key: obj_xxx" `
  --data-binary "part two"

curl.exe -X POST http://localhost:8800/v1/uploads/UPLOAD_ID/complete `
  -H "X-API-Key: obj_xxx"

API

方法 路径 说明
GET /health 健康检查
GET /metrics Prometheus 指标
POST /admin/tenants 创建租户,需要管理员令牌
GET /v1/tenant 查询当前租户配额
PUT /v1/objects/:bucket/*key 上传对象并创建新版本
GET /v1/objects/:bucket/*key 下载当前或指定版本对象
GET /v1/objects 按桶和前缀列出当前对象
GET /v1/objects/:bucket/versions?key=... 查询对象版本
DELETE /v1/objects/:bucket/*key 软删除对象
POST /v1/presigned-downloads 创建预签名下载令牌
POST /v1/uploads 创建分片上传
PUT /v1/uploads/:id/parts/:part 上传分片
POST /v1/uploads/:id/complete 合并并提交分片上传
GET /download?token=... 使用预签名令牌下载

版本与回收语义

覆盖上传会产生新版本,历史版本仍可通过 ?version=N 下载。软删除只隐藏当前版本并释放租户逻辑配额,但历史版本仍然保留,因此其 Blob 不会立即物理删除。只有 Blob 没有任何历史版本引用时,GC Worker 才会执行回收。

配置项

环境变量 默认值 说明
PORT 8800 API 端口
DATABASE_PATH data/objects.db SQLite 文件
STORAGE_ROOT data Blob 和临时分片目录
ADMIN_TOKEN local-admin-token 管理员令牌
SIGNING_SECRET ADMIN_TOKEN 预签名令牌密钥
TENANT_QUOTA_BYTES 52428800 租户默认配额
RATE_LIMIT_PER_MINUTE 600 每租户每分钟请求数
MAX_OBJECT_BYTES 52428800 单对象最大大小
MAX_PART_BYTES 10485760 单分片最大大小
GC_POLL_MS 1000 GC Worker 轮询间隔
GC_LEASE_MS 30000 GC 任务租约时间
GC_GRACE_MS 60000 Blob 回收宽限期

Docker

docker compose up --build

生产环境必须修改 ADMIN_TOKENSIGNING_SECRET,通过 HTTPS 提供服务,并将本地文件系统替换为 S3、MinIO 或其他可靠对象存储。

验证

npm test
npm run test:load

测试覆盖对象版本、ETag、预签名下载、软删除、配额、分片上传、租户隔离、限流和 GC。负载脚本并发上传 200 个对象并输出吞吐。

详细设计见 架构文档

About

支持多租户、内容寻址去重、分片上传、预签名下载和异步回收的对象存储服务

Resources

Stars

1 star

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages