一、 基本概念
二、为何要分离?
2.1. 负载分担
2.2. 服务器资源最大化利用
2.3 增加浏览器并发连接
2. 4 . 消减Cookie, 减少网络流量
三、 谁在使用?
四、 如何实施?
4.1. 范例
4.2. 前端工程师:合理的目录结构
4.3. web程序开发人员:动态化资源URI前缀解析
4.4. 目录、文件名大小写问题
4.5. 服务器架构人员:安全性及gzip压缩
一、 概念
web组件分离是指将一个 web 站点中不同的资源类型 ( 如 html/cgi/php/asp/jsp/flv, 静态图片 , css 样式文件, js 脚本文件等 ) ,部署到不同服务器 ,并在web 页中使用这些资源的 URL 加载之 。
这是构建可扩展站点的最基本的方式之一。
二 、 为何要分离?
1. 负载分担
单台服务器,其性能往往是有限的( 如主机网络带宽, cpu 运算速度, RAM 大小,磁盘容量及速度,这些都是有限的 ) ,不可能无限承担不断增长的访问压力。那么通过将站点的资源分散到不同的服务器上,从而就达到负载分担的效果。
随着站点规模及访问 量 的增 长 ,通过不断增加服务器即可平稳、低成本扩展 之 。
2. 服务器资源最大化利用
不同的资源请求类型,对主机的性能要求有所不同 。
静态文件如html/jpeg/png/css/js/flv 等静态文件,是典型的 IO 密集型访问,无须消耗太多 CPU 资源,也无须消耗太多内存,需要大量空间及较快的 IO 访问速度及吞吐率,也需要服务器有较高的并发处理能力。 (Nginx+ReiserFS)
为何需要较高的并发处理能力? 因为 浏览器 在对web 页的每一次 请求 中,静态资源占据绝对的多数, 那么静态资源就需要较高的服务器并发处理能力。
可使用firebug/HttpWatch 之类的工具跟踪浏览器的行为得到这个结论。
动态文件如asp/php/jsp/cgi 对服务器有何要求?很显然,因为是动态程序,那么须经过 CPU 参与大量运算, 可能 会消耗较多的内存,不会消耗太多磁盘空间, 对服务器并发能力的要求比静态文件要小。这就是典型的 CPU 密集型访问。
那么动态程序对服务器性能需求特点是:快速的CPU, 充足的内存 , 对 IO 性能无太高要求。
内存缓存主机,如memcache, 显然需要消耗大量内存空间,但对 CPU 与磁盘 IO 并无特殊要求。
磁盘缓存主机,如squid/varnish ,显然需要消耗大量磁盘空间,那么对磁盘的 IO 性能有较强要求。
数据库主机,高速cpu ,大量内存及较好的磁盘 IO 性能。
实际上,后面三种(内存缓存/ 磁盘缓存 / 数据库)应该属于后台架构,在这里一并列出也是为了让大家理解不同的资源请求对主机性能要求有所不同。
3. 消减cookie 数据,减少网络流量,提高响应速度
前提是web 页使用了 cookie 。
当某个服务器端程序向浏览器发送Set-Cookie 指令(非 设置 cookie 过期 指令)时,在后续的访问中,浏览器对该域名下所有资源的访问都将发送cookie 数据 ( 除非cookie 过期失效) 。
那么对www.domain-name.com/skin.css 的请求,浏览器同样会携带 cookie, 但对这些静态文件,传回 cookie 是没有意义的。你可能觉得传回的 cookie 数据很小,所以可以忽略带来的网络流量消耗,但是通过下面的事实分析,让你认识到它消耗的网络流量。
单个原子的能量很小以至于无法探测其能量的存在,但大量原子能量集合即成为原子弹的能量。
以120ask 首页为例,清除浏览器所有缓存, cookie, 刷新首页 ( 这时是未登录状态 ) 并使用 firebug 跟踪,我们得出以下结果:
1. 在开始的请求中,浏览没有传回任何 cookie 数据
2. 最后的 资源 请求传回了cookie 数据:
playVDate=2010-9-13; __utma=75435853.518655482.1284350348.1284350348.1284356261.2; __utmc=75435853; __utmz=75435853.1284350348.1.1.utmcsr=(direct)|utmccn=(direct)|utmcmd=(none); __utmb=75435853.1.10.1284356261
上述cookie 数据 共有209 字节,加上浏览器请求 header 的 Cookie: ,共 209+7=216 字节。
假设对每个页面的访问,平均产生80 个静态资源请求,日 PV 量为 500 万,则这些 cookie 消耗的网络流量为:
216*80*500万 = 82397 MB = 80 GB
即产生的额外流量是 80 GB/ 天 , 已经是相当大的值了。
登录状态下,cookie 内容:
playVDate=2010-9-13; __utma=75435853.1912569422.1284356400.1284356400.1284356400.1; __utmb=75435853.4.10.1284356400; __utmc=75435853; __utmz=75435853.1284356400.1.1.utmcsr=(direct)|utmccn=(direct)|utmcmd=(none); ask1user=ask1user1useralias=&ask1user1partition=99&ask1user1askclinicid=&ask1user1useremail=fty%5Fbeing%40yahoo%2Ecn&ask1user1userpass=120ask0a57eae15110b1565a481410bea8ef74&ask1user1usertype=1&ask1user1username=kosee&ask1user1userid=9859507; ASPSESSIONIDCAQQRQRB=DAJDKGBCCMHPFODGKFCOGGIN; showTipsDate=2010-9-13; ASPSESSIONIDAACAASDD=BJBMKNNCPKJBLCKEDKJPHFNC
长度为571+7=578 字节,假设登录状态下日均 PV 为 100 万,则总值为:
578*80*100万 = 43 GB
如果上面的PV 量假设接近事实,那么,每天仅在 Cookie 上消耗的网络流量应该 43 —— 80GB之间。
通过这个事实可以说明: 即使 同一台服务器,使用不同的域名链接静态文件,也是有意义 的 ,因为 它 避免了浏览器请求静态文件时传回cookie!
所以,你也就该明白,为何大型站点均使用一些比较奇怪的域名(而不是多级域名)来链接图片资源了。
2.4. 提高浏览器并发数
当用浏览器打开一个网页时,浏览器先下载网页本身(即HTML ) , 再逐步下载 web 页中的其它资源( css/js/ 图片) , 然而浏览器 对同一个域 名下的资源 下载有默认的并发数限制(浏览器作以限制, 主要是为了避免网络拥塞):
主流浏览器最大并发数 | ||
浏览器 | HTTP 1.1 | HTTP 1.0 |
IE 6,7 | 2 | 4 |
IE 8 | 6 | 6 |
Firefox 2 | 2 | 8 |
Firefox 3 | 6 | 6 |
Safari 3/4 | 4 | 4 |
Chrome 1/2 | 6 | / |
Opera 9/10 | 4 | 4 |
也就是说,如果同一个域名下有较多资源需要逐个下载,那么后面的资源只能等前面的资源下载完成(或连接超时)后才能开始下载,否则后面的这些资源,就一直处于“阻塞”状态。
另一个可选在线工具:http://site-perf.com, 也可测试 web
2.5. 实施成本很低
所有的一切,都是为了最终的目标:在资源有限的情况下,实现价值最大化。
那么是否具有可行性,就得衡量成本与收益的比例关系了。
而替换 html中静态资源链接路径为该资源的 URL 地址即可,几乎没有技术难度。
三 、 谁在使用?
所有中大型站点 ,均使用了此方式来构建他们的站点
使用firefox 跟踪 youku 的首页,部分截图:
四 、 如何实施?
(html 页面中的外链资源最好以 / 开始,避免改动时资源混淆,样式中的图片资源是以样式文件位置为参照的,所以要用相对路径,动态文件好办了,使用变量前缀即可)
4.1 范例
非常容易实施:使用其它域名将需要的静态资源链接到HTML 中即可 , 如以前的 html 代码为:
<img src="/images/small.gif" />
修正以后应该是:
<img src="http://domain-name.com/small.gif" />
如果静态资源与 web页位于同一域名下,强烈建议静态资源链接地址以 / 开始。 因为 web 页生成静态后可能是保存到子目录中。
总结原则:web 页中静态资源链接路径使用域名前缀或 / 是个好办法,而使用 ./, ../ 之类的链接路径说明 web 目录规划存在问题。
web分离组件的分离工作,至少应该有这三种角色人员参与:
web前端工程师 HTML/CSS 模板制作者
web开发人员 将前端工程师制作的模板按一定要求套入到动态程序中
服务器运维人员 负载服务器配置
通过实践总结,针对这三种角色人员,我们对其提供的 参考 建议:
4. 2 . 前端工程师的注意事项:
4. 2 .1. 应该合理规划目录结构 , 可参照 下目录结构:
几个基本要素是:
a. html代码中链接静态文件时,一律使用相对路径,不建议不使用 /, ../, ./ 等等方式。如
<link href="style/base.css" rel="stylesheet" />
<script type="text/javascript" src="javascript/file.js"></script>
<img src="images/small.gif" />
而以下代码均是不被推荐的:
<link href="../style/base.css" rel="stylesheet" />
<script type="text/javascript" src="/javascript/file.js"></script>
<img src="../images/small.gif" />
如此的 后 果就是让html 文件 css/js/ 图片文件位置高度耦合,带来后期调整的困难 (因为后期 web 页面可能需要迁移到多级子目录中 ) 。
同样,css 文件中,也不应该出现 /, ../ 之类的路径。
background:url(images/bg/line.gif); //这是推荐的
background:url(/images/bg/line.gif); //这是不被推荐的
background:url( http://www.domain-name.com /images/bg/line.gif); //视具体情况而定
background:url(./images/line.gif) ; //尚可接受 , 与 background:url(images/bg/line.gif) 效果等同。
4.1.2. <img> 标记使用到的图片与 css 所使用到的图片,存入不同目录中。会给图片后期管理带来极大便利。
如上述目录结构中,<img> 标记使用到的图片一律存放在 /images/ 目录下,而样式用到的图片存放在 /style/images/ 下。如果 <img> 和 CSS 用到同一个图片,建议将此图片复制出两份,放入 它们各自 目录中 , 最大程度降低耦合性。
4. 3 web开发人员的注意事项:
考虑使用参数表示静态资源的可变前缀, 而不是在程序中硬编码静态文件路径, 导致静态资源文件迁移时,不得不对所有模板作批量路径替换 , 成本很高!!!
参考方法:
假设前期的目录结构如下:
/images/ 保存以<img> 标记链接的图片资源
/js/ 保存了所有js 文件
/style/ css文件所在位置
/style/images/ css所用到的图片保存位置
而web 页中,链接这三种常见资源的代码:
<img src="images/small.gif" />
<script type="text/javascript" src="js/file.js"></script>
<link href="style/base.css" rel="stylesheet" type="text/css" />
我们所希望的是:
web开发人员的程序具有模板分析功能,即对 web 模板中 <img src="images/*.(gif|png|jpg|jpeg)" , href="style/*.css", src="js/*.js" 之类样式的链接路径可以动态替换之,加上其 URI 前缀 , 也就是说:
a. 浏览器访问时 这些 web页时 ,这些路径是解析过的,变成:
<img src=" / images/small.gif" />
<script type="text/javascript" src=" / js/file.js"></script>
<link href=" / style/base.css" rel="stylesheet" type="text/css" />
即自动添加了资源的URI 前缀,这们做的好处是当 web 页位于子目录中时,静态资源链接路径永远正确 , 不会发生错乱。
b. 如果这些静态资源被部署到其它域名下,那么应该能轻松实现迁移,即资源的链接路径应该能解析为:
<img src=" http://domain-name.com/static/ images/small.gif" />
<script type="text/javascript" src=" http://domain-name.com/static/ js/file.js"></script>
<link href=" http://domain-name.com/static/ style/base.css" rel="stylesheet" type="text/css" />
关键的PHP 实现代码参考:
定义静态资源URI 前缀
$img_url = 'http://domain-name.com/static/'; 或 /
$js_url = "http://domain-name.com/static/"; 或 /
$css_url = "http://domain-name.com/static/"; 或 /
模板解析关键实现之处( 假设 $content 是模板的内容 ) :
$content = preg_replace("|src=\"js/|",'src="'.$js_url, $content) ;
$content = preg_replace("|src=\"images/|",'src="'.$img_url,$content) ;
$content = preg_replace("|href=\"css/|",'href="'.$css_url,$content) ;
那么,后期静态资源迁移时,只需要修改资源URI 前缀,并重新生成模板缓存及遗留的静态 html 文件即可。
或使用变量替代方式:
<img src=" <?=$img_url?> small.gif" />
<script type="text/javascript" src=" <?=$js_url?> file.js"></script>
<link href=" <?=$css_url?> base.css" rel="stylesheet" type="text/css" />
4.4.
重要的原则:静态资源的链接路径大小写与原文件的大小写完全一致
因为windows 是不区分大小写的,但主流的 Linux/FreeBSD 却是区分大小写的,如果不注意大小写,可能就导致 windows 系统上开发系统 , 在Linux 无法顺利运行。
我们的经验是使用统一的小写形式,仅供参考。
4. 5 . 服务器运维人员注意事项:
3.1. 静态资源服务器上关闭动态脚本解析模块,以提高 http server 性能及安全性。
3.2. 针对文本文件资源( html/asp/php/jsp/css/js ),可考虑启用 http server 的 GZIP 压缩功能。实践证明,一般的 web 页可压缩到 原来体积的三分之一以下。