web 性能学习

一、http了解和关联内容

1、压缩-- Accept-Encoding: gzip,deflate;

2、条件GET请求-- Last-Modified头(If-Modified-Since头将最后修改时间发送给服务器);如果日期没有变化,服务器会返回一个"304 Not Modified"状态码并不再发送响应体。

3、Expires--( 明确指出浏览器是否可以使用组件的缓存副本) ;当浏览器看到响应中有一个Expires头时,它会和响应过期时间组件一起保存到其缓存中,只要组件没有过期,浏览器就会使用缓存版本而不会进行任何HTTP请求。

4、Keep-Alive: (持续链接[Persistent Connection]),可以使浏览器在一个单独的链接上进行多个请求;浏览器和服务器使用Connection头来指出对Keep-Alive的支持。


备注:更多地http信息可以参考:

  HTTP规范:http://www.w3.org/Protocols/rfc2616/rfc2616.html和《HTTP:The Definitive Guide》(http://www.oreilly.com/catalog/httptdg)


二:减少http请求

前述:从页面上移除组件会引起性能和产品设计的矛盾,所以这个方法要谨慎使用;通过下面的方法暂时可以解决以上这个的冲突,又可以减少http的请求。

1、图片地图(Image Map): 一个图片上可以关联多个URL,这样目标URL的选择取决于用户单击了图片上的哪个位置;

  测试:

  (1) 无图片地图:http://stevesouders.com/hpws/imagemap-no.php

  (2)有图片地图:http://stevesouders.com/hpws/imagemap.php

  

  分类:图片地图也分为服务器端和客户器端,服务器端可以根据x、y坐标来映射到适当的操作,客户端的映射可以通过HTML的MAP标签实现而不用向后端应用发送请求。

  缺点:定义图片地图上的区域坐标时,手工的方式容易出错,并且除了矩形无法定义其他形状,通过DHTML创建的图片地图则在Internet Explorer中无法工作(2009年前的测      试,之后的需验证)。

2、CSS Sprites

  显灵板:将多幅图片合并为一幅单独的图片

  示例:http://stevesouders.com/examples/sprites.php

  备注:

  [占卜写板]:任何支持背景图片的HTML元素,比如SPAN或DIV,使用CSS的background-position属性,可以将HTML元素放置到背景图片中期望的位置上。

  例如:

    〈div style="background-image:url('a_lot_of_sprites.gif'); background-position:-260px -90px;width:26px;height:24px;"></div>

  和图片地图对比:速度相当;图片地图要求图片必须是连续的,但是它没有这个限制;具体的讨论可参考 Dave Shea 的《CSS Sprites: Image Slicing's Kiss of Death》;

  同时合并的图片比分离的图片要小(合并的图片降低了图片自身的开销[颜色表、格式信息、等等]);

3、内联图片(Inline Images)

  原理:使用data:URL模式可以在Web页面中包含图片但无需任何额外的HTTP请求。

  data:URL模式规范(http://tools.ietf.org/html/rfc2397)对它的描述为:“允许将小块数据内联为'立即(immediate)'数”;数据就在其URL自身之中;格式如下:

    data:[<mediatype>][;base64],<data>

     一个五角星形状的内联图片可以定义为:

    <IMG alt="Red Star" src="data:image/gif;base64,R0LGOD.........="/>  /*五角星形状的<data>需要自己再验证*/

    data:可以用在任何需要制定URL的地方,包括SCRIPT和A标签;但是主要用于内联图片。

  示例:http://stevesouders.com/examples/inline-images.php

  注意:data:URL是内联在页面中的,在跨越不同页面时不会被缓存。同时最好不要内联公司的LOGO,因为编码过的Logo会导致页面变大;这种情况下使用CSS并将内联图片   作为背景,将该CSS规则放在外部样式表中,这样数据可以缓存在样式表内部。比如:http://stevesouders.com/examples/inline-css-images.php


  缺点:不受IE支持(暂时是ie7以上的都不支持);数据大小的限制(Base64编码会增加图片的大小);

  备注:

  URL模式:http:、ftp:、mailto:、smtp:、pop:、dns:、whois:、finger:、daytime:、news:和urn:。

4、合并脚本和样式表

  分离脚本示例:http://stevesouders.com/examples/combo-none.php

  合并脚本的示例:http://stevesouders.com/examples/combo.php

  合并脚本和样式表的生成过程:简单地将适当的文件链接为一个单独的文件,可以对文件进行精简合并文件很容易;当组合数量增长时,比如有大量需要不同模块的页面,组     合后的数量会很庞大的,所以要确保页面组合的数量是可以管理的。[遵循编译型语言的模式,保持JavaScript的模块化,从一组特定的模块生成一个目标文件]


二、使用内容发布网络(Use a Content Delivery Network)

  前述:服务器放置地理位置分离时,记住不要首先尝试使用分布式架构重新设计自己的web应用程序;因为重新设计将带来很多难以执行的任务比如同步会话状态和在服务器放置地点之间复制数据库事务。当重新设计这一步骤推迟无法达到缩短用户和我们系统的内容之间的距离时,可以考虑使用页面组件的web服务器(Component Web Server)离用户更近,则多个HTTP请求的响应时间将缩短;所以可以首先考虑将组件web服务器分散开;具体的技术可以使用内容发布网络(Content Delivery Network),简称CDN。

  向特定用户发布内容的服务器的选择基于对网络可用度的测量:比如,CDN可能选择网络阶跃数最小的服务器,或者具有最短响应时间的服务器。

  所以可以寻找一些合适的CDN服务提供商;比如Akamai、Mirror Image、Limelight、SAVVIS;免费的Globule(http://www.globule.org)、CoDeeN(http://codeen.cs.princeton.edu)、CoralCDN(http://www.coralcdn.org);  注意在部署这些CDN服务时最好不要使用HTTP重定向来将用户指向到本地服务器,因为那样会使Web页面反应速度变慢。

CDN优点:缩短响应时间,还可以备份、扩展存储能力和进行缓存;还有助于缓和Web流量峰值压力,比如获取天气或股市新闻、浏览流行的体育或娱乐事件时。

缺点:响应时间有可能受到其他网站(比如自己的竞争对手流量的影响),因为CDN服务提供商在其所有客户之间共享其web服务器组;同时不能直接控制组件服务器所带来的特殊麻烦,比如:修改HTTP响应头必须通过服务提供商来完成;还有如果CDN服务的性能下降了,自己的工作质量也随之下降(解决这个问题可以同时使用多个服务提供商)。

注意:CDN用于发布静态内容,如图片、脚本、样式表和Flash。提供动态的HTML页面会引入特殊的存储需求----数据库链接、状态管理、验证、硬件和OS优化等等;这些复杂性超越了CDN的能力范围。另外一方面是静态文件更容易存储并具有较少的依赖性。

使用CDN和不使用CDN对比示例:CDN http://stevesouders.com/hpws/ex-cdn.php;   无CDN  http://stevesouders.com/hpws/ex-nocdn.php.(注意,测试的结果也取决于链接速度)。


三、添加Expires头(Add an Expires Header)

  HTTP1.1引入了Cache-Control头来克服Expires头的限制。因为Expires头使用一个特定的时间,它要求服务器和客户端的时钟严格同步;另外,过期日期需要经常检查,并且一旦未来这一天到来,还需要在服务器配置中提供一个新的日期。

Cache-Control使用max-age指令指定组件被缓存多久,如果从组件被请求开始过去的描述少于max-age,浏览器就使用缓存的版本。

<FilesMatch "\.(gif|jpg|js|css)$">ExpiresDefault "access plus 10 years"</FilesMatch> 图片、脚本和样式表的过期时间被设计为自请求开始的10年之后;

时间可以以年、月、周、日、小时、分钟、秒为单位来设置。它同时向响应中发送Expires头和Cache-Control max-age头;实际的过期日期根据何时接到请求而变,Cache-Control具有优先权并且明确指出了相对于请求时间所经过的秒数,始终同步问题也被避免。

空缓存VS完整缓存(Empty Cache vs Primed Cache): 页面是否缓存了当前页面的组件。

不仅仅是图片(More Than Just Images):一般都对图片是用长久的Expires,但是长久的Expires头应该包含任何不经常变化的组件,包括脚本、样式表和Flash组件等;但是HTML文档不应该长久的Expires头,因为它有可能包含动态内容;

修订文件名(Revving Filenames):为了确保用户能获取组件的最新版本,需要在所有HTML页面中修改组件的文件名。Mark Nottingham的"Caching Tutorial for Web Authors and Webmasters"写有:最有效的解决方案是修改其所有链接,这样全新的请求将从原始服务器下载最新的内容。

  解决上面这个问题可以将版本号嵌在组件的文件名中(例如yahoo_2.0.6.js),而且在全局映射中修订过的文件名会自动更新。潜入版本号不仅可以改变文件名,还能在调试时更容易地找到准确的源代码文件。

无Expires示例:http://stevesouders.com/hpws/expiresoff.php;  长久的Expires的: http://stevesouders.com/hpws/expireson.php


四、压缩组件(Gzip Components)

1、How Cpmpression Works

  浏览器的支持:Accept-Encoding头来标识对压缩的支持;  Accept-Encoding: gzip, deflate.

  Web服务器通过响应中的Content-Encoding头来通知Web客户端: Content-Encoding:gzip.

  gzip-->GNU项目开发的一种免费的格式;并标准化为RFC1952.

2、What to Compress

  一般情况下可以压缩脚本、样式表;但是比较值得压缩的文件还应包括XML和JSON在内的任何文本响应;值得注意的是图片和PDF不应该压缩,因为它们本来就已经被压缩了;试图对它们压缩只会浪费CPU资源,还有可能会增加文件大小。

3、节省(The Saving)

  压缩通常能将数据量减少;

4、配置(Configuration)

  iis、apache各有不同的配置方法。

5、代理缓存(Proxy Caching)

  假设某个URL发送到代理的第一个请求来自于一个不支持gzip的浏览器,这个是到达代理的第一个请求,则该缓存为空;代理会将请求转发到web服务器;此时服务器的响应是未经过压缩的,就是这个没有压缩的响应被代理缓存起来并发给浏览器;现在,假设到达代理的第二个请求访问的是同一个URL,来自于一个支持的gzip的浏览器;代理会使用其缓存中(未经压缩)的内容进行响应,这样就没有使用压缩的意义; 如果上面的操作相反,在这种情况下,代理的缓存中拥有内容的一个压缩版本,并将这个版本提供给后续的浏览器,而不管它们是否支持gzip。解决这个代理缓存的方法是在web服务器的响应中添加Vary头。Web服务器可以告诉代理根据一个或多个请求来改变缓存的响应,由于压缩的决定是基于Accept-Encoding请求头的,因此需要在服务器的Vary响应头中包含Accept-Encoding。    Vary: Accept-Encoding。

这将使得代理缓存响应的多个版本,为Accept-Encoding请求头的每个值缓存一份。在前面的例子中,代理会缓存每个响应的两个版本----Accept-Encoding为gzip时的压缩内容和没有指定Accept-Encoding时的非压缩内容。当浏览器带着Accept-Encoding:gzip访问代理时,它接收到的是压缩过的内容。没有Accept-Encoding请求头的浏览器收到的是未经压缩的内容。默认情况下,mod_gzip会向所有响应添加Vary:Accept Encoding头,以驱使代理执行正确的操作。示例:http://www.w3.org/protocols/fec2616-sec14.html#sec14.44。

6、边缘情形(Edge Cases)

  这里主要涉及时服务器和客户端的压缩对等性,两者是必须正确的。比如发生一些错误(发送压缩内容到不支持它的客户端、忘记将压缩内容声明为已经进行了gzip编码等),页面都会被破坏。错误并不会经常发生,但是他们是必须考虑的边缘情形(Edge Case)。处理这些复杂的情况,最佳的做法是将User-Agent作为代理的另外一种评判标准添加到Vary头中。当mod_gzip检测到你在使用浏览器白名单时,它会自动将User-Agent字段添加到Vary头。问题时User-Agent又上千种不同的值;代理不太可能为其所代理的所有URL缓存Accept-Encoding和User-Agent的全部组合。mod_gzip的文档有说明:"使用对User-Agent HTTP 头进行求值的过滤器规则将会导致完全禁用为响应包进行的缓存"。因为这实际上破坏了代理缓存。另外一种方式是使用Vary:*或Cache-Control: private头来禁用代理缓存。因为Vary:*头防止了浏览器使用缓存的组件,最好使用Cache-Control: private, google和yahoo!都使用了这个方式。这种方式是为所有浏览器禁用代理缓存,因此会增加带宽开销,因为代理无法缓存你的内容。

默认的情况下ETag不能反映出内容是否被压缩,因此代理可能会向浏览器提供错误的内容;最好的解决办法是禁用ETag。

压缩示例:

无: http://stevesouders.com/hpws/nogzip.html

压缩html示例:http://stevesouders.com/hpws/gzip-html.html

压缩所有组件:http://stevesouders.com/hpws/gzip-all.html


五、将样式表放在顶部(Put Stylesheets at the top)

1、sleep.cgi,实现逐步展示的效果: 展示延迟的组件如何影响Web页面;参数:

  sleep: 将响应延迟多长时间(按秒计)。默认值为0.

  type: 返回的组件类型,可取的值只有gif、js、css、html和swf。默认值为gif。

  expires: -1返回已过时的Expires头,0不返回Expires头,1返回一个未来的Expires头。 默认值为1.

  last: 取-1返回一个last-modified头,其时间戳和文件的时间相等。取0则不返回last-Modified头。 默认值为-1.

  redir: 取1则产生一个302响应,重定向回同样的、不带redir=1的URL。

<img src="/bin/sleep.cgi?type=gif&sleep=2&expires=-1&last=0" >

<link rel="stylesheet" href="/bin/sleep.cgi?type=css&sleep-=1&expires=-1&last=0">

图片和样式表都使用expires=-1选项时,会得到一个已过期的Expires头的响应,这样就避免了组件被缓存,你可以重复运行这个测试,每次都能得到同样的体验(可以认为每个组件的URL添加了唯一的时间戳,进一步防止缓存)。为了减少这个测试中的变量,我指定了last=0来从响应中移除last-Modified头。

2.为了防止css加载出现的白屏,应该尽量把css放在文档顶部head中。当浏览器的行为不同时,可以参考http://www.w3.org/tr/html4/struct/links.html#h-12.3。

  [link]只能出现在文档的head节中,出现的次数是任意的;为了兼容历史的问题,浏览器支持违反HTML规范的页面,这样老旧的、不规整的页面就能够浏览;

但是最好是把link放在header中,如果样式表不要求呈现页面,可以想办法在文档加载完毕后动态加载进来;参考后续的《使用外部的javascript和css》.


六、将脚本放在底部 (Put Scripts at the Bottom)

这样做可以使得页面逐步呈现,也可以提高下载的并行度。

具体问题具体分析:(示例:  http://stevesouders.com/hpws/js-middle.php)

脚本带来的问题:

  上面这个示例页面的下半部分要花大约10秒才能显示出来;出现这个一现象是因为脚本阻塞了并行下载;

  另外一个问题是逐步呈现,在使用样式表时,页面逐步呈现会被阻止,知道所有的样式表下载完成,所以要把样式表放到文档的head中,这样就能首先下载而不会阻止页面呈现;使用脚本时,对于所有位于脚本一下的内容,逐步呈现都被阻塞;将脚本放在页面越靠下的地方,越多的内容能够逐步地呈现。

并行下载:

  当缓存为空时,页面上每个组件都会产生一个http请求,针对于浏览器有并行地执行http请求,为什么浏览器不能一次将它们都下载下来呢?

下面来看http 1.1规范,规范中建议浏览器从每个主机名并行地下载两个组件;但是一般情况下,很多web页面需要从一个主机名下载所有的组件;当查看这些http请求会发现它们是呈阶梯状的;(注意,每个主机名并行下载两个组件的限制只是一个建议,默认情况下,ie和firefox都会遵守这样的规范);ie用户可以重写这个默认值,文件是放在:Registry Editor中["how to configure internet explorer to have more than two download sessions", http://support.micorosft.com/?kbid=282402];  firefox可以使用about:config页面中的network.http.max-persistent-connections-per-server设置来修改这一默认设置。但是增加并行下载数量,对主机的带宽和cpu速度影响也大,过多的并行下载反而会降低性能;经过测试使用两个主机名比使用1、4、10个主机名带来更好的性能。

脚本阻塞下载:在下载脚本时,并行下载实际上是被禁用的----即使使用了不同的主机名,浏览器也不会启动其他的下载;其中的一个原因是,脚本可能使用document。write来修改页面的内容,因此浏览器会等待,以确保页面能够恰当地布局;另外一个原因可能是为了保证脚本能够按照正确的顺序执行,如果并行下载多个脚本,就无法保证响应是按照特定顺序到达浏览器的;例子:http://stevesouders.com/hpws/js-blocking.php.

脚本放在顶部:这样脚本会阻塞对其后面内容的呈现,会阻塞对其后面组件的下载;


七:避免CSS表达式

  ie浏览器支持css表达式,一般情况下,在css中使用表达式可以做到跨浏览器的效果,比如:ie不支持min-width,这个属性,可以通过使用表达式达到某个效果,主要是通过css频繁求值来得到某个结果,但是这样一来,会导致css表达式的底下性能。

更新表达式:表达式的求值频率不只是在页面呈现和大小改变时求值,当页面滚动、甚至用户鼠标在页面上移过都要求值。监测css求值可通过计数器来监测,例子http://stevesouders.com/hpws/expression-counter.php。通过这个例子可以可以看到css中使用表达式是很危险的。还有上面这个例子在internet explorer中单击这个页面中的文本框之后,不得不中止这个进程。

避免css表达式:创建一次性表达式和使用时间处理器取代css表达式。

  一次性表达式(One-Time Expressions):

  例子,

  <style>
    p{
 background-color:expression(altBgcolor(this));
    }
  </style>
  <script type = "text/javascript">
    function altBgcolor(elem)
{
 elem.style.backgroundColor = (new Date()).getHours() % 2 ? "#F08A00" : "#B8D4F";
}

  </script>

  css表达式调用了altBgcolor()函数,而该函数将样式的background-color属性设置为一个明确的值,并移除了CSS表达式。

  页面url:http://stevesouders.com/hpws/onetime-expression.php

事件处理器(Event Handlers):为了替代CSS表达式的替代方法;例子:http://stevesouders.cm/hpws/event-handler.php

有时css表达式也会影响页面的加载时间;所以能够不使用CSS表达式的,尽量可以不使用,避免使用。


八:使用外部的JavaScript和CSS(Make JavaScript and CSS External)

内联合外部的js和css的比较:http://stevesouders.com/hpws/inlined.php     http://stevesouders.com/hpws/external.php

但是实际上使用时,因为考虑到js和css文件有机会被浏览器缓存起来;可以通过下面这几种方式实现外部js和CSS组件被缓存的频率的量化,进行衡量:

页面查看:随着用户每个时间段查看次数或者用户每回话产生的页面查看次数的增长而增长。

空缓存VS完整缓存(Empty Cache vs. Primed Cache):能够为用户带来高完整缓存率的,使用外部文件的收益更大。

组件重用(Component Reuse):

比较折中的做法是把页面划分成几种页面类型,然后为每种类型创建单独的脚本和样式表;而这样可比维护一个单独的文件要复杂,但通常比为每个页面维护不同的脚本和样式表要容易,并且对于给定的任意页面都只需下载很少的多余的JavaScript和CSS。

所以你自己做出的与JavaScript和CSS外部文件的边界相关的决定影响着组件的重用程度。

典型的对比结果(Typical Results in the Field):反复加载使用了完整缓存的页面,并与上面"内联JS和CSS"的结果进行比较,可以看出使用带有长久Expires头的外部文件是最快的方式。可缓存的外部JS和CSS的示例 http://stevesouders.com/hpws/external-cacheable.php

主页特例(选择作为浏览器默认页的URL,如Yahoo!的主页(http://www.yahoo.com));页面查看,只要浏览器打开,主页就被访问了,然而,通常每个会话只有一个页面查看;空缓存 VS 完整缓存,完整缓存的百分比要比其他网站更低;组件重用,因为是用户来到网站后访问的唯一一个页面,所以可以忽略重用;综合这些基准,主页可以倾向使用内联而不是外部文件;同时主要要求响应能力更高。

主页加载完后再加载外部组件:onload事件,例子:http://stevesouders.com/hpws/post-onload.php;本例子中的组件加载了两次,但是有时候会出现一些问题,我们可以将这些组件放到一个不可见的IFrame中是一种更好的方式。

动态内联:判断是否有cookie来解决。


九:减少DNS查找(Reduce DNS lookups)

DNS允许用户使用同样的主机名来连接到新的服务器;或者可以这样子理解,可以将多个ip地址关联到一个主机名,为网站提供高冗余度;但是DNS也是开销的,在查找时,不允许下载任何组件,响应时间依赖于DNS解析器(通常由我们的ISP提供)、所承担的请求压力、距离、和带宽速度都有关系。

DNS缓存和TTL(DNS Caching and TTLS):当浏览器缓存丢弃了记录时,才会向操作系统询问地址。

影响DNS缓存的因素(Factors Affecting DNS Caching):服务器可以表明记录可以被缓存多久;查找返回的DNS记录包含了一个存活时间(Time-to-live, TTL)值。该值告诉客户端可以对该记录缓存多久。但是浏览器通常忽略该值,并设置它自己的时间限制;Keep-Alive特性可以同时覆盖TTL和浏览器的时间限制;所以只要浏览器和Web服务器愉快地通信着,并保持着TCP连接打开的状态,就没有理由进行DNS查找。

TTL值:这个不同网站有不同的值;我们使用时建议值是1天;

客户端受到DNS记录的平均TTL值只有最大TTL值得一半。因为DNS解析器自身也拥有与DNS记录相关的TTL;

浏览器的视角(The Browser's Perspective):

查看DNS Client服务----- ipconfig /displaydns   ipconfig /flushdns; 浏览器自身的dns缓存,ie:有三个注册表设置控制(DnsCacheTimeout、KeepAliveTimeout和ServerInfoTimeOut  [HKEY_CURRENT_USER\Software\Microsoft\Windows\CurrentVersion\InternetSettings\]);firefox具有下列配置设置:network.dnsCacheExpiration -- 1分钟,network.dnsCacheEntries--20分钟,network.http.keep-alive.timeout-----5分钟。

通过Keep-Alive和较少的域名来减少DNS查找。


十:精简JavaScript(Minify JavaScript):

精简工具:JSMin(http://crockford.com/javascript/jsmin), Dojo Compressor等等;

锦上添花(Icing on the Cake):既精简,也使用gzip压缩


十一:避免重定向(Avoid Redirects):

当Web服务器向浏览器返回一个重定向时,响应中就会拥有一个范围在3xx的状态码;这表示用户代理必须执行进一步操作才能完成请求。常见的有 :300 301 302 304 305;浏览器会自动将用户带到Location字段所给出的URL,重定向所必需的所有信息都出现在这个头中;js也可以设置重定向,location字段所给出的URL;根据w3c标准,最好是使用标准的3xx HTTP状态码,为了使后退按钮能够正确工作。

重定向损伤性能:重定向延迟加载严重,它延迟了整个HTML文档的传输。在HTML文档到达之前,页面中不会呈现出任何东西,也没有任何组件被下载;在用户和HTML文档之间插入重定向延迟了页面中的所有东西;重定向通常伴随着对HTML文档的请求一起使用,但偶尔也会看到蒋它们用于页面中的组件。

常用的重定向替代的解决方法-----

缺少结尾的斜线:URL的结尾必须出现斜线(/)而没有出现时;如果你的网站包含目录并使用了自动索引,用户就必须忍受一个到达预期页面的重定向;检查web日志就能看到发出了多少301状态码,这样可以认识到去解决缺少结尾斜线的问题。

连接网站:整合后端来实现连接,比如使用 Alias、mod_rewrite、DirectorySlash和直接代码连接;

跟踪内部流量:链接导航去向,可通过web日志查看状态;另外一种选择使用Referer日志来跟踪流量去向,但是这个方法分析要多所有出口的url分析,对于内部流量,值得建立Referer日志来避免重定向,比此节省最终用户响应时间。

跟踪出站流量:1、重定向。2、使用信标(beacon)-----  一个HTTP请求,其URL中包含有跟踪信息,跟踪信息可以从信标Web服务器的访问日志中提取出来;信标也有两种,"图片信标(http://stevesouders.com/hpws/redir-beacon.php)"和"XMLHttpRequest 信标(http://stevesouders.com/hpws/xhr-beacon.php)",虽然这些方式对于很多链接来说过于复杂,但对于使用了target属性的链接而言,它们也能很好地工作--------  <a href="http://en.wikipedia.org/wiki/performance" onclick ="resultBeacon(this)" target="_blank"> Performance - Wikipedia</a>;在这种情况下不会出现竞态情形,简单的图片信标就能很好地工作。例如在跟踪弹出式广告的点击(单击)量时,这种方式就很好;单机弹出式广告不会卸载当前文档,图片信标请求能够顺利完成而不会被中断。

美化URL(Prettier URLs):使用重定向的另一种动机是使URL更加美观并且易于记忆。 比如前面说的URL结尾不带"/",关键是能找到一种方式,无需重定向就能拥有如此简洁的URL;与其让用户忍受额外的HTTP请求,最好还是使用“链接网站”中介绍的Alias、mod_rewrite、DirectorySlash和直接链接代码来避免重定向。


十二:移除重复脚本(Remove Duplicate Scripts)

重复脚本对性能两种影响:不必要的HTTP(IE在没有缓存下回出现)请求和执行JavaScript所浪费的时间;

示例:重复脚本无缓存,http://stevesouders.com/hpws/dupe-scripts.php; 重复脚本有缓存:http://stevesouders.com/hpws/dupe-scripts-cached.php

总体来说:(1)在页面中多次包含相同的脚本会使页面变慢;(2)在IE中,如果脚本没有被缓存,或在重新加载页面时,会产生额外的HTTP请求;(3)在Firefox和IE中,脚本会被多次求值。

避免意外包含同一个脚本两次的方法:在模版系统中实现一个脚本管理模块。包含脚本的典型方式是在HTML页面中使用script标签;通过其他导入脚本的方法,可以使用某个变量存储脚本的名称,用于之后的判断;对于脚本依赖问题,可以使用哈希表或数据库来搜集依赖关系,要判断所依赖的脚本是否有插入,如果没有就插入依赖的脚本;对于简单网站可以手动地维护依赖关系;对于复杂的网站,可以选择通过扫描脚本查找符号定义来自动生成依赖关系;最后脚本加载到页面上。 对于脚本管理模块,可以将加载脚本的功能集中在某个方法,对脚本组件添加版本号,一旦脚本发生变化,只要简单更新某个方法,所有的页面就可以开始使用新的文件名了,无需修改程序里面其他任何功能模版就可以让页面开始使用新的版本。


十三:配置ETag(Configure ETags):

问题:当网站被宿主在多于一台服务器上时,ETag头可能会阻碍缓存。

ETag:实体标签,是Web服务器和浏览器用于确认缓存组件的有效性的一种机制。

组件缓存:Expires Header.

条件GET请求:缓存组件过期了,浏览器在重用它之前必须首先检查它是否仍然有效,这样就有一个条件GET请求;浏览器必须产生这个HTTP请求,执行有效性检查,但是仍比简单地下载所有已过期的组件效率要高。如果浏览器缓存中的组件是有效的(即它能够和原始服务器上的组件相匹配),原始服务器不会返回整个组件,而是返回一个“304 Not Modified”状态码。服务器在检测缓存的组件是否和原始服务器上的组件匹配时有两种方式------比较最新修改日期;比较实体标签。

最新修改:Last-Modified响应头来返回组件的最新修改日期。如, 

---------- > GET /i/yahoo.gif  HTTP 1.1        Host: us.yimg.com 

<----------  HTTP 1.1 200 OK        Last-Modified:tue, 12 Dec 2010 03:03:59 GMT        Content-Length:1195

示例中,浏览器缓存了组件以及它的最新修改日期,下一次请求http://us.yimg.com/i/yahoo.gif时,浏览器会使用IF-Modified-Since头将最新修改日期传回到原始服务器以进行比较;如果原始服务器上组件的最新修改日期与浏览器传回的值匹配,会返回一个304响应,而不会传送1195字节的数据。

---------- > GET /i/yahoo.gif HTTP 1.1        Host: us.yimg.com        If-Modified-Since: Tue,12 Dec 2010 03:03:59 GMT 

<----------  HTTP 1.1 304 Not Modified

实体标签:Entity Tags

ETag提供另外一种方式,用于检测浏览器缓存中的组件与原始服务器上的组件是否匹配("实体"是我们之前提到的“组件”的另一种称呼)。ETag在HTTP 1.1 中引入,ETag是唯一标识了一个组件的一个特定版本的字符串。唯一的格式约束是该字符串必须用引号引起来原始服务器使用ETag响应头来制定组件的ETag.

----------> GET /i/yahoo.gif HTTP 1.1

                 Host: us/yimg.com

<---------- HTTP 1.1 200 OK

                 Last-Modified: Tue, 12 Dec 2010 03:03:59 GMT

                 ETag: "10c24bc-4ab-457e1c1f"

                 Content-Length: 1195

ETag的加入为验证实体提供了比最新修改日期更为灵活的机制。例如,如果实体依据User-Agent或Accept-Language头而改变,实体的状态可以反映在ETag中。

此后,如果浏览器必须验证一个组件,它会使用If-None-Match头将ETag传回原始服务器。如果ETag是匹配的,就会返回304状态码,使响应减小了1195字节。

----------> GET /i/yahoo.gif HTTP 1.1

                 Host: us/yimg.com

                 If-Modified-Since: Tue, 12 Dec 2010 03:03:59 GMT

                 If-None-Match: "10c24bc-4ab-457e1c1f"

<---------- HTTP 1.1 304 Not Modified

ETag的问题在于,通常使用组件的某些属性来构造它,这些属性对于特定的、寄宿了网站的服务器来说是唯一的。当浏览器从一台服务器上获取了原始组件,之后,又向另外一台不同的服务器发起条件GET请求时,ETag是不会匹配的----而对于使用服务器集群来处理请求的网站来说,就是常见了。所以对于拥有多台服务器的网站,Apache和IIS向ETag中嵌入的数据都会大大地降低有效性验证的成功率。ETag还降低了代理缓存的效率。代理后面的用户缓存的ETag经常和代理缓存的ETag不匹配,这导致不必要的请求被发送到原始服务器。用户和代理之间不会出现304响应,而是会产生两个200响应----一个是从原始服务器到代理的,另一个是从代理到用户的。ETag的默认格式还可能会引入安全性弱点[http://www3.ca.com/securityadvisor/vulninfo/vuln.aspx?id=7196的:Apache http daemon file inode disclosure vulnerability]。

If-None-Match比If-Modified-Since具有更高的优先级。如果ETag不匹配最新修改日期是相同的,也能发送一个“304 Not Modified”响应,但实际并不是这样。依据HTTP 1.1规范(http://www.w3.org/Protocols/rfc2616-sec13.html#sec13.3.4),如果请求中同时出现了这两个头,则原始服务器禁止(MUST NOT)返回304(Not Modified),除非请求中德条件头字段全部一致"。实际上如果根本没有If-None-Match头反而会更好一些。

对于是否使用ETag:即便添加了组件具有长久的Expires头,一旦用户单击了Reload或Refresh按钮,依然会产生条件GET请求。没有办法能够绕过它----ETag带来的问题时必须面对的;这样如果选择的就是对ETag进行配置,当代码生成脚本时,可以通过设置ETag头来反映浏览器状态,比如移除了inode(Apache)或ChangeNumber(IIS),如果组件必须通过最新修改日期之外的一些东西来进行验证,则ETag是一种强大的方法。如果无须自定义ETag,最好简单地将其移除。


十四:使用Ajax可缓存(Make Ajax Cacheable)


























  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值