http, etag, last-modified
最近在一个新项目的上线过层中,发现由于etag的错误使用导致网页内容缓存的效果和理想的设计之间存在很大的差距,趁此机会对etag进行了一些了解。下文就是通过一个实际例子来学习etag的一些使用规则。
1 首先在FF浏览器输入以下访问地址
比如我们访问某网站:http://xxxxxxxx/
第一次的http请求代码如下(只包含头部信息)
———————————————————-
-
GET / HTTP/1.1
-
Host: xxxxxxxx
-
User-Agent: Mozilla/5.0 (Windows; U; Windows NT 5.0; ja; rv:1.8.1.3)Gecko/20070309 Firefox/2.0.0.3
-
Accept: text/xml,application/xml,application/xhtml+xml,text/html;q=0.9,text/plain;q=0.8,image/png,*/*;q=0.5
- Accept-Language: ja,en-us;q=0.7,en;q=0.3
-
Accept-Encoding: gzip,deflate
- Accept-Charset: Shift_JIS,utf-8;q=0.7,*;q=0.7
-
Keep-Alive: 300
- Connection: keep-alive
———————————————————-
服务器的返回http头部内容:
———————————————————-
- HTTP/1.x 200 OK
-
Date: Mon, 07 May 2011 11:49:33 GMT
-
Server: Apache/2.2.4 (Unix)
-
Last-Modified: Sat, 20 Nov 2009 20:16:24 GMT
-
Etag: “4c602bd-2c-4c23b600″
-
Accept-Ranges: bytes
-
Content-Length: 44
-
Connection: close
- Content-Type: text/html
再次refresh,重新访问http://xxxxxxxx/
———————————————————-
第二次的请求http头内容如下
———————————————————-
-
GET / HTTP/1.1
-
Host: xxxxxxxx
-
User-Agent: Mozilla/5.0 (Windows; U; Windows NT 5.0; ja; rv:1.8.1.3)Gecko/20070309 Firefox/2.0.0.3
-
Accept: text/xml,application/xml,application/xhtml+xml,text/html;q=0.9,text/plain;q=0.8,image/png,*/*;q=0.5
- Accept-Language: ja,en-us;q=0.7,en;q=0.3
-
Accept-Encoding: gzip,deflate
- Accept-Charset: Shift_JIS,utf-8;q=0.7,*;q=0.7
-
Keep-Alive: 300
- Connection: keep-alive
-
If-Modified-Since: Sat, 20 Nov 2004 20:16:24 GMT
- If-None-Match: "4c602bd-2c-4c23b600"
-
Cache-Control: max-age=0
———————————————————-
此时的http响应头如下
———————————————————-
- HTTP/1.x 304 Not Modified
-
Date: Mon, 07 May 2011 11:51:27 GMT
-
Server: Apache/2.2.4 (Unix)
-
Connection: close
- Etag: “4c602bd-2c-4c23b600″
以上的交互过程中有几个头部信息特别需要关注
1)初次的http请求信息
-
Etag: “4c602bd-2c-4c23b600″
2)第二次请求的http信息
-
If-None-Match: “4c602bd-2c-4c23b600″
3)第二次访问的响应http信息
- HTTP/1.x 304 Not Modified
-
Etag: “4c602bd-2c-4c23b600″
这个etag的相关描述参考apache的文档如下
FileETag 配置当文档是基于一个文件时用以创建etag内容所使用的文件属性。
ETag被用在为了节省网络带宽进行缓存判断的机制上,apache默认采用文件的inode,size,最终修改时间(mtime来生成etag)。apache允许关闭etag功能,只要配置FileETag None 即可。
也就是说
对apache 请求静态内容(html网页、图片、css、javascript文件等)时候,apache会按照请求文件的inode,大小,以及最终的修改时间来生成etag的内容,并且包含在http的响应头部信息返回给客户端。
当浏览器访问网页的时候,会在http请求头中加入If-None-Match头部信息(前提是前次请求时服务器返回了etag头部信息),内容是以前服务器发送回来的etag信息。
- apache收到该请求后,比较接收到的etag和自己再次计算出的etag内容,如果两者相等,则返回「304 Not Modified」,如果不相等,则返回 200 OK和实际请求的内容
※注意这个时候If-None-Match相比于If-Modified-Since具有更高的优先级别。
在www服务器只有一台的情况,请求内容的唯一性可以由etag来确定,但是如果是多台www服务器在负载均衡设备下提供对外服务,尽管各www服务器上的组件内容完全一致,但是由于在不同的服务器上inode是不同的,因此对应生成他的etag也是不一样的,在这种情况下,尽管请求的是同一个未发生变化的组件,但是由于etag的不同,导致apache服务器不再返回「304 Not Modified」,而是返回了200 OK和实际的组件内容(尽管事实上内容不曾发生变化),大大浪费了带宽。
例如:一个在loadbalancer(负载平衡)下的web服务器群,同一浏览器来的多次访问被轮询分配到不同的服务器上,因此每次都得到不同的etag,从而导致客户端每次都要从服务器端接收原本已经在客户端缓存的内容,徒然浪费了服务器端宝贵的带宽资源
一些解决办法
Etag的生成因素限制为:文件大小、最终修改时间 (mtime)
- FileETag MTime Size
不让服务器返回ETag(从而浏览器再次发送http请求时候不再捎带If-None-Match)
CODE:
- FileETag None
如上修改后我们再次观察服务器的返回的http头部情况
- HTTP/1.x 200 OK
-
Date: Tue, 08 May 2011 06:39:22 GMT
-
Server: Apache/2.2.4 (Unix)
-
Last-Modified: Sat, 20 Nov 2009 20:16:24 GMT
- Accept-Ranges: bytes
-
Content-Length: 44
- Connection: close
-
Content-Type: text/html
因为服务器返回的响应头中没有Etag信息了,所以后续的请求中自然也不再会有If-None-Match
FileETag的配置内容一般可以总结为以下3种情况。
- /usr/local/apache2/htdocs/images配所有内容都不生成ETag
- 只配置对于图片内容不生成ETag
- 所有的网站内容都不生成ETag
基本的考虑思路是:
对于动态内容,FileETag的设定并不起作用,不管如何配置,都不会返回ETag信息。
对于静态内容,则可以根据前端loadbalancer的算法,设计不同的etag配置。
1)对于采用轮询算法等每次访问服务器有可能会被分配到不同的real web服务器的,配置如下
FileETag None
或者
FileETag MTime Size
2)如果前端loadbalancer采用基于源IP地址等hash算法来进行负载均分(保证同一个IP来的访问都被分配到同一个real web服务器上)
因为原则上能保证同一个用户来的所有的http 访问都分配到同一个物理web服务器上,所以apache的FileETag可以保持默认配置不做修改。