导航菜单

完整架构:从 0 到生产级

六个月后的架构图

从最简实现到现在,“光影”的图片系统经历了 6 个月、4 次大重构。这是最终的生产级架构:

最简实现:Flask + 本地磁盘,无 CDN,无压缩
用户层
用户浏览器 直接访问服务器
应用层
Flask 服务器 1 核 2G,5Mbps 带宽
存储层
本地磁盘 100GB,无冗余
客户端直传 OSS + 缩略图生成 + WebP 转换
用户层
用户浏览器 直传 OSS
应用层
上传服务 签发 STS 凭证
处理服务 缩略图 + WebP
存储层
阿里云 OSS 原图 + 缩略图
PostgreSQL 元数据
CDN 全球加速 + 内容审核 + 异步处理队列
用户层
用户浏览器 就近访问 CDN
CDN 层
阿里云 CDN 2800+ 节点,边缘裁剪
应用层
上传服务 × 3 签发凭证 + 回调
审核服务 × 2 鉴黄 + OCR
处理服务 × 4 多尺寸 + WebP/AVIF
数据层
阿里云 OSS 标准 + 低频 + 归档
RabbitMQ 异步处理队列
PostgreSQL 元数据 + 用户
生产级图片系统:12,000 用户,85,000 张图片,月成本 ¥1,720
用户层
用户浏览器 <picture> + 懒加载
CDN 层
阿里云 CDN 缓存命中率 96%,边缘裁剪
服务层
上传服务 × 3 STS 凭证 + 频率限制
审核服务 × 2 鉴黄 + OCR + 人审
处理服务 × 4 多尺寸 + EXIF
数据层
阿里云 OSS 标准 + 低频 + 归档
RabbitMQ upload→audit→process
辅助系统
Prometheus CDN 命中率 / 延迟
Grafana 监控面板 + 告警

组件详解

上传链路

# 完整上传流程(从用户选择图片到看到缩略图)
UPLOAD_PIPELINE = {
    'step_1_client': {
        'action': '前端校验文件类型和大小',
        'time': '< 100ms',
    },
    'step_2_credential': {
        'action': '从服务器获取 STS 临时凭证',
        'time': '~50ms',
    },
    'step_3_upload': {
        'action': '客户端直传 OSS(大文件分片)',
        'time': '2~10s(取决于文件大小和网速)',
    },
    'step_4_callback': {
        'action': 'OSS 回调通知上传服务',
        'time': '~100ms',
    },
    'step_5_audit': {
        'action': '内容审核(云 API,同步)',
        'time': '200ms~1s',
    },
    'step_6_process': {
        'action': '异步生成多尺寸缩略图',
        'time': '1~3s(不阻塞用户)',
    },
    'step_7_cdn': {
        'action': '缩略图自动预热到 CDN',
        'time': '后台执行',
    },
}

# 用户体验:
# - 上传完成后立即返回(step 1~4,约 3~12 秒)
# - 列表页先显示占位图,缩略图就绪后自动替换
# - 从用户角度看,上传后 2~5 秒就能看到缩略图

存储架构

# 存储分层策略
STORAGE_LAYERS = {
    '热数据(标准存储)': {
        'data': '最近 30 天的原图 + 缩略图',
        'price': '0.12 元/GB/月',
        'access': 'CDN 回源',
        'size': '~200 GB',
    },
    '温数据(低频存储)': {
        'data': '30~90 天的原图',
        'price': '0.08 元/GB/月',
        'access': '需要时恢复',
        'size': '~500 GB',
    },
    '冷数据(归档存储)': {
        'data': '90 天以上的原图',
        'price': '0.03 元/GB/月',
        'access': '解冻后访问(1~5 分钟)',
        'size': '~2 TB',
    },
}

# 缩略图永远在标准存储(体积小,访问频繁)
# 原图按访问时间自动降级

CDN 配置

# 生产级 CDN 配置
CDN_PRODUCTION_CONFIG = {
    # 域名
    'image_domain': 'img.guangying.com',
    
    # 缓存规则
    'cache_rules': [
        {'path': 'thumbs/*.webp', 'ttl': 2592000},    # 缩略图 30 天
        {'path': 'thumbs/*.avif', 'ttl': 2592000},    # AVIF 缩略图 30 天
        {'path': 'originals/*',   'ttl': 7776000},    # 原图 90 天
        {'path': 'temp/*',        'ttl': 3600},       # 临时文件 1 小时
    ],
    
    # URL 参数化实时裁剪
    'image_processing': {
        'resize': True,    # ?w=800&h=600
        'quality': True,   # ?q=80
        'format': True,    # ?format=webp
        'crop': True,      # ?crop=100,100,800,600
    },
    
    # HTTPS
    'https': {
        'enabled': True,
        'force': True,
        'http2': True,
    },
    
    # 防盗链
    'referer_whitelist': [
        'guangying.com',
        '*.guangying.com',
    ],
    
    # 监控
    'metrics': ['hit_rate', 'traffic', 'request_count', 'origin_latency'],
}

成本账本(月度)

# 当前规模下的月度成本
MONTHLY_COST = {
    '用户数': '12,000',
    '总图片数': '85,000',
    '日均 PV': '150,000',
    
    '存储费': {
        '标准存储': 200 * 0.12,    # 200GB × 0.12 = 24 元
        '低频存储': 500 * 0.08,    # 500GB × 0.08 = 40 元
        '归档存储': 2000 * 0.03,   # 2TB × 0.03 = 60 元
        '小计': 124,
    },
    
    'CDN 流量费': {
        'CDN 流量': 3000 * 0.24,   # 3TB × 0.24 = 720 元
        '回源流量': 150 * 0.50,    # 150GB × 0.50 = 75 元
        '小计': 795,
    },
    
    '处理费': {
        '图片处理': 200 * 0.0015,  # 20万次 × 0.0015 = 0.3 元
        '内容审核': 200 * 0.0025,  # 20万次 × 0.0025 = 0.5 元
        '小计': 1,
    },
    
    '服务器': {
        '应用服务器': 3 * 200,      # 3 台 × 200元/月
        '消息队列': 150,
        '监控': 50,
        '小计': 800,
    },
    
    '总计': 1720,  # 元/月
}

# 其中 CDN 流量费占 46%,是最大开支
# 服务器占 46%,可以通过 Serverless 进一步降低

演进路线图

阶段 1(第 1~2 周):
  Flask + 本地磁盘 + 无 CDN
  ✅ 能用,但慢

阶段 2(第 3~4 周):
  + 缩略图生成
  + WebP 格式转换
  + 客户端直传 OSS
  ✅ 解决了速度和存储问题

阶段 3(第 2~3 月):
  + CDN 加速
  + 内容审核
  + 异步处理队列
  ✅ 解决了分发和安全问题

阶段 4(第 4~6 月):
  + 存储分层
  + CDN 边缘裁剪
  + 监控告警
  + 成本优化
  ✅ 生产级系统

本节小结

从一张 8.7 MB 的图片拖垮网站,到支撑 12,000 用户的生产级图片系统——每一步都是在真实问题驱动下的演进。

没有过度设计,只有问题驱动的迭代。

我的思考

思考 1

如果”光影”的用户量增长 100 倍(120 万用户),这个架构的瓶颈在哪里?

参考答案

100 倍增长后的瓶颈分析:

用户数:12,000 → 1,200,000
图片数:85,000 → 8,500,000
日均 PV:150,000 → 15,000,000

瓶颈 1:CDN 流量费暴涨

CDN 流量:3TB → 300TB
CDN 费用:720 元 → 72,000 元/月

优化方案:
- 全面推进 AVIF 格式(再减 30% 体积)
- CDN 流量包预付费(折扣约 40%)
- 考虑自建 CDN 节点(核心城市)

瓶颈 2:处理服务扛不住

图片处理:20 万次/月 → 2,000 万次/月
单次处理时间:1~3 秒

优化方案:
- 从 4 台扩展到 40 台(弹性伸缩)
- 或迁移到 Serverless(按处理次数计费)
- 利用 CDN 边缘处理,减少中心处理量

瓶颈 3:OSS 单 Region 流量

回源流量:150GB → 15TB
跨 Region 用户延迟增大

优化方案:
- 多 Region 部署(北京 + 上海 + 广州)
- 跨区域复制(异步)
- DNS 智能解析到最近的 Region

核心原则:100 倍增长意味着每个组件都需要考虑水平扩展。好消息是 OSS 和 CDN 天然支持无限扩展,需要重点优化的是处理服务和多 Region 策略。

搜索