压缩组件
如果 HTTP 请求产生的响应包很小,传输时间就会减少,因为只需要将很小的包从服务器传递到客户端。
从 HTTP1.1 开始,Web 客户端可以通过 HTTP 请求中的 Accept-Encoding 头来标识对压缩的支持。
Accept-Encoding: gzip, deflate
如果 Web 服务器看到请求中有这个头,就会使用客户端列出的方法中的一种来压缩响应。Web 服务器通过响应中的 Content-Encoding 头来通知 Web 客户端。
Content-Encoding: gzip
gzip 是目前最流行和最有效的压缩方法。支持 deflate 的浏览器也支持 gzip,但很多浏览器支持 gzip 却不支持 deflate。
压缩什么
压缩的内容包括 XML 和 JSON 在内的任何文本响应,但这里只关注脚本和样式表,因为他们用得最普遍。图片和 PDF 不应该被压缩,因为它们本来就已经被压缩了,会浪费 CPU 资源,还可能增加文件的大小。
服务器会花费额外的 CPU 周期来完成压缩,客户端要对压缩文件进行解压缩。要检测收益是否大于开销,需要考虑响应的大小、连接的带宽和客户端与服务器之间的 Internet 距离。这些信息难以得到且也有其他变数需要考虑。根据经验通常对 1KB 或者 2KB 以上的文件进行压缩。mod_gzip_minimum_file_size 指令控制着希望压缩的文件的最小值。
节省
gzip 是典型的压缩选择。gzip 能将响应整体减少 66%,而 deflate 能减少 60%。
配置
Apache1.3 使用 mod_gzip, 以下是最常用的指令:
启用 mod_gzip
mod_gzip_on
基于文件类型、MIME 类型、用户代理等定义哪些需要压缩、哪些不需要。
mod_gzip_item_include
mod_gzip_item_exclude
例如明确压缩脚本和样式表:
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 指令可以完成这一任务。
Apache2.x 使用 mod_deflate 模块。对压缩脚本和样式表的基本配置:
AddOutputFilterByType DEFLATE text/html text/css application/x-javascript
和 mod_gzip 不同,mod_deflate 包含了一个用于控制压缩级别的指令——DeflateCompressionLevel。
代理缓存
当浏览器通过代理来发送请求时,情况就变得复杂了。假设某 url 发送到代理的第一个请求来自于一个不支持 gzip 的浏览器。这是到达代理的第一个请求,因此缓存为空。代理会将请求转发到 web 服务器。此时服务器的响应是未经过压缩的。这个没有压缩的响应被代理缓存起来并发送给浏览器。假设到达代理的第二个请求是来自同一个 url,来自于一个支持 gzip 的浏览器。代理会使用缓存中(未经压缩)的内容进行响应,这就失去了进行压缩的机会。如果顺序反了,情况可能更加严重。
解决的方法是在 web 服务器的响应中添加 Vary 头,告诉代理根据一个或多个请求头来改变缓存的响应。因此需要在服务器的 Vary 响应头中包含 Accept-Encoding.
Vary: Accept-Encoding
默认情况下,mod_gzip 会向所有响应添加 Vary: Accept-Encoding 头。
浏览器边缘情形
不是所有浏览器都完美支持压缩
在 Apache1.3 可以通过在 mod_gzip_item_include 中使用恰当的 User-Agent 值来指定浏览器白名单:
mod_gzip_item_include reqheader “User-Agent: MSIE [6-9]”
mod_gzip_item_include reqheader “User-Agent: Mozilla/[5-9]”
在 Apache2.x 使用 BrowserMatch 指令
BrowserMatch ^MSIE [6-9] gzip
BrowserMatch ^Mozilla/[5-9] gzip
将代理缓存加进来,把 User-Agent 作为代理的另外一种评判标准添加到 Vary 头中。
Vary: Accept-Encoding,User-Agent
mod_gzip 检测到你在使用浏览器白名单时,会自动将 User-Agent 字段添加到 Vary 头中,不幸的是,由于 User-Agent 有上千种值,代理不太可能为其所代理的所有 url 缓存 Accept-Encoding 和 User-Agent 的所有组合。甚至这样可能会导致完全禁用为响应包进行的缓存。另外一种方法是使用 Vary: 或 Cache-Control: private 头来禁用代理缓存。因为 Vary: 头防止了浏览器使用缓存的组件,不过最好使用 Cache-Control: private,Google 和 Yahoo 都使用了这种方式。
如何平衡压缩和代理支持的决定是很复杂的,需要在加快响应时间、减小带宽开销和边缘情形浏览器缺陷之间进行权衡。