构架.性能分类的最新文章

《High Performance Web Sites》 :Chapter 5

法则5:将样式表放到HEAD中 Put Stylesheets at the Top

  在Yahoo!,有一个小组在他们的项目中使用了一些DHTML特性。其中一个复杂的功能是是在发送邮件时会弹出一个DIV层。其实这个层并不影响其所在页面的渲染,因为它是功能触发式的被渲染的页面部分,所以这个小组的前端工程师就把这个弹出层的CSS样式写到一个独立的样式表文件中,并把对该CSS的引用放在页面的最下方的位置,以确保页面能较快的下载和渲染。
  我们知一个页面是由图片,样式表,scripts等组件渲染成的,而组件是按它们在页面中出现的顺序依次下载,把DHTML的动态功能的样式表文件放到后面,可以让更关健的组件先下载,这样能使页面更快的渲染显示给浏览者,好像事实应该是这样,不是吗。

事实是这样吗?

  其实采用上面的方式在IE下反而会更慢。经过不断尝试,我们还是把这个DHTML所需的样式表放到了页面的上方HEAD中,这时的页面载入是最快的。这好像和我们所期望的有点相反,为什么我们把暂不渲染页面的CSS放到页面的上方位置,延缓了其它关健组件的下载,反而是加快了页面的载入速度呢?这就是我们马上要开始介绍的法则5。

Progressive Rendering
逐步渲染


  前端工程师总是希望他的页面能尽可能快的展示给用户,所以他们希望页面能够随着下载逐步渲染给用户,特别是在页面比较大或是用户使用较慢的网络连接时。有人曾经讨论过给用户一个视觉反馈的重要性,提出要用视觉反馈来作为衡量进度的指标:让用户实实在在的感受到页面一块一块的渲染出来和让用户等着一个空白页让各组件下载到本地再渲染,可能前一种方法的实际组件下载完的速度要慢,但用户会觉得这要快的多。

  把样式表放到页面的下方,在一些浏览器中(包括IE)会阻止我们上面所期望的逐步渲染。因为浏览器如果发现页面中的下方有样式表的引用,就会不进行逐步的渲染以避免样式表的的变更所带来的重新渲染,所以这个时候用户可能会看到一个空白页(其实可能网速并不慢,服务响应也够快,但您还是等一个空白页的过程)。Firefox则不会这样。

  在HTML规范中就清楚的说明了样式表要声明或引用在页面的HEAD中:"Unlike A, [LINK] may only appear in the HEAD section of a document, although it may appear any number of times."

  下面我们用数据来分析:
  CSS在页面的下方:http://stevesouders.com/hpws/css-bottom.php
  CSS在HEAD中:http://stevesouders.com/hpws/css-top.php
  CSS在HEAD中,并用 @import的方式引入:http://stevesouders.com/hpws/css-top-import.php
(注意,这种用@import引入的方式,即使您将css写在HEAD中,也会出现类似将css放在页面下方的情况,出现一个空白的页等待中)
  以上的三个页面中,组件和个数都一样。看看下面的三个页面的组件下载时序图:
5-1.gif

  第一个和第三个,都是在最后才下载样式表,这样就会导致在IE中,页面在6.3秒前都是不会被渲染的,而第二个页面,会最先下载样式表,这时页面就会开始渲染了。
  
  现在我们知道该怎么做了。把样式表以link文件的形式放进HEAD吧。 

拿棍子捅服务器

| | Comments(0) | TrackBacks(0)

  最近太忙了。

  所负责的系统目前流量增长的很快,应用响应量已经是去年同期的两倍有余,现在最重要的工作是要找到系统的瓶颈,并制定一个更好的扩展方案。

  这个星期就像是在拿棍子捅服务器,结果数据哗的一下全流出来了。

  犯困中zzzZZZzzZZZZZ......

《High Performance Web Sites》 :Chapter 4

法则4:使用Gzip压缩组件  Gzip Components

  前端开发工程师能力的好坏直接关系着页面的访问快慢(John:要知道,用极其丑陋的table套table做出的页面比简洁的div与table做出的页面无论是从K字节上,还是载入速度上都是极大的消耗),但另一些如用户的带宽等我们无法控制的因素,也会影响到用户访问您web应用的速度。法则1法则3介绍了如何通过减少不必要的HTTP请求来提升页面加载时间,法则2介绍了用CDN,可以让我们离用户更近,但我们总不能把所有的HTTP请求都优化掉吧。于是,我们这法则4应潮流而生。

  法则4,是通过减少HTTP响应的数据量来加快响应速度。要知道如果一个HTTP的响应足够少,那传输的时间多快呀,因为从服务器端传送到浏览器端的数据包小了,也少了,岂有不快之理。这对像那些网络不太好的用户可是特有效。这一章节就会展示如何通过gzip去压缩HTTP响应,这是减少数据量最easy的方法,但有得到就会有付出,gzip也会有一些不好的影响,这个后面介绍。


压缩是如何工作的


  很久以前我们就把文件压缩应用在电子邮件和FTP站点当中。直到HTTP/1.1的规范制定,web客户端才开始通过HTTP请求中的 Accept-Encoding 头信息来来表示其支持压缩。
      如果web服务器在请求信息中看到这个头信息,它就可以通过响应的 Content-Encoding头信息来返回服务器可用的压缩方式。
  gzip是目前最流行和高效的压缩方式。它是一个可以自由使用,不受专利权限制的压缩格式,是由GNU项目开发出来并定义在RFC1952规范中。另外还有一种压缩方式是deflate,但它不如Gzip那么常用和高效,其实上,我只发现一家网站在使用deflate:msn.com 。而支持deflate数据压缩的浏览器,也支持Gzip,所以Gzip是非常好的选择。


该压缩什么


  服务器要压缩什么是取决于文件格式。很多网站只压缩HTML文件,其实script和样式表文件的也都值得压缩(事实上,任何文本型的数据都可以压缩,包括XML和JSON数据)。图片和PDF文件的则不必再用gzip压缩,因为它们本身就是压缩格式,再多余的压缩只会浪费CPU资源,也得不到多少的文件压缩量。
 
  gzip压缩需要消耗服务器端的CPU,而客户端也需要执行解压gzip格式的数据。至于是否值得压缩的问题,我觉得任何超过1K或2K的文件都值得压缩。mod_gzip_minimum_file_size 参数就可以配置您想要压缩的文件的大小范围,它的默认值是500 bytes.

  老套路,我又调查了美国10大网站使用gzip的情况如下图。有9家都用gzip压缩他们的HTML文件,7家还用gzip压缩了script和样式表文件,其中有5家是完全压缩了所有的script和样式表。一个网站如果压缩所有的HTML文件,样式表,script文件,甚至可以减少70%的数据量,这是我们下面的段落里会提到的。
节省The Savings

  gzip压缩后,返回的数据量一般会减少70%,如下图所示,显示了scrtip,样式表文件在压缩前和压缩后的数据量的变化,同时也显示了如果用deflate方式来压缩的数据量对比。
  很显然,从上图中就知道我们为什么要选择gzip了。gzip会减少约66%的数据量,而deflate只会减少约60%。


配置 Configuration

  配置gzip的模块要取决于您Apache的版本:Apache 1.3使用的是mod_gzip,而 Apache 2.x 使用 的是mod_deflate。这一段落就会讲解如何配置每个模块。

Apache 1.3: mod_gzip

  Apache1.3的gzip压缩功能是通过 mod_gzip 模块来现实的。mod_gzip 有很多的配置参数,这些在mod_gzip的站点上有说明,在这里我介绍一些最常用的参数。

  mod_gzip_on
    开启mod_gzip.
  mod_gzip_item_include
  mod_gzip_item_exclude
         定义要被gzip压缩或不压缩的文件的类型,MIME类型, user agent, 等等。

  大部分的web服务器都会打开mod_gzip,并把text/html设为默认文件类型。最重要的是您应该为script与样式表文件也打开gzip功能。您可以参考下面对Apache 1.3 的配置:

  mod_gzip_item_include  file    \.js$
  mod_gzip_item_include  mime  ^application/x-javascript$
  mod_gzip_item_include  file    \.css$
  mod_gzip_item_include  mime  ^text/css$

  gzip的命令行程序可以控制压缩的级别以及CPU的使用率,但在mod_gzip中却无法配置这些。有时对数据流的压缩会造成CPU的过分负荷,我们可以选择缓存压缩后的响应数据到磁盘或是内存中,如果要手动实现这一功能就太麻烦了,还好,我们可以通过mod_gzip来自动保存压缩后数据到磁盘上,还可以及时的响应更新。实现这一功能则要配置 mod_gzip_can_negotiate 和 mod_gzip_update_static 参数。

Apache 2.x: mod_deflate


  Apache 2.x 是通过mod_deflate 模块来实现压缩。尽管它的名字是deflate,但实际它是实现的gzip压缩方式(John:怪异吧!)。如果要实现像对script和样式表的压缩这种基本的配置只需要下面这一行就够了:
         AddOutputFilterByType   DEFLATE      text/html    text/css    application/x-javascript
  与mod_gzip不同,mod_deflate可以通过参数配置来控制压缩级别。要获取更多的信息,可查阅Apache 2.0 mod_deflate的文档。

代理缓存  Proxy Caching

  以上介绍的简单配置对直接访问web服务器的方式来说没有一点问题,web服务器通过请求中的Accept-Encoding头信息来判断客户端是否可以支持压缩的数据,然后把压缩或没压缩的数据再返回给客户端,这些都是通过HTTP的header头信息来实现的。

  但如果用户是通过代理上网的,那用户的请求也都是通过代理服务器中转发送的,这时就比较麻烦了:假如用户发请求给代理服务器时,并表明其不支持gzip,而代理服务器再中转数据时却将已经缓存过的压缩的数据返回给用户,这时就会造成用户看到的是一堆乱码,反之亦然。
  要解决这个问题则要在服务器端加上vary头信息。mod_gzip默认是为所有的响应都增加Vary:Accept Encoding头信息,这样代理服务器就会缓存压缩和未压缩版的数据了。


特殊情况 Edge Cases

  目前90%的浏览器都支持gzip。但还是会有一些特殊情况我们不得不考虑,如在早期的IE版本中,特别是IE5.5和IE6.0 sp1存在一些bug,一种比较安全的做法是用"浏览器白名单(browser whitelist)",让服务器端只针对一部分浏览器发送gzip格式的数据。如下的设置就是只针对IE6~9的版本和Mozila 5~9的版本发送gzip数据。
 
  Apache 1.3使用User-Agent:
   mod_gzip_item_include  reqheader  "User-Agent: MSIE [6-9]"
   mod_gzip_item_include  reqheader  "User-Agent: Mozilla/[5-9]"

       Apache 2.x 使用 BrowserMatch :
   BrowserMatch  ^MSIE [6-9] gzip
   BrowserMatch  ^Mozilla/[5-9] gzip

  而针对代理缓存,一种方法是在Vary的头信息中加上User-Agent标识来告诉代理服务器,我们使用了白名单;在mod_gzip模块中,如果它发现配置了白名单,则会自动为Vary加上上User-Agent标识。但是不要指望代理服务器会缓存所有的浏览器白名单的每一个url副本。所以我们不得不采用另一个方法:为返回的响应中加上 Vary : * Cache-Control:private  头信息,以表明完全禁止代理服务器的缓存,不让代理去缓存任何一个组件,让用户通过代理时,都要去请求真正的服务器以获得页面数据,事实上Google和Yahoo!都是使用的这一策略,尽管它会带来更多的流量消耗。

  还有一个特殊情况要指出,就是ETags(我们将会在Chapter 13章节单独提到),默认情况下它都不会被压缩,所以最好的办法就是干脆禁用ETags,这个还是放在Chapter  13章节再详谈吧。


  下面三个链接分别是没有gzip压缩,只压缩了HTML和压缩了全部组件的三个链接页面:
 Nothing Gzipped
  http://stevesouders.com/hpws/nogzip.html
 HTML Gzipped
  http://stevesouders.com/hpws/gzip-html.html
 Everything Gzipped
  http://stevesouders.com/hpws/gzip-all.html
  下图就是三者的具体数据量对比和时间对比:

《High Performance Web Sites》 :Chapter 3

法则3:增加Expires Header  Add an Expires Header


  在您设计网页时,快速的响应时间不应该是你唯一要考虑的,如果仅仅是这样,那我们采用法则1,把我们的页面设计成一个极端的网页:没有任何图片,script,样式表。我们都明白,图片、script、样式表这些组件可以增强用户体验,虽然它们会给页面带来较长的载入时间。你幸运了,在这一章介绍的法则3,我就要向你介绍如何最大限度地利用浏览器的缓存来使这些页面组件更高效的为我们的页面服务。

  现在的网页所包含的组件越来越丰富,java applet去了,Flash来了,script也不可缺少,随着div的大潮,css样式表也少不了...... 所以用户若第一次访问您的网页,则可能会产生多个HTTP请求;但如果您在请求返回中使用Expires头信息,则可以缓存这些组件,避免在随后的访问中产生不必要的HTTP请求,Expires头信息最常用于图片,其实它适合应用在所有的组件中,如scripts,样式表和Flash。但大多数当今的热门网站目前并没有这样做。在这一章节中我会提及如何让这些网站可以更快。在使用Header头信息时,也可能会有一定的额外开销,这点会在 "改变文件名"段落中再详谈.


Expires Header

  浏览器使用缓存来减少HTTP请求数和减少HTTP的响应数据量,以达到更快的加载页面。web服务器通过Expries header来告诉web客户端(John:主要是浏览器)当前返回的组件在我指定的时间以前都是可用的,你(浏览器)可以留着下次备用(John:有点像我们在超市买牛奶时,随包装袋上返回的过期日期)。Expries header 在HTTP规范中描述为 "the date/time after which the response is considered stale.",它附带在HTTP的响应中返回给客户端。
  如上图,这是一个典型的expries header ,它告诉浏览器,直到2010年4月15日这个请示的返回都不会变化。如果这个头信息是随着一个图片返回的,那浏览器就会缓存这个图片,以为其后的页面浏览直接使用,以减少一个HTTP请示。


Max-Age and mod_expires

  在我解释缓存是如何更好的提升性能之前,有必要介绍一下与Expries header相关的几个头信息。Cache-Control header是在HTTP/1.1中被引入以弥补Expries header的不足。因为Expries header使用的是一个具体的日期,它就要求服务器与客户端之间要有严格的时钟检查,并且当过期日期到来之后,服务器端还必须配置一个新日期。

  相反,Cache-Control 使用max-age来指定一个时间段来标识一个组件能使用多长时间。这个max-age是以秒为单位来描述的(John:有点保质期的概念)。如果在这个保质期内,这个组件再被请求,浏览器就直接使用缓存里的就行了。下面就是一个返回10年保质期的头信息。
  使用Cache-Control虽然弥补了Expires的不足,但我们仍然还是得使用 Expires header以兼容那些不支持HTTP/1.1的浏览器(尽管这些流量可能还不到1%)。所以我们一般同时使用Expires和Cache-Control max-age,按HTTP规范这时会以max-age的时间为优先考虑,所以我们还是避免不了服务器与客户端之间的时钟检查和服务器端的新日期配置。

  幸运的是我们有Apache的 mod_expires模块,能让我们像设置max-age那样来配置Expires header,它使用ExpiresDefault来设置一个时间段,如下,为图片,scripts和样式表设置10年的有效期:
<FilesMatch "\.(gif|jpg|js|css)$">
     ExpiresDefault "access plus 10 years"
</FilesMatch>

  这个时间可以是年,月,周,天,小时,分钟甚至是秒。使用了这个模块,所有的响应返回中就会同时带着 Expires header 和 Cache-Control max-age 头信息:
  其中Expires header所携带的过期时间具体是多少,则依赖于每个客户端什么时间发出的请求,在上面的例子中,始终是10年。这样就避免了我们每次在到期后都要重新设置服务器,mod_expires会搞定一切。

  图3-1是对10大网站使用这些headr的一个调查。有7家使用了这些header,其中有5家同时使用了
Expires 和 Cache-Control max-age(John:推荐)。各有2家分别只使用 Expires 或Cache-Control max-age。很遗憾有3家什么也没使用。

Empty Cache vs. Primed Cache

  使用Expires  header只会影响已经访问过的页面和组件,当用户第一次访问您的页面时无法避免的会有比较多的HTTP请求数,因为这时浏览器的缓存里是空的。所以如果您的网站能吸引用户访问更多的页面, 并经常在此停留,我们所做的缓存工作才有效果。

不仅仅是图片 More Than Just Images

  一般Expires header常用于图片,其实这并不是最佳实践。Expires header应该应用于所有那些不经常变动的组件,script,样式表或Flash组件。

  在理想情况下,所有组件都在一个页面内,并且都有一个未过期的Expries header,而随后浏览的页面只一个对HTML源文件的HTTP请求,其余的组件都在浏览器的缓存中直接提供,这时的响应时间会提高50%甚至更多。

  我调查了美国的10大网站,并记录了其中有多少图片,script和样式表是设置了至少30天有效期的Expires 或是 Cache-Control max-age header,如表3-2所示,情况并不是太好,图中所列的是被缓存至少30天的组件数,分母是该网站上的所有的该类型组件数,分子是被加了过期header的组件数。
  • 有5家网站把大多数图片的缓存有效期设为30天以上.
  • 有4家网站把大多数样式表的缓存有效期设为30天以上.
  • 有2家网站把大多数script的缓存有效期设为30天以上.
  从上图的百分比中俺发现有74.7%的组件没有被缓存或是有效期低于30天。一种解释就是这些组件因为业务关系而不应该被缓存,如新闻网站CNN.com就是个例子,138张图片中没有一张被缓存,这是因为很多新闻照片要被不断更新而不必缓存在用户端,所以这时更适合用Last-Modified heade,来通过组件的最后修改时间判断是否要读取新数据。

  表3-2的最后一项,显示了未被缓存的组件的最后修改时间的中分线,就CNN.com来说有一半的未缓存组件的最后修改时间是在227天前了,
很显然这些组件是应该被缓存的。

  这种情况也曾发生在Yahoo!,在过去
,Yahoo!并没有缓存script,样式表和一些图片,是因为我们认为这样是经常变化的,用户有必要每次访问时都去请求一次以获取最新的。可问题是,在实际应用中,这些组件并不是我们想像中的那样要经常变化更新,我意识到应该把它们缓存来提升用户体验。后来,Yahoo!选择了去缓存它们,哪怕会带来一些额外的开发花销,这就是我马上要谈到的Revving Filenames。

改变文件名 Revving Filenames

  如果我们已经为组件设置了缓存,可当这些组件又需要更新时,用户如何才到得到最新的组件呢?由我们前面介绍的Expires 或是 Cache-Control max-age header 都会让浏览器不检查任何改变,直接从本地硬盘中读取缓存,除非是过了有效期。所以我们能做的,就是在HTML页面中改变这个组件的文件名。在Mark Nottingham的文章"Caching Tutorial for Web Authors and Webmasters"谈到这个问题:

  最优的方案还是改变它们的链接;只有这样,用户才能从服务器上获取更新。(The most effective solution is to change any links to them; that way, completely new representations will be loaded fresh from the origin server.)
 
  说白了,还是要改变文件名。在已有的页面中改变文件名可能是件头疼的烦事,但是,不得不这样,如果您使用的是PHP,Perl等动态的页面,不妨利用一个变量为所有的组件命名,这样换起文件名来比较省事。在Yahoo!,我们是在组件名后面跟上版本号(如,yahoo_2.0.6.js),把这个版本号做为一个变量写在程序中,真的很省事。


举例 Examples


  下面的两个链接,包包含有相同的组件:6个图片,3个script和1个样式表。第一个链接没有任何Expires header,而第二个链接,全有。
  No Expires
     http://stevesouders.com/hpws/expiresoff.php
  Far Future Expires
    http://stevesouders.com/hpws/expireson.php
  我在900Kbps的DSL下测试,加了过期头设置的链接页面访问时间会减少600~260毫秒,能提升57%,如果页面的组件再多一点,这个百分比还会更高。

  要说明一点,并不是说没有加过期头的组件就不会被浏览器缓存,它们也会被缓存,还记得我在Chapter B 中介绍的有条件的GET请求吗,只不过,会多一个HTTP请求到服务器端以询问这个组件是否有效,如果这个组件的最后修改时间和以前一下,则直接使用缓存中的版本,HTTP返回中将不带这个组件的具体数据了。

  为您页面上的组件贴上保质期吧,再加有有条件的GET请求,带来的会是减少近一半的HTTP请求数还有不必要的数据传输。

《High Performance Web Sites》 :Chapter 2

法则 2: 使用CDN 内容分发网络  Use a Content Delivery Network

  用户的网络带宽在逐年增加,但用户访问您web服务器的快慢仍受着地域的限制(John:最典型的例子就是我们大陆的南北电信互通问题)。Web创业者往往都会在某一地域的机房放置自己的服务器,但一旦他们渡过艰难的初创阶段,开始要面对涌入的大量用户时,他们都要面对这样的现实:即一个地域机房里的那一台服务器已经不足于应付这种大访问了,是时候在多个分散地域(城市结点)上部署更多的内容服务器。

  如何迈出第一步呢?要实施地理上的内容分布,切勿急着尝试以分布式的系统构架去重新设计您的web应用。如果依赖于应用系统的分布,那您的重构工作可能会是十分艰巨,如要处理会话状态的同步和在多个地域的数据库之间处理数据一致性等复杂问题(John:如果您进行的是类似银行级的用户应用,则不在此讨论之列)。您应该优先考虑缩短用户与您web内容之间的地域距离。

  还记得我们在前面的Chapter A中讨论的性能黄金法则吗:
    只有10-20%的最终用户响应时间是花在了下载HTML源文件上。其它的80-90%是花在了下载页面中的所有组件上。
  
  如果您的web应用服务器离用户足够近,那么在Chapter A 中讨论的那个第一个HTTP请求的响应时间就可以更快;再或者您的web组件服务器也离用户足够近,那在页面中请求这些组件的响应时间也会缩短,这可比您重新设计一个分布式的系统的复杂度要小的多了,这就要用到内容分发网络:CDN(content delivery networks)。

 

Content Delivery Networks 

  内容分发网络 CDN 是一个分布在多个地域间用于为用户提供更高效的内容访问服务的网络服务器集群。这种高效不仅体现在性能上,也体现在成本的节省上,因为CDN会选择离请求用户物理结点最近的一台服务器为用户提供内容访问服务,以在到提高响应时间。

  一些大型的互联网公司都拥有自己的CDN,但使用专业CDN服务提供商的服务则更能节约成本。如Akamai科技公司于2005年收购了Speedera网络公司,成为北美CDN市场的领军者,还有Mirror Image网络公司,Limelight 网络公司,SAVVIS 公司等。

  如下图2-1是十大美国网站使用CDN的情况:

post-425982-1195885961.jpg的缩略图

  我们能看到:
  • 有五家网站使用了Akamai
  • 有三家分别选择了 Mirror Image,Limelight,SAVVIS
  • 其他的五家要么没有使用CDN,要么就是有自己的CDN解决方案

     规模较小或是非商业网站未必能负担得起CDN服务器的高额费用,但有一些免费的CDN服务机构可以选择。如部署在阿姆斯特丹自由大学的基于Apache模块的Globule(http://www.globule.org),建在普林斯顿大学的CoDeeN (http://codeen.cs.princeton.edu)等等。(John:老实说,这些免费的CDN对我们没有什么用)
  CDN除了能提升响应时间,还有一个好处。CDN还可以用于备份数据,扩充存储容量甚至是做缓存;它还可以帮助我们解决流量高峰的问题,如,一个提供实时性较强的天气预报或财经新闻的服务,或是发布某种体育娱乐新闻时,这种瞬间的访问高流量也可以被CDN所分流。

  说到这,该说说CDN的缺点了,除了费用高,您的服务响应时间则会依赖于CDN服务商的硬件和同时在使用这家CDN的其它网站,甚至是有可能会受您竞争者的影响,如果您的竞争者也在使用与您相同的CDN服务商。因为CDN服务商一般都是在他所有的分发服务器之间为客户共享这些资源(John:这和大家在机房使用共享的虚拟主机是一个道理)。CDN的另一个不足是您可能会无法直接控制这些内容服务器,假如您要修改所有该CDN中的组件的HTTP响应头,这可能必须通过CDN服务商而不是您自己的技术人员。如果CDN本身出了问题,也会直接影响用户对您的web的访问。在上图2-1中,eBay和MySpace都使用了两家CDN服务商,这可是个明智之举,减少一定的风险,前提是您有足够的业务需求和金钱的支撑。

  CDN是用来分发静态内容的,如图片,scripts,样式表或Flash。而要提供动态的HTML页面则要一定的服务器端要求:数据库连接,状态管理,权限认证,硬件和操作系统优化等,这些复杂的东西都超出了CDN的服务器范围(John:CND不是虚拟主机提供商)。由于CDN只提供静态文件的访问,从另一方面看,也使得它更有针对性,它只针对于提供静态文件较多的网站使用。



时间,节省

  使用CDN到底能带来多少速度的提升。下面的两个链接页面,都包含相同的页面元素:5个script,1个样式表,8个图片。第一个链接页面是放在Akamai公司的CDN上,另一个是放置在一台单独的web服务器上:
  CDN
  http://stevesouders.com/hpws/ex-cdn.php
  No CDN
  http://stevesouders.com/hpws/ex-nocdn.php

  由于这是放在美国的服务器,我们在国内访问可能还没那么快,但从测试的时间上来看,用 CDN的页面比放在单独服务器上的要快近18%,当然了,这一结果要取决于您的带宽接入速率和所在的地域。(John:目前的几大门户网站,新华网,人民网等以新闻资讯为主的网站都使用了CDN,在我前面介绍的Youtube构架中,也提到了他们的CDN策略。)

相关章节:
中文翻译:
翻译:High Performance Web Sites(1)-Chapter A 前端性能的重要性
翻译:High Performance Web Sites(2)-Chapter B HTTP 概述
翻译:High Performance Web Sites(3)-Chapter 1    法则 1: 尽可能减少HTTP请求数
  之前在 What is scalability ? 一文中,John对系统的扩展方式和度量做了一些讨论,但我们还是要知道:对于系统扩展,没有放之四海而皆准的方案,只有最适合自己实际情况的解决方法。在 的一篇有趣的文章:what's your scalability plan ? 中,Todd把一个网友发在留言板中的扩展方案又贴了出来,希望引出更多的草根方案,呵,看这网友贴的方案,就仿佛又看到了一群在创业中经历辛苦但享受梦想的人们。

  这位网友说他的网站是从零开始的,以下是他的扩展计划:

  第1阶段:
  单服务器一台,双路四核的2.66G CPU,8G的内存,10块500G的RAID硬盘。
  操作系统: Fedora 8 。
  代理缓存: Varnish (John:这位网友说 Varnish 比 squid要快,这是真的吗??)
  web服务器:lighttpd ,原因:比Apache2快,更重要的是配置简单。(John:这完全符合创业者的特点)
  对象缓存:  memchched
  编程语言:PHP 5,原因:简洁,没有臃肿的框架。(John:确实,php仍然是创业者的首选,不过现在rails也很值得推荐)
  PHP缓存: APC
  数据库: MySQL 5

  第2阶段:
  把内存升级到64G,缓存所有能缓存的东西。

  第3阶段:
  购买负载均衡设备,为前端增加2台服务器,配置与第1阶段类似:Varnish,memcached,lighttpd.
  原先的机器做为独立成MySQL数据库服务器使用。

      第4阶段:
      看负荷和规模的增长情况了,可考虑水平扩展增加一台数据库服务器或是文件服务器,主要是解决数据的访问瓶颈;若有必要,也可以考虑把Varnish ,Memcached,Lighttpd 分离到独立的服务器中。对于静态的文件,也可以考虑CDN了。

  第5阶段:
  把所有的服务器都升级到64G的内存,我们的口号是:缓存,缓存,缓存。

  第6阶段:
  如果这个网站能走到这一阶段,那现在应该不是缺钱了(风投或是盈收),毕竟没有多少网站能走到这一步。那么就把所有机器都换成是最高配置的。更重要的是,要保证web应用中每一层都能扩展,并且是能横向的通过增加机器来水平扩展。

  哈,这还真是个比较simple的草根扩展方案,但real world往往是复杂和万变的

《High Performance Web Sites》 :Chapter 1

法则 1: 尽可能减少HTTP请求数 Make Fewer HTTP Requests

      在Chapter A中介绍的性能黄金法则,揭示了一个现象:只有10~20%的用户响应时间是花在请求html源文件上,剩下的80~90%的时间则是在请求页面中的各个组件(图片,script,样式表,flash等)。因此,我们的第一个提速的方法就是尽可能的减少这些组件的数量,我们的目的就是要减少HTTP请求的数量。

  减少页面组件的建议,通常会让人在性能和产品设计上产生冲突。毕竟我们的页面现在所蕴含的元素越来越多,更多的小图片,不同的样式表,script等,这一章节,我会介绍一些技术方法来帮助我们平衡性能与产品设计上的冲突。这些将要出场的技术包括:image maps, CSS sprites,inline images以及尽量使用独立的js和样式表文件。使用这些技术方法在我们的案例中能减少50%的响应时间。


Image Maps
  将一个页面中所要引用的图片整合成一个单一的图片文件,按顺序排好,再分切出里面的链接区域。这样对整个图片群的需求样式没有变,但减少了对图片的http请求数。
      图1-1显示的是一个有五个图片组成的导航栏,每个图片对应一个独立的超链接。我们常规的做法,当然就是做五个图片,然后为每个图片做一个链接;但为了更高效,我们把五张独立的图片做成一张image map,这样从五个HTTP请求,就变成了一个HTTP请求,相应的响应时间也就会变的更快了。

  您可以试一下下面的两个链接,自己体会一下Image maps所带来的速度上的不同。

No Image Map
http://stevesouders.com/hpws/imagemap-no.php
Image Map
http://stevesouders.com/hpws/imagemap.php

  如果使用IE6在DSL(~900Kbps)的网络环境下,用image map的方法组成的导航栏要比用单独图片文件的所组成的导航栏要快56%(354ms 比 799ms).这是因为image map会减少四个HTTP请求。

  Image Map最常用的实现方式是使用HTML的map标签,把大图片分成一个一个的小块,并设置其不同的链接。如下:
 
<img usemap="#map1" border=0 src="http://7career.org/images/imagemap.gif">
  <map name="map1">
    <area shape="rect" coords="0,0,31,31" href="http://7career.org/home.html" title="Home">
    <area shape="rect" coords="36,0,66,31" href="http://7career.org/gifts.html" title="Gifts">
    <area shape="rect" coords="71,0,101,31" href="http://7career.org/cart.html" title="Cart">
    <area shape="rect" coords="106,0,136,31" href="http://7career.org/settings.html" title="Settings">
    <area shape="rect" coords="141,0,171,31" href="http://7career.org/help.html" title="Help">
 </map>

  但是它所带来的缺点就是你得手动确定图片的坐标,这会比较乏味和容易出错,并且它只适合把图片都组合在一个长方形的区域里。

CSS Sprites (您可以参考YouTube和iGoogle的首页,它们就是采用的这种优化方式)
   与image maps类似,CSS Sprites也是把若干小图片合成一个大图片,但是CSS Sprites方式更灵活。为了实现CSS Sprites,是把各个小图片像组成一个棋盘一样地合成一个图片。如下图:   然后通过HTML中任何能支持背景图片的元素,如<span>或<div>,再通过CSS中的background-position属性来定位要显示的大图片中的某个小图片的位置。如下,就是要在上面给出的在图片中使用"My"这个图标来充当下面这个div的背景:
<div style="background-image: url('a_lot_of_sprites.gif');
       background-position: -260px -90px;
       width: 26px; height: 24px;">
</div>


  我把前面我介绍image map的例子转成CSS Sprites的形式:把导航栏的五个链接都放到一个名为navbar的DIV中。每个链接都有一个SPAN元素,在#navbar的样式中为SPAN元素定义了背景图片spritebg.gif,但每个SPAN都有一个不同的class以指明其具体显示的背景图片的偏移位置,正是利用了CSS中的background-position属性。

<style>
#navbar span {
   width:31px;
   height:31px;
   display:inline;
   float:left;
   background-image:url(/images/spritebg.gif);
 }
.home { background-position:0 0; margin-right:4px; margin-left: 4px;}
.gifts { background-position:-32px 0; margin-right:4px;}
.cart { background-position:-64px 0; margin-right:4px;}
.settings { background-position:-96px 0; margin-right:4px;}
.help { background-position:-128px 0; margin-right:0px;}
</style>

<div id="navbar" style="background-color: #F4F5EB; border: 2px ridge #333;
                                  width:180px; height: 32px; padding: 4px 0 4px 0;">
  <a href="javascript:alert('Home')"><span class="home"></span></a>
  <a href="javascript:alert('Gifts')"><span class="gifts"></span></a>
  <a href="javascript:alert('Cart')"><span class="cart"></span></a>
  <a href="javascript:alert('Settings')"><span class="settings"></span></a>
  <a href="javascript:alert('Help')"><span class="help"></span></a>
</div>

  这比image map的方式的例子要更快:342ms VS 354ms,但是他们之间的实现方式只有很小的不同。但重要的是,这可比用单独的五个图片的例子要快57%了。
CSS Sprites
   http://stevesouders.com/hpws/sprites.php


  我们看到,image map的方式要求所有的图片是连续的组合在一起的,而CSS Sprites没有这个限制。关于CSS Sprites的优缺点在Dave Shea的权威文章"CSS Sprites: Image Slicing's Kiss of Death"中已经有详细的介绍,但我已经从CSS Sprites中体会到它的优点:减少了HTTP请求,比image maps灵活。另一个让我没想到的优点是它减少了下载的数据量。大多数人可能会认为一个拼合成的大图片肯定要比这此小图片的总量要大,因为它会有一些小图片的间隔区域。实际上正相反,大图片减少了图片中的color tables和格式信息等,而使得大图片比一堆小图片实际size要小一些。

    如果你的网站中有很多背景图片,按钮图片,导航栏图片,那么你应该用CSS Sprites方式来优化你的页面了。(您可以参考YouTube和iGoogle的首页,它们就是采用的这种优化方式)


 Inline images(注:IE暂不支持,您可以跳过这一部分;但说不定什么时候IE就支持了)
  我们上面所做的都是为了减少HTTP请求,现在有一个更绝的方式,把所有的图片都以base64编码以源代码的形式写在HTML源码里:data:[<mediatype>][;base64],<data>   所有对图片的HTTP请求,都化在了对HTML源文件的第一次请求里。

      我们在HTML中肯定都用过ftp:,file:,mailto:这样的标签,实际上像这样的标签还有很多,只是我们平常不太使用,像:smtp:,pop:,dns:,whois:,finger:,daytime:,news:,urn:等等.

      data:URL标签是在1995年第一次提出,按RFC2397规范的描述:它是"allows inclusion of small data items as 'immediate' data.(允许在页面中包含一些小的即时数据)"。如一个内嵌的小红星的图片可以这样引用:(在firefox下可以出来)
   
  <IMG ALT="Red Star"
SRC="data:image/gif;base64,R0lGODlhDAAMALMLAPN8ffBiYvWW
lvrKy/FvcPewsO9VVfajo+w6O/zl5estLv/8/AAAAAAAAAAAAAAAACH5BAEA
AAsALAAAAAAMAAwAAAQzcElZyryTEHyTUgknHd9xGV+qKsYirKkwDYiKDBia
tt2H1KBLQRFIJAIKywRgmhwAIlEEADs=">

  这样是挺方便吧,但它的不足之处也很明显:目前IE不支持;再就是FireFox1.5所能处理的line image有大小限制,不能超过100K;base64的编码会增加HTML的容量,总体下载量会增多。
       Inline Images:http://stevesouders.com/hpws/inline-images.php

      data:URL是内嵌在页面中的,所以它不会在页面之间缓存。所以您别用这种方法存储您公司的logo,因为它会增加您每个页面的容量。要解决这个问题,您可以把inline image写在CSS中,尽管date:URL不会被缓存,但CSS是可以被缓存的,Inline CSS Images:http://stevesouders.com/hpws/inline-css-images.php

下面就是接上面的例子,为每个SPAN加上inline image:

.home { background-image: url(data:image/gif;base64,R0lGODlhHwAfAPcAAAAAAIxKA...);}
.gift { background-image: url(data:image/gif;base64,R0lGODlhHwAfAPcAAAAAAABCp...);}
.cart { background-image: url(data:image/gif;base64,R0lGODlhHwAfAPcAAAAAADlCr...);}
.settings { background-image: url(data:image/gif;base64,R0lGODlhHwAfAPcAAAAAA...);}
.help { background-image: url(data:image/gif;base64,R0lGODlhHwAfAPcAAAAAALW1t...);}

       PHP的file_get_content函数可以很轻松的实现inline image的那串base64的图片编码。我上面的例子就是用的这个函数:

.home { background-image: url(data:image/gif;base64,
<?php echo base64_encode(file_get_contents("../images/home.gif")) ?>);}
.gift { background-image: url(data:image/gif;base64,
<?php echo base64_encode(file_get_contents("../images/gift.gif")) ?>);}
Combined Scripts and Stylesheets | 15
.cart { background-image: url(data:image/gif;base64,
<?php echo base64_encode(file_get_contents("../images/cart.gif")) ?>);}
.settings { background-image: url(data:image/gif;base64,
<?php echo base64_encode(file_get_contents("../images/settings.gif")) ?>);}
.help { background-image: url(data:image/gif;base64,
<?php echo base64_encode(file_get_contents("../images/help.gif")) ?>);}

      我们比较一下上面的几个例子,image maps与CSS Sprites的响应时间基本上相同,但比使用各自独立图片的方式要快50%以上。用inline image与CSS组合的方式虽然增加一个HTTP请求,但是它可以以样式表的形式被缓存。

Combined Scripts and Stylesheets
尽量使用独立的js和样式表文件


  现在的前端开发少不了JavaScript和CSS,但我们还是建议:使用外部的js和css文件引用的方式,因为这要比直接写在页面中性能要更好一点(细节会在Chapter 8中讨论)。但是如果你滥用这条规则,把你的代码切割成了很多个小文件,那只会增加更多的HTTP请求而影响性能。
       表1-1是10大网站首页中js和样式表的数量表,它们的样式表都不多,但js显然还有优化的余地。
       下面的例子中,你会发现用独立的一个js比用多个js文件组成的页面载入要快38%.
Separate Scripts
    http://stevesouders.com/hpws/combo-none.php
Combined Scripts
    http://stevesouders.com/hpws/combo.php

       有的人可能会习惯于按功能或模块来把js分成各种不同的文件,但以我的经验,如果一个网站的页面引用大量的js文件,那应该要分析一下这些分类是否真的便于管理。


结论
      这一章节介绍了我们在Yahoo!用到的以减少HTTP请求数量的技术方法。也是对访问网站很重要的一条法则。它会提高用户第一次访问您网站时的体验速度。而更快的访问速度将会使用户更愿意常回头访问您杰作。

相关章节:
翻译:High Performance Web Sites(1)-Chapter A
翻译:High Performance Web Sites(2)-Chapter B

《High Performance Web Sites》 :Chapter B

HTTP Overview

      在开始介绍具体的优化法则之前,您务必要理解超文本传输协议:HyperText Transfer Protocol(HTTP)对性能的影响。HTTP是浏览器与服务器之间通过Internet通信的方式。HTTP的规范是由万维网联盟:W3C(World Wide Web Consortium)和互联网工程任务组:IETF(Internet Engineering Task Force)共同制定,而最终形成的RFC 2616规范。HTTP/1.1是目前流行通用的版本了,但还是有一些浏览器只支持HTTP/1.0。

  HTTP就是描述请求request和响应response的c/s模型协议。一个浏览器发送HTTP请求(request)到一个指定的URL,这个URL所指定的服务器接到请求后返回一个HTTP响应(response)给浏览器,就这么简单。与很多互联网服务一样,这个协议用的是简单的明文格式。请求的种类有:GET,POST,HEAD,PUT,DELETE,OPTIONS和TRACE。下面我只重点介绍GET请求,这一最常用的。

  一个GET请求把所请求的URL组装在头信息(headers)中,然后发送到这个指定的URL。服务器接收到请求后,经过响应处理,返回一个包含状态码,头信息(headers)和具体明文文本(一般是Html源码)所组装的数据包。如下图,就是请求yahoo_2.0.0-b2.js时的HTTP头信息(headers)的内容示例。

压缩
  如果浏览器和服务器都支持,可以通过压缩来减少响应返回时的数据量。浏览器通过头信息中的Accept-Encoding元素来告诉服务器是否可以接受压缩数据,以及何种压缩方式。而服务器则通过头信息中的Content-Encoding元素来告诉浏览器,我目前支持哪种压缩方式。如下图

  注意上图中返回的数据是已经被压缩的数据。Chapter 4会专门介绍HTTP压缩,以及在代理缓存下产生的一些问题,当然还会讨论一些其它的头信息(headers)。


有条件的GET请求  Conditional GET Request
  如果浏览器已经缓存了一个组件,但是浏览器该怎么知道这个组件是否还有效呢,于是就产生了有条件的GET请求。如果缓存里的组件是有效的,浏览器就继续使用这个组件来加载页面,以减少响应的数据量从而加速了用户体验。

  一般来说,判断缓存的组件是否有效是检查它的最后修时间。浏览器通过response响应数据中名为Last-Modified的头信息来获取这个时间值。再次有相同组件的GET请求时,浏览器会将名为If-Modified-Since头信息随GET请求一起发送到服务器,带着这一条件信息给远程的服务器,这就好像是浏览器对服务器呐喊:"我已经有一个版本的组件了,这里它的最后修改时间,这次我还可以继续使用它吗?"(只要网络不断,服务器总是会听见这呐喊声的)

  如果这个组件在这个Last-Modified描述的时间之后没有被修改过,服务器就不会返回这个组件的具体数据了,而返回一个"304 Not Modified"状态码,这样可不就提高了响应速度了。在HTTP/1.1里,名为ETag和If-None-Match的头信息也可以起到类似的作用,这两个都会在Chapter 13中有讨论。

过期 Expires

  有条件的GET请求和返回304状态码能让页面载入的更快,但它还是需要在客户端和服务器端进行一个往返的验证过程,HTTP请求仍然存在。而名为Expires的头信息会则会避免这种验证过程。

 一旦浏览器发现响应返回的头信息中有Expires属性,它就会把这个属性所描述的日期与组件保存到浏览器缓存中,只要再次访问这个组件时的日期没有超过Expires属性里描述的日期,就会一直被浏览器使用而不会再次发送HTTP请求。Chapter 3会有关于Expires和Cache-Control头信息的更多介绍。



Keep-Alive

  HTTP是建立在TCP协议的上层的实现,在早期的HTTP实现中,每个HTTP请求都会打开一个新的socket连接。实际上效率很低,因为大多数的HTTP请求都是针对同一个服务器发出的(想像一下我们浏览一个页面的情形,是不是该页面的组件大多数都是在这个页面所host的服务器上)。于是,长连接(persistent connections)的概念被引入了HTTP,以解决这种同一个服务器不停地打开关闭socket连接的情况。它能够让浏览器的多个请求只通过一个连接完成。浏览器和服务器就是通过相互传递Keep-Alive头信息来判断是否支持这一特性。

  浏览器或服务器可以通过发送一个close头信息来关闭连接。从规范来说,keep-alive并不包含在HTTP/1.1中,但是绝大多数浏览器和服务器还是支持这一特性的。

  Pipelining, 定义在HTTP/1.1中,它允许在一个socket连接中发送多个请求而不用等待响应返回。Pipelining比长连接(persistent connections)有着更好的性能。但是很不幸,IE不支持(IE7已经支持了),FireFox 2以后的版本虽然支持,但默认是关闭的。所以在Pipelining还没能普及以前,我们就还只能用Keep-Alive的方式来优化浏览器与服务器之间通过socket的通信。这个特性对于HTTPS很重要,因为HTTPS的连接都是长时间的连接.


更多

  这个章节只是大致的介绍了HTTP,并把重点放在介绍影响性能的方面。如果要了解更多,您可以参考HTTP规范(http://www.w3.org/Protocols/rfc2616/rfc2616.html)和由David Gourley 与 Brian Totty编写的《HTTP: The Definitive Guide》(O'Reilly; http://www.oreilly.com/catalog/httptdg)。上面所例举的资料将会您理解我们后台的章节有很大的帮助。

前端性能的重要性The Importance of Frontend Performance


    
我的大部分web生涯都是在扮演后台开发工程师的角色。所以,我很自然的把每个性能作业都作为是一个后台的优化练习罢了,像什么编译器参数,数据库索引,内存管理什么的。而且也有很多关于后台性能优化的书籍和资料让大家从中找到想要的东西。但实际上,对于绝大多数的web页面,只有不到10-20%的最终用户响应时间是花在了从服务器下载HTML源文件到用户浏览器上。如果你想达到不可思议般的用户快速浏览体验,那当然就应该去关注一下那其它的80-90%的用户体验时间花在哪儿呢?又该怎么去减少这个时间呢?后面的章节就会向您讲解与目前web优化所相关的一些基础知识,并提供14条优化性能的法则。

 

来关注一下web页面的性能

为了知道怎么去提高性能,我们应该先了解最终用户们都把等待的时间花在哪儿了?图A-1是在IE上访问Yahoo!的首页(http://www.yahoo.com)HTTP响应时间图(John注:我们可以用HttpWatch(IE)Firebug(ff)工具来生成类似的图)。每一个横条都是一个HTTP请求。第一个横条,标识为html的那个,是HTML文档的初始请求(指请求HTML源文件)。然后浏览器解析这个HTML文档,并且开始下载这个页面的所有组件(John注:这里的组件是指页面所引用的图片,flash等非html文档的东西)。在我们的测试环境下,浏览器的缓存是空的,所以所有的组件都会被下载。在图上,我们可以看到,HTML源文件的下载只占整个响应时间的5%,最终用户花了近95%的时间用在等待下载组件上.还有一小部分是花在HTML,script和样式表(stylesheets)的解析上,在图中我们用每个下载时间条间的空白间隔来表示这些解析时间.

A-2显示的是我们第二次用IE浏览这个网页时的情况.HTML文档的下载占到了整个响应时间的12%.大部分的组件都不用再下载了,因为它们已经被浏览器缓存了。

A-1.gif

A-1 Internet Explorer上访问http://www.yahoo.com,空缓存

 

A-2.gif

 

A-2Internet Explorer上访问http://www.yahoo.com,预先已有缓存

 

Ok,我们来看一下,有五个组件在第二次浏览页面时仍被下载了:


一个重定向(redirect)

     这里的重定向虽然已在第一次访问被处理过了,但是浏览器还是再次请求了一次。因为它的HTTP响应状态码是302(表示"Found" "move temporarily"),在响应header中没有缓存信息,所以浏览器不会去缓存它。关于这方面我们会在Chapter B中讨论HTTP时提到。

三个没有缓存的图片
  接着的三个request请求是在上一次浏览时没有下载的三个图片。这些是经常变动的新闻图片和广告图片.

一个被缓存的图片
  最后一个HTTP request请求是一个有条件的GET请求.这个图片是上次被缓存的,但是因为HTTP的响应头(response headers)的设置,浏览器要确认一下这个图片是不是最新的.有条件的GET请求我们也会在Chaptet B中讨论它.

 

时间都去哪儿了?

再回过来看看HTTP时间响应图,我们看到至少80%的最终用户响应时间是花在了下载页面里的组件上,如果我们来仔细分析一下这个图表的内容,我们会发现浏览器与HTTP之间的交互开始有点变的复杂了。除了上面我提到了HTTP的状态码和header会响应浏览器的缓存,另外,我们还能得到下面一些观察结果:

l          凡是被缓存的组件(图A-2)大多都不会有下载动作。相反,你看到的是紧跟在Html请求后的一个没有下载的空白时间间隔,这表示这段时间是在解析HTMLJavaScriptCSS,以及从缓存中获取相关的组件。

l          在同一时间里的HTTP请求数变化了。图A-2在同一个时间切面上最多只有三个HTTP请求,而图A-1却同时有多达六到七个HTTP请求。这是因为使用了多个不同的主机名(hostname),而无论它们是使用HTTP/1.0还是HTTP/1.1Chapter 6将会解释这种多个同时下载的"平行下载(Parallel Downloads)"。(John注:是指同时下载)

l         这种平行下载不会发生在下载script上,因为在大多数情况下,浏览器会阻止其它下载scriptHTTP请求。Chapter 6会解释为什么会这样,同时也会教你如何应用这个知识去优化你的页面载入时间。

要完完整整地指出时间都去哪儿了真是一个挑战。但至少还是很容易地能看到哪儿没花时间?它没有全花在下载HTML文档上,也没全花在后台的处理上。(几乎都花在了前端下载组件上)这就是为什么前端性能如此重要了。



性能黄金法则

 

        Yahoo!主页这样只有10%-20%的响应时间花在下载HTML源文件的现象并不是一个特例。据我分析,几乎所有与Yahoo!类似的网站都是这样(这可不包括YahooSearch,因为它的页面上组件的数量实在是太少了)。甚至可以进一步说,绝大多数的网站都是这样的情况。表A-1展示了由http://www.alexa.com所统计出的当今美国前10大网站。说明一下,下面提到的网站只有AOL不在前10,因为Craigslist.org本是排在前10,但是它的页面风格没有什么图片,script和样式表,不适合这里做为研究例子,所以我挑选了AOL

A-1  访问10大网站时的下载HTML源文件所占的时间比

T-A-1.gif


        所有的网站都只花了不到20%的总响应时间在获取HTML源文件上(John注:这一部分时间更多地取决于各网站的后端性能)。但只有Google在已有缓存的情况下是个例外。因为http://www.google.com只有6个组件,其中有5个被配置成被浏览器缓存。再次访问时,所有的组件被缓存,只有一个请求HTML源文件的HTTP请求和一个图片请求。

       在做完所有的后台优化努力的情况下,你已经很难再有大的突破了。很显然,是时候关注前端的性能了。

  首先,关注前端性能所能带来的整个性能提升到底有多少。如果我们能把后台的响应时间再优化,再砍掉一半响应时间,最终用户的响应时间也只是相应减少5% ~10%John注:因为后台的性能只能是减少那个20%的响应HTML源文件请求)。相反的,如果我们把前端的性能提高一倍,我们可以把整个响应时间减少40~45%

  再说了,前端的改进一般不需要太多的时间和资源。而优化后台的性能时间基本上都得深入应用的构架和代码,找出优化的关健代码路径,增加或者改进硬件,分布化数据库等等,这些工作动辄就要以周和月为时间单位。在接下来的章节中介绍的前端性能优化法则都来自于最佳实践,比如修改web服务器的配置文件(Chapters34);将script和样式表放置到页面中相应的地方(Chapters 5 6);将图片,script,样式表组合到一起(Chapters 1)。这些实践只需要以小时和天为工作单位--远比提高后台性能需要的时间成本要低。

  第三点,前端性能的这些实践都是经过实际考验的。在Yahoo!中有超过50个小组用这些实践减少了25%甚至更多的最终用户响应时间。在一些案例中,我们分析了运用这些要点的网站,一般都能达到25%或更多的性能提升。

  在开始每个新的性能提升项目时,我都会画一张类似图A-1的图表来阐释一下性能的黄金法则:

  只有10-20%的最终用户响应时间是花在了下载HTML源文件上。其它的80-90%是花在了下载页面中的所有组件上

  俺把这句浓缩一下,就是:80-90%的用户等待时间是来自于前端的页面加载

  这本书后面的部分将会提供详细的指导法则去减少这80-90%的最终用户响应时间。为了说明这些法则,我会陆续提到很多相关的技术:HTTP headersJavaScriptCSSApache等等。

  因为一些HTTP的基本概念对理解这本书非常有必要,所以我会在Chapter B中重点简介。

  在这之后就是分章节的14条优化法则。我把这些法则按优先级列出。每条法则都有自己的适应范围。举个例子,法则2更适合商业网站而不适合个人站点。如果您应用所有适合您网站的的优化法则,您可以将页面速度提升25-50%从而增强用户体验。这本书的最后章节会告诉您如何以性能的眼光去分析美国的10大网站。

What is scalability ?

| | Comments(0) | TrackBacks(0)

     什么是scalablity? scalablity译成中文是:可扩展性,伸缩性。

      很多人都在谈论系统性能,系统可用性,但我觉得scalablity(可扩展性)并不是这些,虽然我们仍然需要关注速度,性能,高可用性,程序平台,网络等等,但这些并不是scalablity的全部。
     scalablity,简单的说,就是关于您如何把事情做的更大。扩展(scaling)一个web应用就是指怎么做才能使您的系统允许更多的人去访问或使用您的应用。
  在目前的业内实践中,有两种扩展WEB应用的方法:

  • "垂直扩展--Vertical Scalability" - 通过增加资源来增强系统的处理能力。如:给现有的系统升级CPU,或是为RAID/SAN扩充更多的硬盘。
  • "横向扩展--Horizontal Scalability" - 增加多个应用处理单元的机器。一般是集群,分布式文件系统,负载均衡的形式来横向扩展系统。

       每一个组件,无论是CPU,服务器,存储系统或是负载均衡,都还是会有一些运营上的开销。当您尝试扩展您的系统时,您一定要清楚到底哪些升级或扩展是对您系统影响比例最大的,这要经过一些测量。我们把比这些测量的结果叫做"可扩展性因子"。比如:您每为系统增加一个CPU,但因为一些开销要耗掉这个CPU 5%的性能,实际上您的可扩展性因子是0.95,也就意味着您只能使用到这个CPU 95%的资源。

  • 如果可扩展性因子是一个常量(=1),我们就称之为 "线性扩展linear scalability".
  • 但实际上有很多组件的扩展性并不太好,如上面介绍的增加CPU时的有非功能性开销的例子. 如果扩展性因子小于 1.0,我们就称之为 "子线性扩展性-sub-linear scalability".
  • 有一种情况比较少见,就是通过增加组件而获得性能(扩展性因子>1)的大幅度提升(如把在多个磁盘上的I/O操作移到一个RAID上),这种情况我们称之为 "超线性扩展supra-linear scalability".
  • 还有一些设计的不好的应用,根本不适合扩展,扩展的努力只会让系统更糟 ,我们就称之为"负可扩展性negative scalability".

      如果您需要扩展系统,垂直扩展是最简单的方式(只要您银行存款足够,这事就成了)。一般在不做代码优化的情况下,您可以把您的系统程序迁移到一个超级的,也更贵的64位CPU的服务器上;也可以把存储系统换成EMC,日立或Netapp的产品。但是垂直扩展只会让您的花费越来越大。

  横向扩展,则不一定要相当数量的昂贵服务器。因为它可以通过普通的机器和存储硬件以达到规模效应来解决问题,像早期的Yahoo!,Google都是这样。横向扩展不仅仅是便宜,它是把应用程序构建在多个服务器组成的一个大server的基础上,这样就会随之有两个常见的问题: "Split brain"(功能,数据分割) 和 "hardware failure"(硬件故障,毕竟机器太多了)。

  无限的线性横向扩展是很难实现的,无限的垂直扩展则更不可能了(硬件的性能都有极限)。如果您正在建设中的系统事先无法确定用户的数量,那么用垂直扩展比较明智。但如果您正在建立一个给百万网友使用的WEB应用,那垂直扩展是一个错误,它实在太昂贵了。

  可扩展性不仅仅是关于CPU(处理能力)的,一个成功的可扩展的WEB应用,它的所有层都应该能够方便的扩展,如:存储层(文件集群系统),数据层(数据分区,数据联合),应用层(memcached,scaleout,terracota,tomcat集群等),WEB层,负载均衡层,防火墙。举个例子,如果您对您未来的WEB应用网络负载没有考虑负载均衡,那么花再多的钱在WEB层的横向扩展上都是浪费,因为您的WEB访问的将会被瓶颈限制,而这时只有负载均衡能解决问题。

  选择一个正确的可扩展性方案取决于您的系统应用规模和您的银行帐户情况。如果有人说他有一个能解决所有问题的方案,那人肯定是个骗子或是外行。如果您再参加某人的"可扩展性"讨论,您一定要先问问他:What is scalability ?

More to say:

  • Always try to identify the worst case scenarios.

  • Always build for the worst case scenarios.

  • Always test the known worst case scenarios.

And , always have a back-up plan!


      更多资料:http://www.royans.net/arch/

最近的留言

John发表于2008北京车展: 你是要看车,还是看美
bannoorse发表于2008北京车展: 太好看了啊啊啊 希
易风行发表于宇宙大爆炸(四):宇宙的模样: 这片子真的不错,很值
John发表于宇宙大爆炸(四):宇宙的模样: 自此为止,《宇宙大爆
星迷发表于周星驰的下一个梦想是什么?: 人因梦想而伟大
John发表于数据恢复中....: 呵,谢谢了,已发过的
风南发表于数据恢复中....: 我rss里还有一些片
风南发表于数据恢复中....: 唉。早知道我把那几篇