Files
ystp/docs/architecture.md

12 KiB
Raw Permalink Blame History

技术架构设计

目标:支撑“网站 + 对外 API + 计费”的商用闭环。产品范围见 docs/prd.md,计费口径见 docs/billing.md

系统架构图

                       ┌───────────────────────────────┐
                       │ CDN (静态资源/下载可选加速)    │
                       └──────────────┬────────────────┘
                                      │
                               ┌──────▼──────┐
                               │ Nginx/Caddy │
                               │ TLS/反向代理 │
                               └──────┬──────┘
          ┌───────────────────────────┼───────────────────────────┐
          │                           │                           │
          ▼                           ▼                           ▼
 ┌─────────────────┐        ┌─────────────────┐        ┌─────────────────┐
 │ Web 前端 (Vue3) │        │ API 服务 (Axum) │        │ Admin 前端(Vue3)│
 │ 上传/结果/账单   │        │ 认证/计费/接口   │        │ 运营/风控/配置    │
 └─────────────────┘        └───────┬─────────┘        └─────────────────┘
                                    │
                 ┌──────────────────┼──────────────────┐
                 │                  │                  │
                 ▼                  ▼                  ▼
        ┌─────────────────┐  ┌─────────────────┐  ┌─────────────────┐
        │ PostgreSQL (DB) │  │ Redis (缓存/队列)│  │ 对象存储 (S3)    │
        │ 用户/任务/账单/用量│  │ Streams/RateLimit│  │ 原图/压缩结果     │
        └─────────┬───────┘  └───────┬─────────┘  └─────────┬───────┘
                  │                  │                     │
                  │                  ▼                     │
                  │           ┌───────────────┐            │
                  │           │ Worker (Rust) │            │
                  │           │ 压缩/计量/回写  │            │
                  │           └───────────────┘            │
                  │                                        │
                  ▼                                        ▼
        ┌─────────────────┐                      ┌─────────────────┐
        │ 支付渠道/网关     │◄──────Webhooks───────│ API(Webhook处理) │
        │ Stripe           │                      │ 订阅/发票/状态   │
        └─────────────────┘                      └─────────────────┘

核心组件设计

0. 服务拆分(推荐)

为避免 CPU 密集的压缩任务影响 API 延迟,建议最小拆分为:

  • API 服务:认证、限流、计费/订阅、任务编排、回调、签名 URL、管理后台 API。
  • Worker 服务执行图片压缩CPU 密集)、写入结果、落用量账本、推送进度。

本地开发可以合并进一个进程feature flag生产建议分开部署并可独立扩容。

1. 压缩引擎

// 压缩配置
pub enum CompressionLevel {
    /// 高压缩比 - 有损压缩,文件最小
    High,
    /// 中等压缩 - 平衡模式
    Medium,
    /// 低压缩比 - 无损/近无损,质量优先
    Low,
}

pub struct CompressionConfig {
    pub level: CompressionLevel,
    pub output_format: Option<ImageFormat>,  // 可选转换格式
    pub max_width: Option<u32>,              // 可选调整尺寸
    pub max_height: Option<u32>,
    pub preserve_metadata: bool,             // 是否保留元数据(默认 false
}

2. 压缩策略

格式 高压缩比(有损) 低压缩比(无损) 使用库
PNG pngquant 量化到 256 色 oxipng 无损优化 imagequant + oxipng
JPEG mozjpeg quality=60 mozjpeg quality=90 mozjpeg
WebP lossy quality=75 lossless webp
AVIF quality=50 quality=90 ravif
// 压缩核心逻辑
pub trait ImageCompressor: Send + Sync {
    async fn compress(&self, input: &[u8], config: &CompressionConfig) -> Result<Vec<u8>>;
    fn supported_formats(&self) -> Vec<ImageFormat>;
}

pub struct PngCompressor;
pub struct JpegCompressor;
pub struct WebpCompressor;
pub struct AvifCompressor;

// 统一压缩入口
pub struct CompressionEngine {
    compressors: HashMap<ImageFormat, Box<dyn ImageCompressor>>,
}

impl CompressionEngine {
    pub async fn compress(&self, input: &[u8], config: &CompressionConfig) -> Result<CompressionResult> {
        let format = detect_format(input)?;
        let compressor = self.compressors.get(&format)
            .ok_or(Error::UnsupportedFormat)?;

        let output = compressor.compress(input, config).await?;

        Ok(CompressionResult {
            original_size: input.len(),
            compressed_size: output.len(),
            format,
            data: output,
        })
    }
}

3. 任务处理模型

支持两种模式:

同步模式(小文件/单文件)

请求 -> 压缩 -> 直接返回结果

异步模式(大文件/批量)

请求 -> 创建任务 -> 返回任务ID
         ↓
     后台Worker处理
         ↓
     客户端轮询/WebSocket通知
         ↓
     下载结果
pub enum TaskStatus {
    Pending,
    Processing,
    Completed,
    Failed,
}

pub struct CompressionTask {
    pub id: Uuid,
    pub user_id: Option<Uuid>,      // 游客为空
    pub session_id: Option<String>, // 游客会话Cookie
    pub status: TaskStatus,
    pub files: Vec<FileTask>,
    pub config: CompressionConfig,
    pub created_at: DateTime<Utc>,
    pub completed_at: Option<DateTime<Utc>>,
}

pub struct FileTask {
    pub id: Uuid,
    pub original_name: String,
    pub original_size: u64,
    pub compressed_size: Option<u64>,
    pub status: TaskStatus,
    pub error: Option<String>,
}

4. 用户认证系统

// JWT Claims
#[derive(Serialize, Deserialize)]
pub struct Claims {
    pub sub: Uuid,          // 用户ID
    pub role: UserRole,     // 用户角色
    pub exp: i64,           // 过期时间
}

pub enum UserRole {
    User,
    Admin,
}

// API Key 认证
pub struct ApiKey {
    pub id: Uuid,
    pub user_id: Uuid,
    pub key_prefix: String, // 前缀索引(仅展示用)
    pub key_hash: String,   // 推荐HMAC-SHA256(key, server_pepper) 或 sha256+pepper
    pub name: String,
    pub permissions: Vec<Permission>,
    pub rate_limit: u32,    // 每分钟请求数
    pub created_at: DateTime<Utc>,
    pub last_used_at: Option<DateTime<Utc>>,
}

pub enum Permission {
    Compress,
    BatchCompress,
    ReadStats,
    BillingRead,   // 查看账单/用量(可选)
    WebhookManage, // 管理 Webhook可选
}

5. 限流与配额Rate Limit & Quota

用途区分:

  • 限流:保护服务(超出返回 HTTP 429
  • 配额:试用/计费限制(超出返回 HTTP 402 / QUOTA_EXCEEDED

默认建议(最终以 system_config + 套餐 plans.* 为准):

  • 匿名试用:10 req/min + 10 units/day + 5MB/文件 + 5 文件/批量
  • 登录用户:60 req/min;文件大小/批量/保留期/周期额度由套餐决定
  • API Key100 req/min(可配置);文件大小/批量/周期额度由套餐/Key 覆盖决定

6. 用量计量与计费Metering & Billing

计量口径见 docs/billing.md,架构上建议:

  • Worker 在每个文件成功输出后写入 usage_events(账本明细),并更新 usage_periods(按订阅周期聚合)。
  • API 在创建任务/接收同步压缩请求时做配额预检快速失败Worker 做最终扣减(账本落地,保证一致性)。
  • 对外 API 强烈建议支持 Idempotency-KeyDB 侧存储“幂等记录 + 响应摘要”,避免重复扣减与重复任务。

7. 支付回调Webhooks

Stripe 通常通过 webhook 推送订阅/支付状态变更:

  • API 服务提供 /webhooks/{provider} 入口,验签 + 幂等 + 可重放
  • webhook 事件入库后异步处理(避免回调超时),更新订阅/发票状态,并写审计日志。

核心依赖

[dependencies]
# Web 框架
axum = "0.7"
tokio = { version = "1", features = ["full"] }
tower = "0.4"
tower-http = { version = "0.5", features = ["cors", "fs", "compression"] }

# 图片处理
image = "0.25"
oxipng = "9"
imagequant = "4"           # PNG 有损压缩pngquant 核心)
mozjpeg = "0.10"           # JPEG 压缩
webp = "0.3"               # WebP 编解码
ravif = "0.11"             # AVIF 编码

# 数据库
sqlx = { version = "0.7", features = ["runtime-tokio", "postgres", "uuid", "chrono"] }

# Redis
redis = { version = "0.24", features = ["tokio-comp"] }

# 认证
jsonwebtoken = "9"
argon2 = "0.5"             # 密码哈希

# 工具
uuid = { version = "1", features = ["v4", "serde"] }
chrono = { version = "0.4", features = ["serde"] }
serde = { version = "1", features = ["derive"] }
serde_json = "1"
thiserror = "1"
tracing = "0.1"
tracing-subscriber = "0.3"

# 配置
config = "0.14"
dotenvy = "0.15"

性能考虑

1. 并发处理

  • 使用 Tokio 异步运行时
  • 图片压缩使用 spawn_blocking 避免阻塞异步线程
  • 可配置 Worker 线程数
// 在独立线程池中执行 CPU 密集型压缩
let result = tokio::task::spawn_blocking(move || {
    compress_image_sync(&data, &config)
}).await??;

2. 内存管理

  • 流式处理大文件
  • 限制并发压缩任务数
  • 压缩完成后立即清理临时文件

3. 缓存策略

  • Redis 缓存用户会话
  • 可选:相同图片哈希缓存结果(去重)

安全考虑

  1. 输入验证:检查文件魔数,不仅依赖扩展名
  2. 文件大小限制:防止 DoS
  3. 像素/维度限制:防止“图片炸弹”(解码后超大)
  4. 路径遍历防护:存储时使用 UUID 命名
  5. SQL 注入防护使用参数化查询SQLx 自动处理)
  6. XSS 防护:前端输出转义
  7. CSRF 防护SameSite Cookie + Token
  8. 速率限制:防止滥用
  9. 默认移除元数据:避免泄露定位/设备信息(除非用户明确开启保留)

可扩展性

水平扩展

                    ┌─────────────┐
                    │ Load Balancer│
                    └──────┬──────┘
           ┌───────────────┼───────────────┐
           ▼               ▼               ▼
     ┌──────────┐    ┌──────────┐    ┌──────────┐
     │ API 实例1 │    │ API 实例2 │    │ API 实例3 │
     └──────────┘    └──────────┘    └──────────┘
           │               │               │
           └───────────────┼───────────────┘
                           ▼
                  ┌─────────────────┐
                  │ 共享 PostgreSQL │
                  │ 共享 Redis      │
                  │ 共享 S3 存储    │
                  └─────────────────┘

后续可添加功能

  • 消息队列RabbitMQ/NATS处理异步任务
  • 分布式任务调度
  • CDN 加速下载