引言:



Squid作为一个缓存工具,其核心的功能就是缓存。那么判断一个obj是否缓存以及缓存多久就是极为重要的工作。事实上目前我们SQUID开发的很多内容都是和这个问题有关。Squid在过期时间处理上有其独有的细致一面:不可避免的就会引入复杂的一面。理解squid就必须理解过期时间处理。这也是用好squid的一个前提。



<span id="more-20"></span>

1. squid过期相关的基础知识

1.1 概述

谈论squid就不得不谈论HTTP,谈论HTTP则必须提及RFC2616.



RFC2616文档(http1.1协议)定义了一系列的规则,我们需要就这些规则来指导我们的行为(也许跟据需求做相反的事情)。



HTTP缓存被定义为:除去许多情况下的对发送请求的需求,和除去在其他许多情况下发送完整响应的需求。前者需要控制不必要的网络奔波去下载不需要的东西,而后者是控制对不需要的东西仅请求不下载。这就牵扯到过期机制的两个组成方式:截止机制和验证机制。



截止机制:通俗说就是过期时间。HTTP缓存效率最大值的表现。如果一个资源(URI)仅仅从本地缓存目录中提取而产生响应无疑是最省力的。换句话说,缓存能够不必首先联系服务器而返回响应。



验证机制:和截止机制相关的。即在截止机制中设置了一个过期时间。当超过这个时间时,会收到一个带有验证请求的访问发往缓存而不是一个正常请求。如果缓存中的副本未有变化,则采用304Not Modified来说明此问题。否则必须回复完整响应(包含实体body)。



<strong></strong>

1.2 Date与 Expires

Date和Expires都是表示绝对时间的概念,其不同点在于:



<strong>Date</strong><strong>给出的是响应发出的绝对时间。</strong><strong></strong>



格式为:
“Date:” HTTP-date
<strong>Expires</strong><strong>给出响应的内容过期的绝对时间。</strong><strong></strong>



格式为:
“Expires:” HTTP-date
例如,今天是3月25日,apache源站设置了
ExpiresActive on ExpiresDefault “access plus 1 month”
那么请求该源站返回的header中就应该包括
Expires: Fri, 24 Apr 2009 08:00:57 GMT
<strong>注意:</strong>



a. Date与Expires都是绝对时间。



b. Date与Expires通常是格林尼治标准时间(如果后面带有GMT),也就是比北京时间早8个小时。

1.3 Age与max-age

这两个概念是比较容易搞混的。其相同点是:



a. 都是响应头的一部分



b. 都是一个时间长度,以秒为单位



但主要是不同点:



<strong>其中</strong><strong>age</strong><strong>是指:自从这个响应在源站服务器上生成以来(而不是源站内容修改以来!!),到现在所经历的秒数。</strong><strong></strong>



Age通常只对cache才有意义。



其格式为:
“Age:”age-value
<strong>max-age</strong><strong>是指:一个响应被源站发出后,在</strong><strong>cache</strong><strong>当中存活的时间,换句话说,一个响应的</strong><strong>Age</strong><strong>值如果超过了</strong><strong>max-age</strong><strong>,就认为这个响应是过期的。</strong><strong></strong>



<strong></strong>当然,max-age也是对于cache才有意义。



其格式为:
“Cache-control: max-age=” max-age-value
再次重申:max-age比Expires更加优先!

1.4 If-Modified-Since与Last-Modified

如果一个内容真的过期了,怎么办?从源站重新下载吗?



当然不。http协议对于过期的内容的处理,是先到源站验证一下,这个内容有没有发生改变,如果没有改变过,当然就不用重新下载了。这种验证是通过 If-Modified-Since等一系列http头实现的。



<strong>If-Modified-Since</strong><strong>是http</strong><strong>请求头的一部分,它表示客户端向源站询问“这个内容自从x</strong><strong>年x</strong><strong>月x</strong><strong>日x</strong><strong>时起,是否变过?”</strong>



其格式为:
“If-Modified-Since:” HTTP-date
<strong></strong>



如果源站发现该内容变过,则会返回一个<strong>200OK</strong>的响应,并把新的内容发回来给客户端。



如果源站发现内容没有变过,则会返回一个<strong>304</strong>的响应,告诉客户端“没有改变过,你的内容还是可以用的”,这种情况下就不用发送内容了,可以大大节省传输带宽。



注意:



a)如果客户端发的If-Modified-Since后面的日期是非法的日期,源站将直接返回<strong>200OK</strong>+新内容。



b) If-Modified-Since后面跟的日期是客户端上一次接到的响应中,<strong>Last-Modified</strong>的值。



<strong>Last-Modified</strong><strong>是响应头的一部分,是源站给出的,一个内容在源站上最终更新的时间。</strong>



对于静态内容,Last-Modified是文件的更新时间,对于动态内容,取决于源站的具体实现。



其格式为:
“Last-Modified:” HTTP-date

1.5 Etag与If-None-Match

有的时候,Last-Modified 与 If-Modified-Since不能够可靠地标示内容的改变,这就需要Etag等更加可靠的打标签的机制。



<strong>Etag</strong><strong>是响应头的一部分,是源站对内容所打的一个标签,用来标示这个文件,这个标签当且仅当内容变化才会变化,就像md5码一样。</strong>



与md5码不同的是,打Etag不需要读取整个文件的内容,比md5消耗的资源要少得多。



Etag起到的作用跟Last-Modified差不多,都是标示内容的变化,只是比Last-Modified更可靠。



<strong>If-None-Match</strong><strong>是请求头的一部分,它表示客户端向源站询问“这个内容的标签还是不是xxxxxx?</strong><strong>”</strong>



如果源站发现该内容变过,则会返回一个<strong>200OK</strong>的响应,并把新的内容发回来给客户端。



如果源站发现内容没有变过,则会返回一个<strong>304</strong>的响应,告诉客户端“没有改变过,你的内容还是可以用的”,这种情况下就不用发送内容了,可以大大节省传输带宽。

2. 目前过期时间部分我们的squid是如何实现(修改)的

2.1 squid碰到了什么困境

如上的squid描述我们可以看出squid在处理过期时间方面的支持是很强大也很全面的。但我们仍然发现一些因源站给出错误header导致我们缓存过期的问题(通常出现此问题即出现连续过期)。很多客户囿于技术/策略问题,将控制权交给我们。此时,我们就需要squid理顺过期关系进行自维护的处理。

2.2 mod_use_server_date做了什么

mod_use_server_date做的最重要的一件事情,就是确定一个obj的“起始”时间。知道一个obj从什么时候“出生”,然后知道其生存时间,即可完全决定其在squid的生存时间。第二件重要的事情就是“推定”经过其他squid设备的obj的“起始”时间。下面画图说明此问题:



如上图所示,在菱形时间点发生了请求,下层去获取OBJ进入squid群组和过期时间。



从上图我们可以发现其中核心功能实现是max-age(或者由expire和date计算得到)。而多层squid之间的交流主要靠AGE。也就是说:
Max-age(或者由expires-date计算得到)决定OBJ生存时长 Age决定OBJ的出生时间点

2.3 expires和refresh_pattern哪个在生效

如上(2.2节)是use_server_date的设计大意。那么在复杂的网络条件下,如何知道“<strong>是</strong><strong>expires</strong><strong>功能生效呢,还是</strong><strong>refresh_pattern</strong><strong>策略生效呢?</strong>”



要回答这个问题,首先要确认的是:
配置的refresh_pattern, URL对应行是否配置override-expire: 1)如果配置了,refresh_pattern生效,忽略过期时间 2)如果没有,先检查过期时间,再检查refresh_pattern
目前我们的默认配置都是



ignore-reload 和override-lastmod(稍后讲解refresh_pattern时解释)所以可以不用注意如上问题。



如果确认如上refresh_pattern没有配置override-expire,则遵循如下规则:
如果源站给的信息可以计算出OBJ生存时间,则OBJ不走refresh_pattern 源站给的header可能: 1)有max_age 2)有expires和date 3)仅有expires 上述3者关系是顺序优先匹配,如果1符合就不需要2、3,同理2符合就不需要3. 满足此3项的为可计算生存时间的OBJ.
有如上3个条件符合所得出的过期时间也不尽相同。



squid的处理策略,



如果源站给的Header符合:



1)有max_age



2)有expires和date



则结果相同,过期时间是squid最初服务时间+max-age或者是squid最初服务时间+源站expire-源站date。



如果源站给的Header符合:



3)仅有expires



则以此header数据为准,绝对时间设置过期。

3. 目前的refresh_pattern使用情况和存在问题

3.1 refresh_pattern概述

refresh_pattern是squid.conf中关于过期的一种配置项。



通常如下格式:
refresh_pattern -i ^http 1440 0% 1440 ignore-reload override-lastmod
我们可以分解一行配置行为:



l  功能节名:refresh_pattern



l  URL正则匹配区:-i  ^http (-I含义为是否区分大小写)



l  Age&lm-factore区:1440   0%  1440



l  Options区:ignore-reload override-lastmod等



前二区目前没有疑问,下面就后二区详细介绍。

3.2 Age&lm-factore区

本区有3个配置节 分别为 min percentage max。



Min:是指一个object在<strong>缺少明确的</strong><strong>Expires</strong>的情况下,能够<strong>确定</strong>的在cache中被认为<strong>新鲜</strong>的<strong>分钟</strong>数(<strong>小于此值肯定是新鲜的</strong>)。这个参数推荐设为0,否则会造成动态应用中的内容被不正确地缓存住。



Max:是指一个object在<strong>缺少明确的</strong><strong>Expires</strong>的情况下,在 cache中<strong>可能</strong>被认为新鲜的分钟数的<strong>最大值(大于此值肯定是过期的)</strong>。



percentage是指,一个object在<strong>缺少明确的</strong><strong>Expires</strong>的情况下,它的LM-factor的最大值(LM-factor超过此值则认为过期,否则新鲜)
LM-factor= (当前时间-到达cache的时间) / (到达cache的时间 – Last-Modified) * 100%
关于LM-factor,在《Squid: The Definitive Guide》一书中,有一个更加形象的图来说明这个概念。



LM-factor是HTTP协议中关于Last-Modifed时间参与过期计算的一个模糊处理过期时间的方式(如果不指明某URL过期时间)。<strong>显然在我们目前的应用下,是不该使用此项模糊策略的</strong>。我们目前线上机器配置percentage为0%,已经省略此功能节。

3.2 Options区

我们squid使用squid2.7,其options区目前可以有:
  1. override-expire: 强制min在expires前生效。
  2. override-lastmod: 强制min在lm-factor之前生效(在expire之后)。
  3. reload-into-ims:将no-cache或者reload请求转化为IMS请求给源站。
  4. ignore-reload:将no-cache或者reload请求头忽略。
  5. ignore-no-cache:强制忽略从源站而来的“Pragma: no-cache”和“Cache-control: no-cache”
  6. ignore-private:强制忽略从源站而来的“Cache-control: private”
  7. ignore-auth:强制将一个请求认为是源站发送的带有“Cache-control: public”
  8. stale-while-revalidate=NN:强制在过期时间生效后的NN秒内非过期
  9. ignore-stale-while-revalidate:强制忽略源站带有的“Cache-Control: stale-while-revalidate=NN”header
  10. max-stale=NN:强制将一个超过NN秒的OBJ设置为未缓存,即使其在cache,或者更新失败。
  11. negative-ttl=NN:强制此行refresh_pattern的negative_ttl时间,即使全局已经有过期时间设置。

3. SQUID 304的修改

Squid如果缓存了一个OBJ,且其未过期时,如果碰到客户端来请求更新此obj,则squid向下发送的304/Not Modified中的header不能进行更新(特别是Date和Expires),还是给源站给的。此时会导致客户的IE中的date和expires 显示为很早之前的(上层一直缓存的)。 在这种情况下源站不发送过期时间靠我们refresh_pattern进行判断的话,则不能刷新客户端的缓存记录时间。



此问题虽然可以使用update_headers来规避,但是显然其开销是非常大的。SQUID 304修改可以在无损状态下修复此问题。对于关闭update_headers导致304较多或者源站未给明确过期时间的请求,修改后都可以看到很明显



<br />



以上内容摘自于互联网。