Category: 性能

世界杯预测算法

探索一个预测世界杯冠军队的算法

晚上俄罗斯世界杯就要开赛了,大家伙们又要开始预测比分了,我想了一个预测世界杯冠军队算法的数学模型,有兴趣的同学可以试试看,说不定能赢足彩大奖。

预测一场比赛的胜负,若不做任何数据收集和分析,那么我们猜测胜负的概率,跟抛硬币没有太大分别(差别就是小组赛有胜、负、平三种可能,只有淘汰赛才真正跟抛硬币只有正、反两种可能相似),所以我们需要通过球员、教练、裁判的历史大数据训练出一个概率模型。

假设世界杯某球队A,按规定有球员23名,主教练员1名,那么球队A的基础赢球概率为:

模型1:P(A)=W(A).(∑P’/23.).P”      (球队常数W(A)=32/世界排名,P’为单个球员赢球概率,P”为教练员赢球概率,均可从单个球员跟教练员的历史比赛数据中得出)

这个模型符合我们常见的大牌球星与大牌教练的组合胜率高于普通球队的认识,但是,这很不够精确。

因为上面的模型我们没有考虑的是每个球员比赛数据中对手的数据,假设两个球员a和b,各有50场比赛经历,经计算胜率均为0.55,但a参加的是西甲联赛,而b参加的是中超,很显然,这个人胜率没有意义了。所以,我们对上面的数学模型进行改造:

模型2:P(A)=W(A).(∑P’R’/23.).P”        (R’为单个 球员历史对手常数,由历史对手在各国家联赛/国家队中的排名所决定)
R’=  k log ∏R(a)   (R(a)为A球队单一球员a的一场历史比赛对手的当时得分,k为常数)

目前这一版的模型已经比第一版要精确一大点了。但是………….还不够精确。

通过大数我们可以发现一些好玩的事情:假如世界杯某小组的A,B,C,D四支球队,A对B高胜率,B对C高胜率,C对D高胜率,但D却对A有高胜率,按照逻辑传递性,矛盾出现了,若B队碰上D队,到底是高胜率,还是低胜率?哥德尔不完备定理似乎出现了。

出现这种情况的原因是,我们的数据模型还不够完备,所以只单纯分析队员,或是单纯的分析球队,或是简单的组合分析都不行,因为球队是个动态数据,球队出现的队员是每年甚至每场都在变化的,所以我们还需要更复杂的数学模式和更多的数据。

我们要具体到B,D两支同组球队的对比数据,而降低那些无用历史数据的权重。

具体方法是:找出所有B球队队员参与的比赛对手中包含了D队队员的比赛数据,进行建模。(这句话很重要,请再读一遍)

模型3:  P(B|D)=W(A).(∑P'(B|D)R'(D|B)/23).P”

P'(B|D)      是B队单一球员在有D队球员为对手的比赛中的胜率
R'(D|B) =k log ∏R(D|B)       R(D)是D队球员在以B队球员为队手的球队的所有比赛中的个人打分

目前这一个模型算是比较接近真实了,但是,还不够精准,我们一直忽略了P”这个主教练的胜率,还有裁判员F的执法偏好。。。还有,上场真正踢的是11个人,他们今天用的队型是不是各个队员胜率中覆盖的队形。。。

还有一种情况就是新晋的球队,或新队员比较多的球队,其对阵数据不足的时候,那可能就要回到第二个数据模型了。

好复杂,要不说足彩500W,不是那么好挣的,就瞎掰到这儿吧, 所以说,现实是复杂的,但至少世界杯是快乐的。

晚上看球了。

”Twitter搜索现在快3倍啦“

在2010年春季,为了服务不断增长的流量、提升最终用户的响应时延和服务的可用性、有能力快速开发新的搜索功能,搜索团队开始重写我们的搜索引擎。作为不努力的一部分,我们发布了新的实时搜索引擎,将后端从Mysql迁到了lucene的实时版。上周我们发布新的前端,替换了Ruby on Rails版:我们称之为Blender的Java服务器。我们很高兴地宣布这些改变使我们的搜索时延下降了3倍,同时也使得我们有能力在接下来的几个月里快速迭搜索特性。

性能收益

Twitter搜索是世界上注量最重的搜索引擎之一,每天服务超过10亿的查询。在我们发布Blender的前一周,日本 #tsunami 产生了很大的查询负载,并产生了查询响应时间的峰值。随着Blender的发布,我们的95%时延减少了3倍,从800毫秒到250毫秒,同时前端的CPU负载降低了一半。我们每台机器的容量能服务10倍的请求增长。这也意味着我们能能够用更少的机器支持同样的请求数,从而降低前端服务的成本。

Twitter搜索现在快3倍啦

blender发布前后的95%的查询接口时延对比图

Twitter改进后的搜索架构

为了弄明白这些性能收益,你首先必须明白我们以前的RoR前端是如何地低效。前端运行固定数量的单线程工作进程,每一个做下面的事情:

. 解析请求
. 同步查询索引服务
. 聚合并展现结果

我们很早就知道,同步方式处理请求会低效地使用我们的CPU。经过一段时间,在Ruby的代码层累积欠了重大的技术债,这样很难增加新特性和改进我们搜索系统的可靠性。

Blender通过下面的方式解决了这些问题:

 创建了一个完全异步的聚合服务。没有线程会去等待网络IO完成
. 从后端服务中聚合结果,例如,实时的,最流行的微推和地理分片
. 优雅的服务之间的依赖关系处理,工作流自动处理后端服务之间的传递性依赖

下图显示了Twitter的搜索引擎架构。从网站、接口或内部客户过来的查询请求通过硬件负载均衡设备转发到Blender上。Blender解析请求,然页转发给后端服务,使用工作流来处理服务之间的依赖。最后,将后端服务返回的结果进行合并,以合适的语言呈现给客户。

Twitter搜索现在快3倍啦
Twitter跟Blender有关的搜索架构图

Blender简介

Blender基于Netty http://www.jboss.org/netty打造的Thrift和HTTP服务,Netty是用Java写的高扩展的客户和服务器库,基于它能快速地开发各种协议的客户和服务器端程序。我们没有用其它产品,如Mina和Jetty,主要是因为在其它项目中已经用过它,当然它的接口更简洁、文档也写得不错,这也是我们用它的原因之一。为了让Netty跟Trift一起工作,我们写了解析Thift的代码,专门解析从Netty的channel缓存中的过来的Thift请求,当它从socket读请求或需要封装Thrift响应时,都会调用它些代码。

Netty定义名叫通道的抽象功能,用来封装到网络套接字的连接,提供读、写和连接等IO请求的接口。所有通道的IO请求天然都是异步的。这意味着任何IO调用立即返回一个通道实例对象,以通知请求是否完成、失败或被取消。

当Netty服务器收到一个新的连接,它会创建一个通道流水线来处理这个连接。一个通道流水线不是别的,其实就是通道处理程序的序列,通过它实现处理连接的业务逻辑。在随后的一个小节里,我们会说一下Blender是如何将这些通道流水线映射到查询的工作流处理上的。

工作流框架

对于Blender来说,一个工作流就是一组后端服务,服务之间有依赖关系,每个后端服务都需要处理才能处理收到的查询请求。Blender自动解决后端服务之间的依赖,例如,如果服务A依赖于服务B,首先会请求A,把它返回的结果返回给B。工作流可以很方便地用有向无环图来表示(见下图)

Twitter搜索现在快3倍啦
6个后端服务的Blender工作流示例图

在上面的示例图中,我们有6个服务{s1,s2,s3,s4,s5,s6},之间有依赖。从S3到S1的边意味着先要访问S3,之后才是S1,因为访问S1时需要访问S3后的结果。对于这样的一个工作流,Blender框架先会进行拓扑排序以确实所有服务的顺序,它们也必須以这样的顺序依次调用。上图的服务执行顺序将是{(s3,s4),(s1,s5,s6),(s2)}。这也表明s3和s3可以在第一轮里并发调用,一旦它们返回结果,S1、S5和S6可以在第二轮里并发调用,最后才调用S2。

一旦Blender确定了工作流的执行顺序,工作流将会映射成Netty的流水线。流水线是一个处理程序序列,请求依次会通过这些处理程序。

复用进来的请求

因为工作流映射成了Netty的管道,我们需要将进来的客户端请求转发给合适的流水线。因为这个原因,我们设置了一个代理层,它负现复用并转发客户端请求到流水线上:

. 当一个远程Thrift客户端跟Blender建立一个长连接时,代理层会创建一组本地的客户,跟每个本地的工作流服务对应。注意当Blender进程起动时,会在同一个java虚拟机里起动所有的本地工作流服务
. 当从套接字上收到一个请求时,代理层读取这个请求,计算出需要请求那个工作流服务,然后转发给对应的工作流服务
. 类似地,当收到本地的工作流服务的响应时,代理层读取结果,并把结果转发给远程的客户

我们利用Netty的事件驱动模型来异步完成上面的所有任务,这样也没有任何线程需要等待IO。

分配后端请求

一旦请求转到了工作流流水线,将依次由工作流定义好的服务处理程序处理。每个服务处理程序创建一个合适的后端请求,转发给远程的服务器。例如,实时处理程序会创建一个实时搜索请求并异步地将它发给一个或多个实时索引服务器。我们使用twitter commons库https://github.com/twitter/commons(最近开源了!)来提供连接池管理、负载均衡和死主机检测。

当请求分配给所有后端之后,请求对应的IO线程就将被释放。一个定时线程每隔几毫秒就检查有没有后端的请求返回,并设置对应的标记,表明请求成功、超时或者失败了。在一个查询请求的存在周期内我们为它维持一个对象,用来记录上面这些状态信息。

成功的响应会聚合在一起,再发给工作流中的下一批处理程序。当第一批所有请求的响应的返回之后,将异步地发出第二批请求。这个过程会一直重复直到我们完成工作流或者超出工作流设置的超时时间。

你能够看出,在工作流执行期间内,没有任何线程会去等待IO。这让我们有效地利用Blender机器的CPU,并且处理大量的并发请求。我们能将大量请求并发发给后端去执行,从而也能减少请求的时延。

Blender部署及以后的工作

为了在我们的系统中增加Blender的时候,能确保提供高质量的服务。我们用老的RoR前端作为代理,转发请求给Blender集群。这样使得我们在对后边的技术进行大的调整时,给用户一个一致服务体验。我们下一阶段部署时,会从搜索的服务栈中完全去掉RoR,让用户直接去连接Blender,从而进一步降低访问时延。

原文链接:
Twitter Search is Now 3x Faster

天涯社区的进化经验(一)技术篇

设备投入
– 1999 1台服务器
• 2000年,在线用户300
• 2002年,在线用户1000
• 2003年,在线用户8000
WEB+DB共用在一台服务器上
– 2004 4台WEB 2台DB
• 2004年,在线用户2万
WEB及DB剥离。同时WEB不断增加,DB进行分库(4台WEB,2台DB)
– 2005 7台WEB 4台DB
• 2005年,在线用户14万
– 2006 11台WEB 4台squid(页面缓存) 6台DB
• 2006年,在线用户20万
– 2007 +双线接入 F5设备
实现双线双IP,购置F5实现本地及全局负载均衡,haproxy部署,动静剥离
– 现在 136台应用 38台DB
2008年,数据库双机(事务复制),IBM架构咨询。
• 2009年,在线用户40万
Memcache,数据访问层,服务器虚拟化。
• 2010年,在线用户54万

大多数网站发展初期遇到的技术问题,不是没有采用多层架构、没有使用内存缓存、没有购买好的服务器,而是数据结构及检索设计出了问题。
数据库调优是一个长期的过程,建立模拟环境,来跟踪每条请求的反应时间。

数据库设计注意事项
• 在对产品及业务深度了解的前提下进行数据库设计
• 表记录规模的增加对查询不要有性能问题
• 合理的索引(不要犯常识性错误)
• 慎用联合查询(几乎不用),通过一定的数据冗余来回避联合查询
• 必要时通过合并数据,减少结果集大小
• 尽量通过WHERE来定位分页数据
• 在设计时尽量考虑数据规模增长后的拆分问题

WEB服务扩展
• 无状态服务
– 不要用session保留用户数据,Cookie(或会话Cookie)保留用户标示,用户数据统一保留在数据库,或内存中)
• 应用拆分
– 按不同业务拆分,按流量拆分
• DNS轮询、或通过应用控制轮询

构架完善:
双链路接入双IP
– 使用了2台F5-GTM-1500做链路判断。
• 负载均衡
– 使用了2台F5-LTM-6400实现负载均衡
– LVS(IP层负载均衡)
– 反向代理(haproxy 7层负载均衡
• 动静分离
– 使用lighttpd来提供静态资源服务。
• 代理服务
– 使用haproxy提供七层负载均衡,通过ACL拆分应用。
• 页面缓存
– 2005年,使用squid-2.6在前端实现页面级缓存。
– 已经替换成varnish,增加压缩模块。
• 内存缓存
– Memcache

建立服务可用性监控
• 要预警及报警机制和故障点快速定位
– 应用运行中的错误监测
– 应用可用性监测
– 服务器及网络设备健康监控
– 网络流量监控
– 全国访问质量监测

Even Faster Web Sites: Performance Best Practices for Web Developers

这是Steve Souders继《High Performance Web Sites》 后,于今年推出的另一部新书:《Even Faster Web Sites: Performance Best Practices for Web Developers》,其实这书有六个章节是由另外八位业内专家所写:如Ajaxina.com的联合创始人Dion AlmaerBen Galbraith,JSon的创造者Douglas Crockford,还有YSlow,YUI的开发工程师等,如果说《High Performance Web Sites》定义了一般网站所通用的14条法则,而这本Even Faster Web Sites则是专对web2.0网站的一个优化的最佳实践的经验总结。

本书主要分三部分,第1到7章节是讲JavaScript的优化实践,第8到12章是讲网络性能优化实践,第13,14章是讲浏览器的具体性能优化实践,而附录部分则是对性能分析工具使用的一个介绍。

Even Faster Web Sites pdf电子版下载(右键另存为)

 

翻译:High Performance Web Sites(8)-Chapter 6

《High Performance Web Sites》 :Chapter 6

法则6:把script放到页面的下端

第五章我们将样式表放置于HTML的HEAD中以加快页面渲染。其实script也有类似的问题,但解决的方法正好相反:把script放到页面的下端,会利于页面的快速渲染。

Script所带来的问题
为了更好的说明这第6条法则,我们先看一个反例吧。把script放在HTML页面的中间位置,看看这种方式页面的下载情况,如:http://stevesouders.com/hpws/js-middle.php
这个script是睡眠10秒钟然后启动,我们访问一下这个页面,发现整个HTML已经全部download完毕,但页面在渲染到中间时,停住了,下一半仍是空白的未渲染区域。直到这个js睡眠的10秒过后,另一半页面的内容才开始渲染和下载后面所需的图片等组件。很显然,这个放在HTML中间的script阻碍了后面页面元素的下载,还阻碍了页面的渲染。
这也就是为什么我们要把所有的样式表放到HTML页面的上端HEAD处,它们才会被先下载,不会阻塞页面的渲染。而script后面的页面内容则会等待script执行完毕,才开始渲染。所以把script放置的越下,更多的页面内容才会更快的渲染。
并行下载
对下载速度影响最大的就是页面组件的数量。前几章介绍过,如果当前浏览器的缓存是空的,则每一个组件都产生一个HTTP请求。你可能会问:那为什么浏览器不一次性的请求所有的组件呢,非得一个一个的去请求?
要解释这个,就要说到HTTP/1.1规范了,它建议浏览器一次对同一个域名最多只发起2个并行下载请求.(http://www.w3.org/Protocols/rfc2616/rfc2616-sec8.html#sec8.1.4)
很多的页面连同其所包含的组件都是一个域名下的,如下图所示的组件下载的阶梯图 6-1:

 

如果一个页面的组件是分布在两个域名下的,那它的响应时间总体来说应该会快两倍,如图6-2所示的情况,会有四个组件并行下载。
通常情况下IE与Firefox都遵循了HTTP/1.1规范关于同一个域名每次只能有两个并行下载请求的建议。如果想扩展IE的下载束缚,可以参考:微软网站的文章:How to configure Internet Explorer to have more than two download sessions,” http://support.microsoft.com/?kbid=282402.
而要改Firefox的默认设置,则可以在配置页面中修改network.http.maxpersistent-connections-per-server属性.有意思的是,在对HTTP/1.0的支持中,Firefox默认的是每个域名下同时可有八个并行下载请求,也就是说可一次同时下载8个页面组件,这可比IE的默认值要大方多了。如图6-3就是显示的在Firefox下下载一个页面的示意图,这可要比6-2所示的看起来爽多了。

 

现在大多数的网站都遵循HTTP/1.1规范,增加每个域名下的并行下载数量是个办法,但我们总不能都指望着用户去调整自己浏览器的设置参数。前端工程师应该考虑把页面组件放在不同的域名下,增加并行下载最多也只是消耗一点客户端的CPU资源和带宽,除非是大量的并行下载才会影响到性能。在Yahoo!的实验下,我们发现把页面组件放在两个域名下要比划分成1,4或10个域名性能要好,具体可参见(Tenni Theurer, “Performance Research, Part 4: Maximizing Parallel Downloads in the Carpool Lane,”http://yuiblog.com/blog/2007/04/11/performance-research-part-4/.)
Script是怎么阻碍下载的
组件并行下载的好处我们都知道了。但script这个东西却可能会破坏这个和谐社会:因为script可能会用document.write语句来改变页面内容,所以浏览器会等待script执行完毕。
另一个原因是浏览器要保证script的执行顺序,先出现的script要先执行,哪怕后面的script的k数有多么小,也要等着,因为script在功能上可能会有前后关系,后面的script可能会用到前面script的某些操作。
来个例子说明
http://stevesouders.com/hpws/js-blocking.php
这个页面所包含的组件出现顺序如下:
1,host1下的图片A1
2,host2下的图片B1
3,host1下的script,等待10秒完成
4,host1下的图片A2
5,host2下的图片B2
看看是host1下的两张图片同时下载?还是其他?

 

IE和Firefox都会先下载host1和host2的图片,然后开始下载host1上的script,直到这个script下载完成,才会开始下载后面的两个图片。
最糟糕的情况:script在页面的顶部

在这种情况下将会导致页面是个空白:
• Script下的文字页面内容会被阻止渲染。
• Script下的组件也会被阻止下载
直到这个script下载并执行完毕才会解除阻止。给个实例:http://stevesouders.com/hpws/js-top.php
因为script在顶部,整个页面的渲染被阻止了,于是这个页面将会呈现一个我们在第五章中解释的空白屏。而逐步渲染是一个很关键的良好用户体验。图6-5就是这个例子的页面下载阶梯图,后面的图片组件都被阻止了。

 

最好的情况:script在页面的底部
放置script最佳的地方当然是页面的底部了。页面的内容不会被阻止渲染,可视组件能依次下载渲染。如实例http://stevesouders.com/hpws/js-bottom.php,图6-6就显示了这种方式script对页面的影响是最小的,我们可以对比一下图6-5看看,再给一个具体的比较实例吧:http://stevesouders.com/hpws/move-scripts.php是不是感觉更明显了。

 

这也不是说所有的scrript就应该放到页面底部了,如果是包含有document.write这样影响页面渲染内容的script,是不应该被放到最下面的,所以还是要以实际的页面功能、逻辑等方面来考虑,通常情况下,只要是不涉及强制更改页面渲染的js,都应该放到页面底部。
另外有一种方式是http://stevesouders.com/hpws/js-defer.php所示的defer方式,在script定义时声明,但这种只对IE有效,对firefox无效,大家知道一下就行了。

 

翻译:High Performance Web Sites(7)-Chapter 5

《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放在页面下方的情况,出现一个空白的页等待中)
以上的三个页面中,组件和个数都一样。看看下面的三个页面的组件下载时序图:

 

第一个和第三个,都是在最后才下载样式表,这样就会导致在IE中,页面在6.3秒前都是不会被渲染的,而第二个页面,会最先下载样式表,这时页面就会开始渲染了。

现在我们知道该怎么做了。把样式表以link文件的形式放进HEAD吧。

相关章节:

翻译:High Performance Web Sites(6)-Chapter 4

《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 Savingsgzip压缩后,返回的数据量一般会减少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
下图就是三者的具体数据量对比和时间对比: