现在几乎任何一个网站、Web App 以及移动 APP 等应用都需要有图片展示的功能,对于图片功能从下至上都是很重要的。必须要具有前瞻性的规划好图片服务器,图片的上传和下载速度至关重要,当然这并不是说一上来就搞很NB的架构,至少具备一定扩展性和稳定性。虽然各种架构设计都有,在这里我只是谈谈我的一些个人想法。
对于图片服务器来说IO无疑是消耗资源最为严重的,对于web应用来说需要将图片服务器做一定的分离,否则很可能因为图片服务器的 IO 负载导致应用崩溃。因此尤其对于大型网站和应用来说,非常有必要将图片服务器和应用服务器分离,构建独立的图片服务器集群,构建独立的图片服务器其主要优势:
- 分担 Web 服务器的 I/O 负载-将耗费资源的图片服务分离出来,提高服务器的性能和稳定性。
- 能够专门对图片服务器进行优化-为图片服务设置有针对性的缓存方案,减少带宽网络成本,提高访问速度。
- 提高网站的可扩展性-通过增加图片服务器,提高图片服务吞吐能力。
从传统互联网的 web1.0,历经 web2.0 时代以及发展到现在的 web3.0,随着图片存储规模的增加,图片服务器的架构也在逐渐发生变化,以下主要论述三个阶段的图片服务器架构演进。
初始阶段

在介绍初始阶段的早期的小型图片服务器架构之前,首先让我们了解一下 NFS 技术,NFS 是 Network File System 的缩写,即网络文件系统。NFS 是由 Sun 开发并发展起来的一项用于在不同机器,不同操作系统之间通过网络互相分享各自的文件。NFS server 也可以看作是一个 FILE SERVER,用于在 UNIX 类系统之间共享文件,可以轻松的挂载(mount)到一个目录上,操作起来就像本地文件一样的方便。
如果不想在每台图片服务器同步所有图片,那么 NFS 是最简单的文件共享方式。NFS 是个分布式的客户机/服务器文件系统,NFS 的实质在于用户间计算机的共享,用户可以联结到共享计算机并象访问本地硬盘一样访问共享计算机上的文件。具体实现思路是:
- 所有前端 web 服务器都通过 nfs 挂载 3 台图片服务器 export 出来的目录,以接收 web 服务器写入的图片。然后[图片1]服务器挂载另外两台图片服务器的 export 目录到本地给 apache 对外提供访问。
- 用户上传图片:用户通过 Internet 访问页面提交上传请求 post 到 web 服务器,web 服务器处理完图片后由 web 服务器拷贝到对应的 mount 本地目录。
- 用户访问图片:用户访问图片时,通过[图片1]这台图片服务器来读取相应 mount 目录里边的图片。
以上架构存在的问题:
- 性能:现有结构过度依赖 nfs,当图片服务器的 nfs 服务器有问题时,可能影响到前端 web 服务器。NFS 的问题主要是锁的问题. 很容易造成死锁, 只有硬件重启才能解决。尤其当图片达到一定的量级后,nfs 会有严重的性能问题。
- 高可用:对外提供下载的图片服务器只有一台,容易出现单点故障。
- 扩展性:图片服务器之间的依赖过多,而且横向扩展余地不够。
- 存储:web 服务器上传热点不可控,造成现有图片服务器空间占用不均衡。
- 安全性:nfs 方式对于拥有 web 服务器的密码的人来说,可以随意修改 nfs 里边的内容,安全级别不高。
当然图片服务器的图片同步可以不采用 NFS,也可以采用 ftp 或 rsync,采用 ftp 这样的话每个图片服务器就都保存一份图片的副本,也起到了备份的作用。但是缺点是将图片 ftp 到服务器比较耗时,如果使用异步方式去同步图片的话又会有延时,不过一般的小图片文件也还好了。使用 rsync 同步,当数据文件达到一定的量级后,每次 rsync 扫描会耗时很久也会带来一定的延时性。
发展阶段

当网站达到一定的规模后,对图片服务器的性能和稳定性有一定的要求后,上述 NFS 图片服务架构面临着挑战,严重的依赖 NFS,而且系统存在单点机器容易出现故障,需要对整体架构进行升级。于是出现了上图图片服务器架构,出现了分布式的图片存储。
其实现的具体思路如下:
- 用户上传图片到 web 服务器后,web 服务器处理完图片,然后再由前端 web 服务器把图片 post 到到[图片1]、[图片2]…[图片N]其中的一个,图片服务器接收到 post 过来的图片,然后把图片写入到本地磁盘并返回对应成功状态码。前端web服务器根据返回状态码决定对应操作,如果成功的话,处理生成各尺寸的缩略图、打水印,把图片服务器对应的 ID 和对应图片路径写入 DB 数据库。
- 上传控制:我们需要调节上传时,只需要修改 web 服务器 post 到的目的图片服务器的 ID,就可以控制上传到哪台图片存储服务器,对应的图片存储服务器只需要安装 nginx 同时提供一个 python 或者 php 服务接收并保存图片,如果不想开启 python 或者 php 服务,也可以编写一个 nginx 扩展模块。
- 用户访问流程:用户访问页面的时候,根据请求图片的 URL 到对应图片服务器去访问图片。
如: http://imgN.xxx.com/image1.jpg
此阶段的图片服务器架构,增加了负载均衡和分布式图片存储,能够在一定程度上解决并发访问量高和存储量大的问题。负载均衡在有一定财力的情况下可以考虑 F5 硬负载,当然也可以考虑使用开源的 LVS 软负载(同时还可开启缓存功能)。此时将极大提升访问的并发量,可以根据情况随时调配服务器。当然此时也存在一定的瑕疵,那就是可能在多台 Squid 上存在同一张图片,因为访问图片时可能第一次分到 squid1,在 LVS 过期后第二次访问到 squid2 或者别的,当然相对并发问题的解决,此种少量的冗余完全在我们的允许范围之内。在该系统架构中二级缓存可以使用 squid 也可以考虑使用 varnish 或者 traffic server,对于 cache 的开源软件选型要考率以下几点
1)性能:varnish 本身的技术上优势要高于 squid,它采用了“Visual Page Cache”技术,在内存的利用上,Varnish 比 Squid 具有优势,它避免了 Squid 频繁在内存、磁盘中交换文件,性能要比 Squid 高。varnish 是不能 cache 到本地硬盘上的。还有强大的通过 Varnish 管理端口,可以使用正则表达式快速、批量地清除部分缓存。nginx 是用第三方模块 ncache 做的缓冲,其性能基本达到 varnish,但在架构中 nginx 一般作为反向(静态文件现在用 nginx 的很多,并发能支持到2万+)。在静态架构中,如果前端直接面对的是 cdn 活着前端了 4 层负载的话,完全用 nginx 的 cache 就够了。
2)避免文件系统式的缓存,在文件数据量非常大的情况下,文件系统的性能很差,像 squid,nginx的proxy_store,proxy_cache 之类的方式缓存,当缓存的量级上来后,性能将不能满足要求。开源的 traffic server 直接用裸盘缓存,是一个不错的选择,国内大规模应用并公布出来的主要是淘宝,并不是因为它做的差,而是开源时间晚。Traffic Server 在 Yahoo 内部使用了超过 4 年,主要用于 CDN 服务,CDN 用于分发特定的 HTTP 内容,通常是静态的内容如图片、JavaScript、CSS。当然使用 leveldb 之类的做缓存,我估计也能达到很好的效果。
3)稳定性:squid 作为老牌劲旅缓存,其稳定性更可靠一些,从我身边一些使用者反馈来看 varnish 偶尔会出现 crash 的情况。Traffic Server 在雅虎目前使用期间也没有出现已知的数据损坏情况,其稳定性相对也比较可靠,对于未来我其实更期待 Traffic Server 在国内能够拥有更多的用户。
以上图片服务架构设计消除了早期的 NFS 依赖以及单点问题,时能够均衡图片服务器的空间,提高了图片服务器的安全性等问题,但是又带来一个问题是图片服务器的横向扩展冗余问题。只想在普通的硬盘上存储,首先还是要考虑一下物理硬盘的实际处理能力。是 7200 转的还是 15000 转的,实际表现差别就很大。至于文件系统选择 xfs、ext3、ext4 还是 reiserFs,需要做一些性能方面的测试,从官方的一些测试数据来看,reiserFs 更适合存储一些小图片文件。创建文件系统的时候 Inode 问题也要加以考虑,选择合适大小的 inode size,因为 Linux 为每个文件分配一个称为索引节点的号码 inode,可以将 inode 简单理解成一个指针,它永远指向本文件的具体存储位置。一个文件系统允许的 inode 节点数是有限的,如果文件数量太多,即使每个文件都是 0 字节的空文件,系统最终也会因为节点空间耗尽而不能再创建文件,因此需要在空间和速度上做取舍,构造合理的文件目录索引。
云存储阶段

2011 年李彦宏在百度联盟峰会上就提到过互联网的读图时代已经到来,图片服务早已成为一个互联网应用中占比很大的部分,对图片的处理能力也相应地变成企业和开发者的一项基本技能,图片的下载和上传速度显得更加重要,要想处理好图片,需要面对的三个主要问题是:大流量、高并发、海量存储。
阿里云存储服务(OpenStorageService,简称OSS),是阿里云对外提供的海量,安全,低成本,高可靠的云存储服务。用户可以通过简单的 REST 接口,在任何时间、任何地点上传和下载数据,也可以使用 WEB 页面对数据进行管理。同时,OSS 提供 Java、Python、PHP SDK,简化用户的编程。基于 OSS,用户可以搭建出各种多媒体分享网站、网盘、个人企业数据备份等基于大规模数据的服务。在以下图片云存储主要以阿里云的云存储 OSS 为切入点介绍,上图为 OSS 云存储的简单架构示意图。
真正意义上的“云存储”,不是存储而是提供云服务,使用云存储服务的主要优势有以下几点:
- 用户无需了解存储设备的类型、接口、存储介质等。
- 无需关心数据的存储路径。
- 无需对存储设备进行管理、维护。
- 无需考虑数据备份和容灾
- 简单接入云存储,尽情享受存储服务。
架构模块组成

1)KV Engine:OSS 中的 Object 源信息和数据文件都是存放在 KV Engine 上。在 6.15 的版本,V Engine 将使用 0.8.6 版本,并使用为 OSS 提供的 OSSFileClient。
2)Quota:此模块记录了 Bucket 和用户的对应关系,和以分钟为单位的 Bucket 资源使用情况。Quota 还将提供 HTTP 接口供 Boss 系统查询。
3)安全模块:安全模块主要记录 User 对应的 ID 和 Key,并提供 OSS 访问的用户验证功能。
OSS 术语名词汇
Access Key ID & Access Key Secret (API 密钥):用户注册 OSS 时,系统会给用户分配一对 Access Key ID & Access Key Secret,称为 ID 对,用于标识用户,为访问 OSS 做签名验证。
Service:OSS 提供给用户的虚拟存储空间,在这个虚拟空间中,每个用户可拥有一个到多个 Bucket。
Bucket:Bucket 是 OSS 上的命名空间;Bucket 名在整个 OSS 中具有全局唯一性,且不能修改;存储在 OSS 上的每个 Object 必须都包含在某个 Bucket 中。一个应用,例如图片分享网站,可以对应一个或多个 Bucket。一个用户最多可创建 10 个 Bucket,但每个 Bucket 中存放的 Object 的数量和大小总和没有限制,用户不需要考虑数据的可扩展性。
Object:在 OSS 中,用户的每个文件都是一个 Object,每个文件需小于 5TB。Object 包含 key、data 和 user meta。其中,key 是 Object 的名字;data 是 Object 的数据;user meta 是用户对该 object 的描述。
1 | // 其使用方式非常简单,如下为 java sdk: |
- 分布式文件系统:用分布式存储有几个好处,分布式能自动提供冗余,不需要我们去备份,担心数据安全,在文件数量特别大的情况下,备份是一件很痛苦的事情,rsync 扫一次可能是就是好几个小时,还有一点就是分布式存储动态扩容方便。当然在国内的其他一些文件系统里,TFS和 FASTDFS 也有一些用户,但是 TFS 的优势更是针对一些小文件存储,主要是淘宝在用。另外 FASTDFS 在并发高于 300 写入的情况下出现性能问题,稳定性不够友好。OSS 存储使用的是阿里云基于飞天 5k 平台自主研发的高可用,高可靠的分布式文件系统盘古。分布式文件系统盘古和 Google 的 GFS 类似,盘古的架构是 Master-Slave 主从架构,Master 负责元数据管理,Slave 叫做 Chunk Server,负责读写请求。其中 Master 是基于 Paxos 的多 Master 架构,一个 Master 死了之后,另外一个 Master 可以很快接过去,基本能够做到故障恢复在一分钟以内 。文件是按照分片存放,每个会分三个副本,放在不同的机架上,最后提供端到端的数据校验。
- HAPROXY 负载均衡:基于 haproxy 的自动 hash 架构 ,这是一种新的缓存架构,由 nginx 作为最前端,代理到缓存机器。 nginx 后面是缓存组,由 nginx 经过 url hash 后将请求分到缓存机器。这个架构方便纯 squid 缓存升级,可以在 squid 的机器上加装 nginx。 nginx 有缓存的功能,可以将一些访问量特大的链接直接缓存在 nginx 上,就不用经过多一次代理的请求,能够保证图片服务器的高可用、高性能。比如 favicon.ico 和网站的 logo。 负载均衡负责 OSS 所有的请求的负载均衡,后台的 http 服务器故障会自动切换,从而保证了 OSS 的服务不间断。
- CDN:阿里云 CDN 服务是一个遍布全国的分布式缓存系统,能够将网站文件(如图片或 JavaScript 代码文件)缓存到全国多个城市机房中的服务器上,当一个用户访问你的网站时,会就近到靠近 TA 的城市的服务器上获取数据,这样最终用户访问你的服务速度会非常快。
阿里云 CDN 服务在全国部署超过 100 个节点,能提供给用户优良的网络加速效果。当网站业务突然爆发增长时,无需手忙脚乱地扩容网络带宽,使用 CDN 服务即可轻松应对。和 OSS 服务一样,使用 CDN,需要先在 aliyun.com 网站上开通 CDN 服务。开通后,需要在网站上的管理中心创建你的 distribution(即分发频道),每个 distribution 由两个必须的部分组成:distribution ID 和源站地址。
使用阿里云 OSS 和 CDN 可以非常方便的针对每个 bucket 进行内容加速,因为每个 bucket 对应一个独立的二级域名,针对每个文件进行 CDN 删除,简单、经济地解决服务的存储和网络问题,毕竟大多数网站或应用的存储和网络带宽多半是被图片或视频消耗掉的。
从整个业界来看,最近这样的面向个人用户的云存储如国外的 DropBox 和 Box.net 非常受欢迎,国内的云存储目前比较不错的主要有七牛云存储和又拍云存储。
- 上传下载分而治之:图片服务器的图片下载比例远远高于上传比例,业务逻辑的处理也区别明显,上传服器对图片重命名,记录入库信息,下载服务器对图片添加水印、修改尺寸之类的动态处理。从高可用的角度,我们能容忍部分图片下载失败,但绝不能有图片上传失败,因为上传失败,意味着数据的丢失。上传与下载分开,能保证不会因下载的压力影响图片的上传,而且还有一点,下载入口和上传入口的负载均衡策略也有所不同。上传需要经过 Quota Server 记录用户和图片的关系等逻辑处理,下载的逻辑处理如果绕过了前端缓存处理,穿透后端业务逻辑处理,需要从 OSS 获取图片路径信息。近期阿里云会推出基于 CDN 就近上传的功能,自动选择离用户最近的 CDN 节点,使得数据的上传下载速度均得到最优化。相较传统 IDC,访问速度提升数倍。
- 图片防盗链处理:如果服务不允许防盗链,那么访问量会引起带宽、服务器压力等问题。比较通用的解决方案是在 nginx 或者 squid 反向代理软件上添加 refer ACL 判断,OSS 也提供了基于 refer 的防盗链技术。当然 OSS 也提供了更为高级的 URL 签名防盗链,其其实现思路如下:
首先,确认自己的 bucket 权限是 private,即这个 bucket 的所有请求必须在签名认证通过后才被认为是合法的。然后根据操作类型、要访问的 bucket、要访问的 object 以及超时时间,动态地生成一个经过签名的 URL。通过这个签名 URL,你授权的用户就可以在该签名 URL 过期时间前执行相应的操作。
1 | // 签名的Python代码如下: |
其中 method 可以是 PUT、GET、HEAD、DELETE 中的任意一种;最后一个参数“timeout”是超时的时间,单位是秒。一个通过上面 Python 方法,计算得到的签名 URL 为:
http://oss-example.oss-cn-hangzhou.aliyuncs.com/oss-api.jpg?OSSAccessKeyId=44CF9590006BF252F707&Expires=1141889120&Signature=vjbyPxybdZaNmGa%2ByT272YEAiv4%3D
通过这种动态计算签名 URL 的方法,可以有效地保护放在 OSS 上的数据,防止被其他人盗链。
- 图片编辑处理 API:对于在线图片的编辑处理,GraphicsMagick(GraphicsMagick(http://www.graphicsmagick.org/))对于从事互联网的技术人员应该不会陌生。GraphicsMagick 是从 ImageMagick 5.5.2 分支出来的,但是现在他变得更稳定和优秀,GM 更小更容易安装、GM 更有效率、GM 的手册非常丰富 GraphicsMagick 的命令与 ImageMagick 基本是一样的。
GraphicsMagick 提供了包括裁、缩放、合成、打水印、图像转换、填充等非常丰富的接口 API,其中的开发包 SDK 也非常丰富,包括了 JAVA(im4java)、C、C++、Perl、PHP、Tcl、Ruby 等的调用,支持超过 88 中图像格式,包括重要的 DPX、GIF、JPEG、JPEG-2000、PNG、PDF、PNM 和 TIFF,GraphicsMagick 可以再绝大多数的平台上使用,Linux、Mac、Windows 都没有问题。但是独立开发这些图片处理服务,对服务器的 IO 要求相对要高一些,而且目前这些开源的图片处理编辑库,相对来说还不是很稳定,笔者在使用 GraphicsMagick 的时候就遇到了 tomcat 进程 crash 情况,需要手动重启 tomcat 服务。
阿里云目前已经对外开放图片处理 API,包括了大多数常用处理解决方案:缩略图、打水印、文字水印、样式、管道等。开发者可以非常方便的使用如上图片处理方案,希望越来越多的开发者能够基于 OSS 开放出更多优秀的产品。