11 月 19 - 20 日 Apache Pulsar 社区年度盛会来啦,立即报名! 了解详情
写点什么

Windows 平台网站图片服务器架构的演进

  • 2014-05-11
  • 本文字数:4667 字

    阅读完需:约 15 分钟

构建在 Windows 平台之上的网站,往往会被业内众多架构师认为很“保守”。很大部分原因,是由于微软技术体系的封闭和部分技术人员的短视造成的。由于长期缺乏开源支持,所以只能“闭门造车”,这样很容易形成思维局限性和短板。就拿图片服务器为例子,如果前期没有容量规划和可扩展的设计,那么随着图片文件的不断增多和访问量的上升,由于在性能、容错 / 容灾、扩展性等方面的设计不足,后续将会给开发、运维工作带来很多问题,严重时甚至会影响到网站业务正常运作和互联网公司的发展(这绝不是在危言耸听)。

之所以选择 Windows 平台来构建网站和图片服务器,很大部分由创始团队的技术背景决定的,早期的技术人员可能更熟悉.NET,或者负责人认为 Windows/.NET 的易用性、“短平快”的开发模式、人才成本等方面都比较符合创业初期的团队,自然就选择了 Windows。后期业务发展到一定规模,也很难轻易将整体架构迁移到其它平台上了。当然,对于构建大规模互联网,更建议首选开源架构,因为有很多成熟的案例和开源生态的支持,避免重复造轮子和支出授权费用。对于迁移难度较大的应用,比较推荐 Linux、Mono、Mysql、Memcahed……混搭的架构,同样能支撑高并发访问和大数据量。

单机时代的图片服务器架构(集中式)

初创时期由于时间紧迫,开发人员水平也很有限等原因。所以通常就直接在 website 文件所在的目录下,建立 1 个 upload 子目录,用于保存用户上传的图片文件。如果按业务再细分,可以在 upload 目录下再建立不同的子目录来区分。例如:upload\QA,upload\Face 等。

在数据库表中保存的也是”upload/qa/test.jpg”这类相对路径。

用户的访问方式如下:

http://www.yourdomain.com/upload/qa/test.jpg

程序上传和写入方式:

程序员 A 通过在 web.config 中配置物理目录 D:\Web\yourdomain\upload 然后通过 stream 的方式写入文件;

程序员 B 通过 Server.MapPath 等方式,根据相对路径获取物理目录 然后也通过 stream 的方式写入文件。

优点:实现起来最简单,无需任何复杂技术,就能成功将用户上传的文件写入指定目录。保存数据库记录和访问起来倒是也很方便。

缺点:上传方式混乱,严重不利于网站的扩展。

针对上述最原始的架构,主要面临着如下问题:

  1. 随着 upload 目录中文件越来越多,所在分区(例如 D 盘)如果出现容量不足,则很难扩容。只能停机后更换更大容量的存储设备,再将旧数据导入。
  2. 在部署新版本(部署新版本前通过需要备份)和日常备份 website 文件的时候,需要同时操作 upload 目录中的文件,如果考虑到访问量上升,后边部署由多台 Web 服务器组成的负载均衡集群,集群节点之间如果做好文件实时同步将是个难题。

集群时代的图片服务器架构(实时同步)

在 website 站点下面,新建一个名为 upload 的虚拟目录,由于虚拟目录的灵活性,能在一定程度上取代物理目录,并兼容原有的图片上传和访问方式。用户的访问方式依然是:

http://www.yourdomain.com/upload/qa/test.jpg

优点:配置更加灵活,也能兼容老版本的上传和访问方式。

因为虚拟目录,可以指向本地任意盘符下的任意目录。这样一来,还可以通过接入外置存储,来进行单机的容量扩展。

缺点:部署成由多台 Web 服务器组成的集群,各个 Web 服务器(集群节点)之间(虚拟目录下的)需要实时的去同步文件,由于同步效率和实时性的限制,很难保证某一时刻各节点上文件是完全一致的。

基本架构如下图所示:

从上图可看出,整个 Web 服务器架构已经具备“可扩展、高可用”了,主要问题和瓶颈都集中在多台服务器之间的文件同步上。

上述架构中只能在这几台 Web 服务器上互相“增量同步”,这样一来,就不支持文件的“删除、更新”操作的同步了。

早期的想法是,在应用程序层面做控制,当用户请求在 web1 服务器进行上传写入的同时,也同步去调用其它 web 服务器上的上传接口,这显然是得不偿失的。所以我们选择使用 Rsync 类的软件来做定时文件同步的,从而省去了“重复造轮子”的成本,也降低了风险性。

同步操作里面,一般有比较经典的两种模型,即推拉模型:所谓“拉”,就是指轮询地去获取更新,所谓推,就是发生更改后主动的“推”给其它机器。当然,也可以采用加高级的事件通知机制来完成此类动作。

在高并发写入的场景中,同步都会出现效率和实时性问题,而且大量文件同步也是很消耗系统和带宽资源的(跨网段则更明显)。

集群时代的图片服务器架构改进 (共享存储)

沿用虚拟目录的方式,通过 UNC(网络路径)的方式实现共享存储(将 upload 虚拟目录指向 UNC)

用户的访问方式 1:

http://www.yourdomain.com/upload/qa/test.jpg

用户的访问方式 2(可以配置独立域名):

http://img.yourdomain.com/upload/qa/test.jpg

支持 UNC 所在 server 上配置独立域名指向,并配置轻量级的 web 服务器,来实现独立图片服务器。

优点: 通过 UNC(网络路径)的方式来进行读写操作,可以避免多服务器之间同步相关的问题。相对来讲很灵活,也支持扩容 / 扩展。支持配置成独立图片服务器和域名访问,也完整兼容旧版本的访问规则。

缺点 :但是 UNC 配置有些繁琐,而且会造成一定的(读写和安全)性能损失。可能会出现“单点故障”。如果存储级别没有 raid 或者更高级的灾备措施,还会造成数据丢失。

基本架构如下图所示:

在早期的很多基于 Linux 开源架构的网站中,如果不想同步图片,可能会利用 NFS 来实现。事实证明,NFS 在高并发读写和海量存储方面,效率上存在一定问题,并非最佳的选择,所以大部分互联网公司都不会使用 NFS 来实现此类应用。当然,也可以通过 Windows 自带的 DFS 来实现,缺点是“配置复杂,效率未知,而且缺乏资料大量的实际案例”。另外,也有一些公司采用 FTP 或 Samba 来实现。

上面提到的几种架构,在上传 / 下载操作时,都经过了 Web 服务器(虽然共享存储的这种架构,也可以配置独立域名和站点来提供图片访问,但上传写入仍然得经过 Web 服务器上的应用程序来处理),这对 Web 服务器来讲无疑是造成巨大的压力。所以,更建议使用独立的图片服务器和独立的域名,来提供用户图片的上传和访问。

独立图片服务器 / 独立域名的好处

  • 图片访问是很消耗服务器资源的(因为会涉及到操作系统的上下文切换和磁盘 I/O 操作)。分离出来后,Web/App 服务器可以更专注发挥动态处理的能力。
  • 独立存储,更方便做扩容、容灾和数据迁移。
  • 浏览器(相同域名下的)并发策略限制,性能损失。
  • 访问图片时,请求信息中总带 cookie 信息,也会造成性能损失。
  • 方便做图片访问请求的负载均衡,方便应用各种缓存策略(HTTP Header、Proxy Cache 等),也更加方便迁移到 CDN。

我们可以使用 Lighttpd 或者 Nginx 等轻量级的 web 服务器来架构独立图片服务器。

当前的图片服务器架构(分布式文件系统 +CDN)

在构建当前的图片服务器架构之前,可以先彻底撇开 web 服务器,直接配置单独的图片服务器 / 域名。但面临如下的问题:

  1. 旧图片数据怎么办?能否继续兼容旧图片路径访问规则?
  2. 独立的图片服务器上需要提供单独的上传写入的接口(服务 API 对外发布),安全问题如何保证?
  3. 同理,假如有多台独立图片服务器,是使用可扩展的共享存储方案,还是采用实时同步机制?

直到应用级别的(非系统级) DFS(例如 FastDFS HDFS MogileFs MooseFS、TFS)的流行,简化了这个问题:执行冗余备份、支持自动同步、支持线性扩展、支持主流语言的客户端 api 上传 / 下载 / 删除等操作,部分支持文件索引,部分支持提供 Web 的方式来访问。

考虑到各 DFS 的特点,客户端 API 语言支持情况 (需要支持 C#),文档和案例,以及社区的支持度,我们最终选择了 FastDFS 来部署。

唯一的问题是:可能会不兼容旧版本的访问规则。如果将旧图片一次性导入 FastDFS,但由于旧图片访问路径分布存储在不同业务数据库的各个表中,整体更新起来也十分困难,所以必须得兼容旧版本的访问规则。架构升级往往比做全新架构更有难度,就是因为还要兼容之前版本的问题。(给飞机在空中换引擎可比造架飞机难得多)

解决方案如下:

首先,关闭旧版本上传入口(避免继续使用导致数据不一致)。将旧图片数据通过 rsync 工具一次性迁移到独立的图片服务器上(即下图中描述的 Old Image Server)。在最前端 (七层代理,如 Haproxy、Nginx) 用 ACL(访问规则控制),将旧图片对应 URL 规则的请求(正则)匹配到,然后将请求直接转发指定的 web 服务器列表,在该列表中的服务器上配置好提供图片(以 Web 方式)访问的站点,并加入缓存策略。这样实现旧图片服务器的分离和缓存, 兼容了旧图片的访问规则并提升旧图片访问效率,也避免了实时同步所带来的问题。

整体架构如图:

基于 FastDFS 的独立图片服务器集群架构,虽然已经非常的成熟,但是由于国内“南北互联”和 IDC 带宽成本等问题(图片是非常消耗流量的),我们最终还是选择了商用的 CDN 技术,实现起来也非常容易,原理其实也很简单,我这里只做个简单的介绍:

将 img 域名 cname 到 CDN 厂商指定的域名上,用户请求访问图片时,则由 CDN 厂商提供智能 DNS 解析,将最近的(当然也可能有其它更复杂的策略,例如负载情况、健康状态等)服务节点地址返回给用户,用户请求到达指定的服务器节点上,该节点上提供了类似 Squid/Vanish 的代理缓存服务,如果是第一次请求该路径,则会从源站获取图片资源返回客户端浏览器,如果缓存中存在,则直接从缓存中获取并返回给客户端浏览器,完成请求 / 响应过程。

由于采用了商用 CDN 服务,所以我们并没有考虑用 Squid/Vanish 来重复构建前置代理缓存。

上面的整个集群架构,可以很方便的做横向扩展,能满足一般垂直领域大型网站的图片服务需求(当然,像 taobao 这样超大规模的可能另当别论)。经测试,提供图片访问的单台 Nginx 服务器(至强 E5 四核 CPU、16G 内存、SSD),对小静态页面(压缩后的)可以扛住上万的并发且毫无压力。当然,由于图片本身体积比纯文本的静态页面大很多,提供图片访问的服务器的抗并发能力,往往会受限于磁盘的 I/O 处理能力和 IDC 提供的带宽。Nginx 的抗并发能力还是非常强的,而且对资源占用很低,尤其是处理静态资源,似乎都不需要有过多担心了。可以根据实际访问量的需求,通过调整 Nginx 参数,Linux 内核调优、缓存策略等手段做更大程度的优化,也可以通过增加服务器或者升级服务器配置来做扩展,最直接的是通过购买更高级的存储设备和更大的带宽,以满足更大访问量的需求。

值得一提的是,在“云计算”流行的当下,也推荐高速发展期间的网站,使用“云存储”这样的方案,既能帮你解决各类存储、扩展、备灾的问题,又能做好 CDN 加速。最重要的是,价格也不贵。

总结,有关图片服务器架构扩展,大致围绕这些问题展开:

  1. 容量规划和扩展问题。
  2. 数据的同步、冗余和容灾。
  3. 硬件设备的成本和可靠性(是普通机械硬盘,还是 SSD,或者更高端的存储设备和方案)。
  4. 文件系统的选择。根据文件特性(例如文件大小、读写比例等)选择是用 ext3/4 或者 NFS/GFS/TFS 这些开源的(分布式)文件系统。
  5. 图片的加速访问。采用商用 CDN 或者自建的代理缓存、web 静态缓存架构。
  6. 旧图片路径和访问规则的兼容性,应用程序层面的可扩展,上传和访问的性能和安全性等。

作者介绍

丁浪,技术架构师。擅长大规模(高并发、高可用、海量数据)互联网架构,专注于打造“高性能,可扩展 / 伸缩,稳定,安全”的技术架构。 热衷于技术研究和分享,曾分享和独立撰写过大量技术文章。


感谢崔康对本文的审校。

给InfoQ 中文站投稿或者参与内容翻译工作,请邮件至 editors@cn.infoq.com 。也欢迎大家通过新浪微博( @InfoQ )或者腾讯微博( @InfoQ )关注我们,并与我们的编辑和其他读者朋友交流。

2014-05-11 23:154209

评论

发布
暂无评论
发现更多内容

TiDB v6.0.0(DMR) 缓存表初试

TiDB 社区干货传送门

6.x 实践

体验 TiDB v6.0.0 之 Clinic

TiDB 社区干货传送门

实践案例 6.x 实践

TiDB 生态工具 -- TiUniManager(原 TiEM)v1.0.0 体验

TiDB 社区干货传送门

6.x 实践

TiDB 集群一次诡异的写入慢问题排查经历

TiDB 社区干货传送门

故障排查/诊断

新版 TiDB 社区技术月刊,一站式 Get 社区全动态

TiDB 社区干货传送门

社区活动 故障排查/诊断 数据库架构设计 应用适配

Let's go, TiCheck!

TiDB 社区干货传送门

监控

TiDB 6.0 新特性解读 | TiFlash 新增算子和函数下推

TiDB 社区干货传送门

6.x 实践

TiDB Numa 性能压测

TiDB 社区干货传送门

版本测评 性能测评

TiDB 6.0 的「元功能」:Placement Rules in SQL 是什么?

TiDB 社区干货传送门

6.x 实践

TiDB 查询优化及调优系列(二)TiDB 查询计划简介

TiDB 社区干货传送门

TiDB 6.0 新特性解读 | Collation 规则

TiDB 社区干货传送门

6.x 实践

一篇文章说透缓存表

TiDB 社区干货传送门

TiDB 源码解读 新版本/特性解读 6.x 实践

体验 TiDB v6.0.0 之 TiDB 的数据迁移工具 DM-WebUI

TiDB 社区干货传送门

实践案例 6.x 实践

TiDB 查询优化及调优系列(一)TiDB 优化器简介

TiDB 社区干货传送门

TiDB上百T数据拆分实践

TiDB 社区干货传送门

迁移 管理与运维

tiup修改参数显示成功但不生效

TiDB 社区干货传送门

MVCC导致limit 1执行慢测试

TiDB 社区干货传送门

实践案例 管理与运维 性能测评

TiDB 5.1 Write Stalls 应急文档

TiDB 社区干货传送门

实践案例

tidb-v5.2.3内存使用率高的几个case

TiDB 社区干货传送门

TiKV缩容不掉如何解决?

TiDB 社区干货传送门

集群管理 故障排查/诊断 扩/缩容

TiDB 6.0 Placement Rules In SQL 使用实践

TiDB 社区干货传送门

管理与运维 版本测评 新版本/特性解读 6.x 实践

一个小操作,SQL查询速度翻了1000倍。

TiDB 社区干货传送门

性能调优 实践案例 管理与运维 故障排查/诊断

select查询失败,报“no such file or directory”错误

TiDB 社区干货传送门

TiFlash 源码阅读(一) TiFlash 存储层概览

TiDB 社区干货传送门

我和tidb 的故事 - 我们终会在平行世界相遇

TiDB 社区干货传送门

体验TiDB v6.0.0 之TiCDC

TiDB 社区干货传送门

实践案例 6.x 实践

记一次tidb离线环境下安装非本地镜像源组件的过程

TiDB 社区干货传送门

实践案例 管理与运维 安装 & 部署 应用适配

TiEM初级实践

TiDB 社区干货传送门

6.x 实践

用一个性能提升了666倍的小案例说明在TiDB中正确使用索引的重要性

TiDB 社区干货传送门

性能调优 实践案例 应用适配

一次 TiDB 5.1 Write Stall 问题处理

TiDB 社区干货传送门

故障排查/诊断

TiDB 4.0 升级 5.1 二三事——避坑指南

TiDB 社区干货传送门

版本升级

Windows平台网站图片服务器架构的演进_Windows_丁浪_InfoQ精选文章