文件服务架构的变迁

如今我们随处可见的图片浏览、视频浏览等功能,都依赖于文件服务。而文件服务架构经历了三个阶段,我分别称之为刀耕火种时代、自建服务时代和云服务时代。

刀耕火种时代的文件服务架构

在互联网早起,中小公司通常依赖于自己的服务器来提供文件服务。最常见的一种方式是将文件直接上传到项目服务器,然后通过服务器返回图片URL给客户端。比如很多古老的PHP项目通常是直接上传到uploads目录下(当然Java也类似),这种方式非常原始且粗糙,甚至非常不安全,经常出现忘记做过滤或者过滤规则设置不合理,导致出现上传木马文件到项目目录中的情况,如果遇到项目目录权限配置的疏漏,经常出现安全漏洞,这也是导致以前PHP经常被人诟病不安全的原因之一。

除了上述的安全问题外,这种方式还存在诸多问题,如成本高昂、维护复杂、扩展性差等问题。还是用上面的例子来说明,本就有限的服务器资源和带宽资源除了要提供网页服务和API接口服务之外,还需要提供文件服务,包括但不限于文件上传、存储、分发、处理等功能。这意味着项目服务器需要承担较大的负载压力,同时也增加了项目的维护成本。而且项目服务器本身通常是Apache或Tomcat,这些服务器本身就不是为文件服务器设计的,用来提供文件服务,既不恰当也不高效,而且如果要对图片进行裁切缩放等处理,还要搭配imagick或者GD等应用软件,图片处理本身就非常消耗CPU资源,这就使得本就捉襟见肘的服务器性能更加雪上加霜。

自建服务时代

为了解决以上问题,衍生出一种优化方案,这种方案的基本思想是将文件服务独立出来,部署在一个单独的服务器上,与项目服务器分离。这样做的好处是图片服务器不会影响到业务服务器的性能,业务服务器只需要负责处理业务逻辑,而文件服务器只需要负责提供文件访问以及处理图片相关的操作。同时,将文件服务独立出来还可以将文件服务的负载压力分散到多个服务器上,避免单点故障,同时也可以根据实际需求进行水平扩展。

这个时期文件存储通常需要自己配置Raid磁盘阵列来提高存储性能和可靠性。而且通常会采用Nginx或者Varnish等反向代理服务器来处理访问请求,将请求转发到图片服务器上。当然了,由于Nginx本身出色的性能,这个时期的文件服务器通常也是Nginx。同时,为了提高访问速度,通常会采用CDN(内容分发网络)来缓存文件,将文件分发到全球各地的节点上,实现快速访问。我们熟悉的网宿CDN大概就是发源于此时期。

即使解决了存储和访问的问题,但是文件处理的问题仍然存在。比如图片文件裁切、缩放、压缩等操作,通常需要在事先或事后处理好,然后分发到文件服务器上,这就显得还是不那么灵活。

不过此时期,业内的大公司已经有自己的NFS(Network File System)方案了,比如大名鼎鼎的GFS(Google File System)、淘宝的TFS(Taobao File System)和腾讯的TFS(Tencent File System)等。不过这些方案通常只是在公司内部使用,并未向外提供服务。中小公司依然缺乏合适的的高可用性、高扩展性和高可靠性的文件服务。

云服务时代的文件服务架构

云服务的诞生

不得不说,云服务的出现也促进了互联网的发展和繁荣。低廉的成本使得中小公司也能使用到和大公司一样的互联网基础设施,同时也为中小公司的业务发展提供了更多的可能性。

云服务时代的文件服务架构

自从以AWS为代表的云服务的出现以来,互联网公司的服务架构也经历了很大的变革。云服务提供了一种高度可扩展、高可用、成本低廉的文件服务解决方案。中小公司可以根据实际需求,选择合适的云服务商(如阿里云、腾讯云、七牛云等)来搭建自己的服务架构。

S3(Simple Storage Service)是AWS提供的一种对象存储服务,它提供了高可用性、高扩展性、高可靠性的文件存储解决方案。S3的基本概念是将文件(对象)存储在存储桶(Bucket)中,每个对象都有一个唯一的URL来访问。S3还提供了丰富的API和SDK,方便开发人员集成到自己的应用程序中。各个云服务提供商(如阿里云、腾讯云、七牛云等)也都推出了自家的S3服务,尽管名称各不相同,但都是兼容AWS的S3,毕竟AWS S3已经成为了事实标准,即使后来涌现的各种开源文件服务系统也都兼容S3,S3已经成为一种事实上的文件服务协议。

图片服务的核心需求

对于大多数中小公司来说,图片服务需要满足以下核心需求:

  • 存储需求:安全、可靠的图片存储,支持海量图片
  • 访问速度:全球各地用户都能快速访问图片
  • 图片处理:支持常见的图片处理操作(裁剪、缩放、压缩等)
  • 成本控制:经济实惠的解决方案
  • 可扩展性:随着业务增长可以平滑扩展
  • 简单维护:尽量减少运维成本

基于OSS和CDN的架构设计

整体架构

我推荐的架构如下:

1
用户浏览器/客户端 → CDN节点 → OSS存储 → 图片处理服务

这种架构的核心是利用云服务商提供的OSS(对象存储服务)作为存储基础,配合CDN(内容分发网络)来加速访问,同时利用OSS自带的图片处理功能或第三方图片处理服务来处理图片。

为什么选择OSS?

  1. 高可用性:主流云服务商的OSS都能提供99.99%以上的可用性
  2. 按需付费:按实际使用量付费,避免资源浪费
  3. 自动扩展:存储容量可以自动扩展,无需担心存储上限
  4. 安全可靠:提供多副本存储、访问控制等安全特性
  5. 图片处理:大多数OSS都内置了基础的图片处理功能

为什么选择CDN?

  1. 加速访问:通过就近缓存,显著提升图片加载速度
  2. 降低源站压力:减少对OSS的直接访问请求
  3. 节省流量成本:通过缓存减少重复下载,降低OSS的流量费用
  4. 全球覆盖:即使是小公司也能提供接近全球的快速访问体验

OSS选择与配置

主流OSS对比

服务商 产品 优势 劣势
阿里云 OSS 功能完善,生态丰富 价格相对较高
腾讯云 COS 性价比高,与微信生态结合好 部分高级功能需付费
七牛云 Kodo 专注于图片服务,提供丰富的处理功能 整体生态不如大厂完善
AWS S3 全球覆盖最广,功能最成熟 国内访问可能受限

桶(Bucket)设计建议

对于中小公司,我建议采用以下桶设计:

  1. 按环境分离:开发环境、测试环境、生产环境使用不同的桶
  2. 按业务分类:如用户头像、商品图片、文章配图等使用不同的桶
  3. 按存储类型区分:频繁访问的图片和归档图片使用不同存储类型

文件命名与组织

一种简单的文件命名规范:

1
{业务类型}/{年月日}/{UUID}.{扩展名}

例如:

1
2
avatar/20251022/8e7f9a6b-c3d2-4e1a-9b8c-7d6e5f4a3b2c.jpg
product/20251022/1a2b3c4d-5e6f-7a8b-9c0d-1e2f3a4b5c6d.png

我自己常用的文件命名规范是:

1
{业务类型}/{年月日}/{base62编码后的uid+base62编码后的时间因子+编码后的随机数}.{扩展名}

例如:

1
/_avatar/20251020/Q0v_V0A6WdR_Lc7YFF.png

我来说一下这个命名规范的优势:

  1. 可读性高:文件名中包含了业务类型、日期、uid、时间因子,方便溯源和管理。
  2. 唯一性:每个文件名都是唯一的,避免了文件名冲突的问题。
  3. 时间有序:文件名中包含了日期信息,方便按时间排序和查询。
  4. 随机数:文件名中包含了随机数,增加了文件名的随机性,避免了文件名重复的问题。

另外,下划线_在文件名中也有特殊含义,它可以用来区分临时和正式的图片路径。例如,_avatar表示这是一个临时的头像图片文件路径,而avatar则表示这是一个正式的头像图片文件。有了这个区分,再加上日期,我们就可以配合定时任务,定期清理过期的临时图片文件,保持存储的整洁和效率,降低存储成本。

CDN配置最佳实践

缓存策略

  1. 设置合理的缓存时间

    • 静态图片:7-30天
    • 不常变化的图片:1-7天
    • 可能频繁更新的图片:1小时-1天
  2. 缓存刷新机制

    • 重要图片更新后主动刷新CDN缓存
    • 使用版本号或哈希值作为文件名的一部分,避免缓存问题

域名配置

建议使用专用域名:

1
images.yourcompany.com

图片处理策略

图片格式选择

  • WebP格式:提供更好的压缩率和质量,现代浏览器支持良好
  • AVIF格式:新一代图片格式,压缩率更高,但浏览器支持还在普及中
  • 降级方案:对于不支持WebP的浏览器,提供JPG/PNG格式

图片处理方案

  1. OSS内置处理:大多数OSS提供基础的图片处理功能

    • 阿里云OSS:支持resize、crop、rotate等操作
    • 腾讯云COS:提供imageMogr2等处理能力
  2. 按需处理示例

    1
    2
    3
    4
    5
    6
    7
    8
    # 原图
    https://images.yourcompany.com/avatar/20251022/user1.jpg

    # 缩略图(300x300)
    https://images.yourcompany.com/avatar/20251022/user1.jpg?x-oss-process=image/resize,m_fixed,w_300,h_300

    # WebP格式
    https://images.yourcompany.com/avatar/20251022/user1.jpg?x-oss-process=image/format,webp

成本优化策略

  1. 合理选择存储类型

    • 热数据:标准存储
    • 温数据:低频访问存储
    • 冷数据:归档存储
  2. CDN流量优化

    • 启用Brotli/Gzip压缩
    • 使用WebP等高效格式
    • 设置合理的缓存策略
  3. 按需处理图片:避免预先生成大量不同尺寸的图片

安全性考虑

  1. 访问控制

    • 使用签名URL保护私密图片
    • 设置合理的Referer白名单
  2. 防盗链

    • 配置CDN防盗链
    • 使用时间戳防盗链
  3. 文件类型限制

    • 上传时严格校验文件类型
    • 避免上传可执行文件伪装成图片

实际案例:电商网站图片架构

架构图

1
2
用户上传 → 应用服务器 → 图片处理服务 → OSS存储
用户访问 → CDN → OSS存储

具体实现

  1. 上传流程

    • 前端获取临时上传凭证
    • 直接上传到OSS
    • 上传成功后通知后端
  2. 访问流程

    • 用户请求图片URL
    • CDN节点响应,如果有缓存
    • 如果没有缓存,CDN回源OSS获取
    • OSS返回图片,CDN缓存并返回给用户

总结

基于OSS和CDN的图片服务架构非常适合中小公司,它提供了以下优势:

  1. 低运维成本:无需自己维护存储服务器
  2. 按需付费:根据实际使用量付费,避免资源浪费
  3. 高可用性:利用云服务商的基础设施保障服务稳定
  4. 全球加速:通过CDN提供快速的访问体验
  5. 灵活扩展:随着业务增长可以平滑扩展

这种架构让中小公司能够专注于业务发展,而不必过多担心基础设施的问题。当然,随着业务的发展,可能需要考虑更复杂的架构,但在初期阶段,这是一个非常实用的解决方案。