[笔记]经验分享:流量大的网站可以采用静态gzip压缩某些页面及文件

来源:互联网 发布:筑龙投标软件 编辑:程序博客网 时间:2024/05/01 07:55
本文主要介绍如何通过对页面进行压缩从而节省网站的带宽以及提升用户的访问速度。
网站的访问速度是由多个因素所共同决定的,这些因素例如应用程序的响应速度、网络带宽、服务器性能、与客户端之间的网络传输速度等等。其中最重要的一个因素是应用程序本身的响应速度,因此当你为网站性能所苦恼时,你第一个需要着手进行处理的便是尽可能的提升应用程序的执行速度,你可以使用缓存或者是优化代码的执行效率来提升应用程序的速度。

但是,本文并不是介绍如何来提升应用程序的执行效率,前面提到的只不过是为了防止您病急乱投医。在确保您的应用程序的性能已经达到足够好,同时服务器的性能也完全满足的情况下,不妨来试试网页压缩来进一步提升网页的浏览速度,而且非常重要的是,它完全不需要任何的成本,只不过是会让您的服务器CPU占用率稍微提升一两个百分点而已或者更少。

网页压缩是一项由 WEB 服务器和浏览器之间共同遵守的协议,也就是说 WEB 服务器和浏览器都必须支持该技术,所幸的是现在流行的浏览器都是支持的,包括 IE、FireFox、Opera 等;服务器有 Apache 和 IIS 等。双方的协商过程如下:

首先浏览器请求某个 URL 地址,并在请求的头 (head) 中设置属性 accept-encoding 值为 gzip,deflate,表明浏览器支持 gzip 和 deflate 这两种压缩方式(事实上 deflate 也是使用 gzip压缩协议,下面我们会介绍二者之间的区别);
WEB 服务器接收到请求后判断浏览器是否支持压缩,如果支持就传送压缩后的响应内容,否则传送不经过压缩的内容;
浏览器获取响应内容后,判断内容是否被压缩,如果是则解压缩,然后显示响应页面的内容。
在实际的应用中我们发现压缩的比率往往在 3 到 10 倍,也就是本来 50k 大小的页面,采用压缩后实际传输的内容大小只有 5 至 15k大小,这可以大大节省服务器的网络带宽,同时如果应用程序的响应足够快时,网站的速度瓶颈就转到了网络的传输速度上,因此内容压缩后就可以大大的提升页面的浏览速度。

接下来我们介绍几种常用的环境下如何启用网页压缩功能。

纯 Tomcat 服务器

如果您的 WEB 应用程序是跑在 Tomcat 服务器下的,而且直接使用 Tomcat 所提供的 HTTP服务,那建议你马上动手,因为实在是太简单了,你只需要在 server.xml 配置文件中给 HTTP Connector 增加一个compression 的参数值为 on 并重启 Tomcat 服务器就立刻生效,配置如下:

    <Connector port="8080" protocol="HTTP/1.1"
            maxThreads="150" connectionTimeout="20000"
            redirectPort="8443" compression="on"/>
            

Tomcat 采用的是 HTTP/1.1 的 GZIP 压缩协议,它会根据浏览器送过来的请求中的 accept-encoding 值是否包含gzip 来判断浏览器是否支持 gzip 压缩协议,如果浏览器支持就启用 gzip 压缩,否则就不进行任何压缩处理。Tomcat中还有另外一个参数compressableMimeType,这个参数可以用来指定压缩哪种类型的内容,例如可以指定该配置值为:text/html,text/plain ,则只压缩 contentType 为 text/html 和 text/plain 的页面,不过您最好也将 css 和javascript 文件也算在压缩的文件类型中,因为这两者的压缩效果也十分的明显。

Apache 服务器

在 apache 1.3 版本,大家常用 mod_gzip 对输出内容进行压缩,现在主流的浏览器都支持 gzip 解压缩。在 apache2下,这个模块换名为 mod_deflate,对应的模块文件名是 mod_deflate.so。mod_gzip 本文不做介绍,下面描述一下在Apache 2 下如何启用并配置 mod_deflate 模块。默认安装的 Apache 不管是 Windows 还是Linux/Unix,都是不启用该模块的, Linux/Unix 下甚至不带该模块,你需要手工编译这个模块。

下面我们分别介绍在 Windows 和 Linux 操作系统下如何启用并配置 mod_deflate 模块。

在 Windows 下采用安装程序安装的 Apache 服务器已经带有 deflate 所需要的模块 mod_deflate.so 和 mod_headers.so,我们只需要在 httpd.conf 配置文件中启用并进行相关的配置即可,配置如下:

LoadModule deflate_module modules/mod_deflate.so
            LoadModule headers_module modules/mod_headers.so
            <Location />
            # Insert filter
            SetOutputFilter DEFLATE
            # Netscape 4.x has some problems...
            BrowserMatch ^Mozilla/4 gzip-only-text/html
            # Netscape 4.06-4.08 have some more problems
            BrowserMatch ^Mozilla/4/.0[678] no-gzip
            # MSIE masquerades as Netscape, but it is fine
            # BrowserMatch /bMSIE !no-gzip !gzip-only-text/html
            # NOTE: Due to a bug in mod_setenvif up to Apache 2.0.48
            # the above regex won't work. You can use the following
            # workaround to get the desired effect:
            BrowserMatch /bMSI[E] !no-gzip !gzip-only-text/html
            # Don't compress images
            SetEnvIfNoCase Request_URI .(?:gif|jpe?g|png)$ no-gzip dont-vary
            # Make sure proxies don't deliver the wrong content
            Header append Vary User-Agent env=!dont-vary
            </Location>
            


而如果是 Linux/Unix 操作系统,如果你没有在编译安装的过程中将所需要的两个模块 mod_deflate 和 mod_headers编译进去的话,那就稍微有点麻烦,首先我们先看如何在编译安装 Apache 过程中也同时编译这两个模块,请在执行 configure程序时增加两个参数分别是:

# ./configure --enable-deflate --enable-headers
            


这样在编译完 Apache 后就可以直接在 httpd.conf 中启用并配置 deflate 模块了,配置的方法跟 Windows 平台下是相同的。

如果说您的 Apache 已经在运行了,不想再重新编译一次,那也可以选择只编译 deflate 模块所需的文件 mod_deflate.c 和mod_headers.c。这两个文件位于 {apache-src}/modules/filters/ 目录下(其中 {apache-src}为 apache 源文件所在的目录)。使用如下命令来单独编译这两个源文件。

# {apache-bin}/apxs -i -a -c {apache-src}/modules/filters/mod_deflate.c
            # {apache-bin}/apxs –i –a –c {apache-src}/modules/filters/mod_headers.c
            


其中 {apache-bin} 为 Apache 安装目录下的 bin 目录,接下来在 httpd.conf 直接配置该模块即可。

很多时候你在单独编译 deflate 模块的时候可能会碰到编译错误,提示是:

Cannot load /opt/apache/modules/mod_deflate.so into server: /opt/apache/modules/mod_deflate.so: undefined symbol: deflate

解决的方法如下:

编辑 /usr/local/apache2/bin/apr-config 文件修改其中的 LDFLAGS 值为 "-lz",然后再重新编译 mod_deflate 模块,apxs -ica mod_deflate.c 即可。

为了省却不必要的麻烦,请尽量在编译安装时直接加上 --enable-deflate --enable-headers 参数。

IIS 服务器

微软的 IIS 服务器同样也是目前用得最多的 WEB 服务器之一,而且用来运行 ASP 页面也是必不可少的。IIS6 本身支持 gzip 压缩,IIS5 就比较费劲了,你可以找一些第三方的组件来处理,例如 httpzip,网址是:http://www.port80software.com/products/httpzip/?vid=3354166,不过这玩意是收费的。接下来我们介绍如何在 IIS6 中启用压缩功能。

打开 Internet 信息服务(IIS)管理器,右击"网站"->"属性",选择"服务"。在 "HTTP压缩" 框中选中 "压缩应用程序文件" 和 "压缩静态文件",按需要设置 "临时目录" 和 "临时目录的最大限制",如下图所示:


图1 设置网站属性


接下来配置 gzip 组件,在 Internet 信息服务(IIS)管理器,点击 "Web 服务扩展"->"增加一个新的 Web服务扩展...",在 "新建 Web 服务扩展" 框中输入扩展名 "HTTP Compression",添加 "要求的文件" 为C:/WINDOWS/system32/inetsrv/gzip.dll,选中 "设置扩展状态为允许",如下图所示:


图2 设置 Web 服务扩展


图3 新建 Web 服务扩展


还没完呢,我们还需要修改一个配置文件,修改之前请先停止 IIS 服务,打开 C:/Windows/System32/inetsrv/MetaBase.xml,这个文件很大,找到下面一段信息:

<IIsCompressionScheme       Location ="/LM/W3SVC/Filters/Compression/gzip"
            HcCompressionDll="%windir%/system32/inetsrv/gzip.dll"
            HcCreateFlags="1"
            HcDoDynamicCompression="TRUE"
            HcDoOnDemandCompression="TRUE"
            HcDoStaticCompression="TRUE"
            HcDynamicCompressionLevel="0"
            HcFileExtensions="htm
            html
            txt"
            HcOnDemandCompLevel="10"
            HcPriority="1"
            HcScriptFileExtensions="ASP
            dll
            exe"
            >
            </IIsCompressionScheme>
            


增加一些要进行压缩的文件后缀,其中 HcFileExtensions 是静态文件的扩展名,增加 js 和 css 等;HcScriptFileExtensions 为动态文件的扩展名,增加 ASPx,保存后启动 IIS 即可生效。

最后我们介绍如何来测试前面所做的工作是否起效,你可能会觉得很奇怪,配置好了,用浏览器打开页面正常,查看页面源码,内容并没有变化,大小也跟原来一样,怎么回事呢?这是因为浏览器已经把内容解压了的结果,有两个方法来判断压缩是否生效:第一,查看 WEB 服务器的日志,不管是 Apache或者是 IIS,二者的访问日志格式都差不多是下面这种格式:

127.0.0.1 - - [14/May/2006:08:44:28 +0800] "GET /manual/style/css/manual.css HTTP/1.1" 200 19351

最后两个数字分别是 HTTP 的结果码(200 表示 OK),19351表示的是响应内容的大小,把这个大小跟你在浏览器上查看源码的大小比较一下就可以知道是否生效。另外一种方法就是自己写一个 HTTP客户端的小程序并设置 Accept-Encoding 的值为 gzip,deflate,由这个程序去请求服务器端的某个 URL地址,然后打印出响应的内容,如果是一堆乱码,恭喜你,配置成功。下面是一段 Java 写的测试客户端代码(需要用到commons-httpclient 包):

HttpClient http = new HttpClient();
            String url = "http://www.dlog.cn/javayou";
            GetMethod get = new GetMethod(url);
            try{
            System.out.println("fetching url : "+ url);
            get.addRequestHeader("accept-encoding", "gzip,deflate");
            int er = http.executeMethod(get);
            if(er==200){
            System.out.println(get.getResponseContentLength());
            String html = get.getResponseBodyAsString();
            System.out.println(html);
            System.out.println(html.getBytes().length);
            }
            }finally{
            get.releaseConnection();
            }
            

结论

以上是目前比较流行的两个 WEB 服务器软件以及 Tomcat 服务器对于页面压缩的配置方法;其他的一些 J2EE应用服务器如果不支持这个功能的话可以考虑利用过滤器(Servlet Filter)来进行处理,具体的代码以及配置方法可以参考 Resin服务器所提供的文档。但是有一点需要提醒各位读者的是,本文介绍的访问只是在服务器本身的响应速度已经足够优化的情况下进行,也就是说在带宽成为系统瓶颈的时候才来考虑该方案。
--------------------------------------------------------------------------------------------------------------

再补充一个例子和一些相关知识

GET /lookfor.htm HTTP/1.1
Accept: */*
User-Agent: Mozilla/4.0 (compatible; MSIE 7.0; Windows NT 5.1; InfoPath.1; .NET CLR 2.0.50727; .NET CLR 3.0.04506.30)
Host: www.veryhman.com
Connection: Keep-Alive

HTTP/1.0 200 OK
Date: Wed, 09 Jul 2008 13:03:01 GMT
Server: Apache
Last-Modified: Wed, 09 Jul 2008 11:51:24 GMT
Content-Length: 98279
Content-Location: search.gz
Content-Encoding: gzip
Content-Type: text/html
Age: 64
X-Cache: HIT from www.veryhman.com
Connection: keep-alive

很多网站都采用动态gzip压缩,流量缩小了,但系统开销大了,网站还是慢。这里介绍的解决方案采用事先压缩好的静态gz文件,加入http头部信息做转向。

本来点到为止已经可以了,但是有人似乎不太满意,所以再补充一点内容:给一个应用实例。

这个方案的重点是静态的Gzip压缩,实际还要结合浏览器缓存机制来用才好。把经常用到的要加载的东西集中压缩在一起,第一次加载时放进浏览器缓存里,以后就可以经常使用本地缓存的内容。

一个漫画搜索引擎:http://www.veryhman.com/lookfor.htm

其中有某个脚本个头比较大,于是按上面的思路,把它gzip压缩。为了能充分利用浏览器缓存,给这个文件加上Head头标Expires,设为一个未来的时间(注意:为 'Expires' 指定的日期不得早于 1980 年 1 月 1 日,或晚于 2038 年 1 月19 日,3:14:07GMT,否则某些系统就会出错。),再按照上面的红字设置另外几个头标就可以了。
Content-Length: {gz文件的大小(字节数)}
Content-Location: {gz文件名}
Content-Encoding: gzip
Content-Type: application/x-javascript(因为是个脚本嘛,其实用text/html大多数浏览器也是支持的)

除非用户按F5强制刷新,或者清空了浏览器缓存,否则在缓存Expires到期以前浏览器每次访问这个文件时都会优先使用缓存中的内容(至于怎么做才能不耽误更新,也就是需要更新这个文件时不被缓存阻扰,则是另一个早已人们被解决得很成熟的问题了;在这个例子里,我采用最简单、直接的在文件名中包含版本号的办法。)。

此外,按照HTTP/1.1协议浏览器从服务器访问一个缓存中没有的文件时,服务器会返回一个Last-Modified头标指明文件的最后更新时间,同时返回一个Etag(Entity Tags,实体标识)头标:

  基础知识
        1) 什么是”Last-Modified”?

        在浏览器第一次请求某一个URL时,服务器端的返回状态会是200,内容是你请求的资源,同时有一个Last-Modified的属性标记此文件在服务期端最后被修改的时间,格式类似这样:

        Last-Modified: Fri, 12 May 2006 18:53:33 GMT

        客户端第二次请求此URL时,根据 HTTP 协议的规定,浏览器会向服务器传送 If-Modified-Since或Unless-Modified-Since报头,询问该时间之后文件是否有被修改过:

        If-Modified-Since: Fri, 12 May 2006 18:53:33 GMT
Unless-Modified-Since: Thu, 27 Dec 2007 02:35:45 GMT

        如果服务器端的资源没有变化,则自动返回 HTTP 304 (NotChanged.)状态码,内容为空,这样就节省了传输数据量。当服务器端代码发生改变或者重启服务器时,则重新发出资源,返回和第一次请求时类似。从而保证不向客户端重复发出资源,也保证当服务器有变化时,客户端能够得到最新的资源。

        2) 什么是”Etag”?

        HTTP 协议规格说明定义ETag为“被请求变量的实体值”。另一种说法是,ETag是一个可以与Web资源关联的记号(token)。典型的Web资源可以一个Web页,但也可能是JSON或XML文档。服务器单独负责判断记号是什么及其含义,并在HTTP响应头中将其传送到客户端,以下是服务器端返回的格式:

        ETag: "50b1c1d4f775c61:df3"

        客户端的查询更新格式是这样的:

        If-None-Match: W/"50b1c1d4f775c61:df3"

        如果ETag没改变,则返回状态304然后不返回,这也和Last-Modified一样。Etag主要在断点下载时比较有用。
        
      Last-Modified和Etags如何帮助提高性能?
        聪明的开发者会把Last-Modified和ETags请求的http报头一起使用,这样可利用客户端(例如浏览器)的缓存。因为服务器首先产生Last-Modified/Etag标记,服务器可在稍后使用它来判断页面是否已经被修改。本质上,客户端通过将该记号传回服务器要求服务器验证其(客户端)缓存。
        过程如下:
                1. 客户端请求一个页面(A)。
                2. 服务器返回页面A,并在给A加上一个Last-Modified/ETag。
                3. 客户端展现该页面,并将页面连同Last-Modified/ETag一起缓存。
                4. 客户再次请求页面A,并将上次请求时服务器返回的Last-Modified/ETag一起传递给服务器。
                5. 服务器检查该Last-Modified或ETag,并判断出该页面自上次客户端请求之后还未被修改,直接返回响应304和一个空的响应体。

关于ETag的一些补充知识
http://bbs.chinaunix.net/archiver/tid-1186771.html

在我们的这个例子里,可以完全不用Etag(这样可以进一步节省服务器端的开销,因为计算Etag多少也是要消耗一点资源的),只用Last-Modified。其实在这个例子里,由于采用前台技巧控制文件的版本更新,我们甚至可以不必老老实实地检查If-Modified-Since的时间,只要请求头里有If-Modified-Since,就说明浏览器缓存里有这个文件,直接返回304 Not Modified消息就可以了。
-------------------------------------------------------------------------------------------------------------
 
代码:
GET /lookfor.htm HTTP/1.1
Accept: */*
Accept-Language: zh-cn
Accept-Encoding: gzip, deflate
User-Agent: Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1; SV1;.NET CLR 2.0.50727; .NET CLR 3.0.04506.648; .NET CLR 3.5.21022)
Host: www.veryhman.com
Connection: Keep-Alive
HTTP/1.1 200 OK
Date: Tue, 15 Jul 2008 01:03:29 GMT
Content-Length: 3348
Content-Type: text/html
Content-Location: http://www.veryhman.com/lookfor.htm
Last-Modified: Mon, 14 Jul 2008 10:45:40 GMT
Accept-Ranges: bytes
ETag: "ae2e8c29ee5c81:2daef"
Server: Microsoft-IIS/6.0
X-Powered-By: ASP.NET
难道换服务器了???
怎么我获取的时候.成IIS/6.0了?而不是Apache?而且也没有声明使用GZIP压缩...

只有在获取那个js的时候.
http://www.veryhman.com/search20080713.asp
倒是有声明使用的是GZIP压缩.而且也没有使用你所说的
复制内容到剪贴板
代码:
Content-Location: search.gz
并没有让它重定向..而是直接声明了,当前页面使用了GZIP.
复制内容到剪贴板
代码:
GET /search20080713.asp HTTP/1.1
Accept: */*
Referer: http://www.veryhman.com/lookfor.htm
Accept-Language: zh-cn
Accept-Encoding: gzip, deflate
User-Agent: Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1; SV1;.NET CLR 2.0.50727; .NET CLR 3.0.04506.648; .NET CLR 3.5.21022)
Host: www.veryhman.com
Connection: Keep-Alive
HTTP/1.1 200 OK
Cache-Control: private
Connection: close
Date: Tue, 15 Jul 2008 01:03:29 GMT
Content-Type: application/x-javascript
Content-Encoding: gzip
Expires: Thu, 31 Dec 2037 15:59:58 GMT
Last-Modified: Sun, 27 Apr 2008 16:50:52 GMT
Server: Microsoft-IIS/6.0
X-Powered-By: ASP.NET
Set-Cookie: ASPSESSIONIDQCQSDRRR=OPOFNGNBGFFNKGDLEFHNHGBE; path=/