HTTP 缓存机制介绍
Ø 简介
本文简单讨论 HTTP 缓存机制的基本概念与使用方法,经本人测试得出结论,不对之处欢迎指正。
1. 什么是HTTP 缓存
1) HTTP 缓存其实是一种输出缓存,当第一次请求(可能是A用户)时,以设定的时间缓存响应的HTML 页面,第二次请求(可能是B用户) 时,如果在缓存期内,将使用缓存内容输出,而不会以新的请求处理。
2) 缓存内容会因设定的时间而失效,也有可能因为系统内存紧张,提前移除缓存内容。
2. 缓存的优点
1) 减少相同资源重复性的请求,以减少网络宽带的消耗;
2) 减少客户端与服务端的瓶颈,也减轻了服务器的压力;
3) 自然在速度上也有所提高,提高了用户的体验。
3. 理解下Request Headers 中缓存标头的意思:
1) If-Modified-Since: 该值由服务器响应,对此文件的最后修改时间,与 Last-Modified 值相同,会再次发送给服务器。
2) If-None-Match:该值由服务器响应,服务器对此资源生成的缓存唯一标识,与ETag 值相同,会再次发送给服务器。
4. 理解下Response Headers 中缓存标头的意思:
1) Cache-Control: 字面上就可以理解为缓存控制,包含以下值:
1. private: 默认值。只能缓存在客户端,而不能由共享(代理服务器)缓存进行缓存。
2. public: 响应由客户端和共享(代理)缓存进行缓存。
3. no-cache: 如果没有字段名,则指令应用于整个请求,且在满足请求前,共享(代理服务器)缓存必须对原始Web 服务器强制执行成功的重新验证。如果有字段名,则指令仅应用于命名字段;响应的其余部分可能由共享缓存提供。
4. max-age=xxx: 表示缓存的最长时间。
5. no-store: 浏览器和服务器都不使用缓存。
2) Date: 服务器响应时间。
3) Expires: 缓存绝对过期时间(HTTP 1.0)。
4) Vary: 告诉代理服务器缓存两种版本的资源:压缩和非压缩,这有助于避免一些公共代理不能正确地检测Content-Encoding标头的问题。可以在 system.webServer 节点中添加,例如:
<system.webServer>
<httpProtocol>
<customHeaders>
<remove name="Vary"></remove>
<add name="Vary" value="Accept-Encoding"></add>
</customHeaders>
</httpProtocol>
</system.webServer>
5) Last-Modified: 表示服务器对此文件的最后修改时间。当浏览器第一次请求某一个URL时,服务端的返回状态是200;第二次请求此URL时,根据HTTP协议的规定,浏览器会向服务器传送If-Modified-Since报头,询问该时间之后文件是否有被修改过;如果服务端的资源没有变化,则自动返回HTTP 304(Not Changed.)状态码,内容为空,这样就节省了传输数据量。
6) ETag: 表示服务器对此资源生成的缓存唯一标识,与 Last-Modified 功能类似。
5. 先看一下非缓存下的请求/响应缓存报文,如图:
6. 使用Response.Cache.SetMaxAge() 方法,实现让浏览器在指定的时间内使用浏览器缓存(该方式对于强制刷新无效)
1) 添加如下代码:
protected void Page_Load(object sender, EventArgs e)
{
Response.Cache.SetCacheability(HttpCacheability.Private); //可以忽略,因为Private 是默认值
Response.Cache.SetMaxAge(new TimeSpan(0, 0, 0, 10)); //设置缓存的最长时间
}
2) 发送请求:http://localhost:50441/Cache/One,并且在缓存期内连续多次刷新页面,将看到如下结果:
3) 可以发现在非强制刷新的情况下,若还在缓存期内(10秒),并未发出新的请求,并且服务器的时间未改变,说明还使用的是缓存。但是,第四次强制刷新,则缓存无效了(即使还在缓存期内)。
7. 使用 OutputCache 命令设置缓存,对应Last-Modified 与If-Modified-Since(该方式对于强制刷新有效)
n Duration:设置缓存持续时间(以秒为单位)。
n VaryByParam:表示根据什么参数来更新缓存,可选值:
1. none:不根据参数更新缓存。
2. paramNames:表示根据参数名称更新缓存,多个参数以分号(;)分割,例如:"id;name"。
3. *:表示任何参数改变时,都更新缓存。
1) 在页面头部添加以下代码即可:
<%@ OutputCache Duration="10" VaryByParam="none" %>
2) 发送请求:http://localhost:50441/Cache/One,连续刷新页面两次,将看到如下结果:
3) 可以看出“缓存绝对过期时间”比服务器响应时间长了10秒,表示在这10秒以内再次请求(无论是强制刷新 还是 超链接刷新等)这个地址,都会使用缓存,即返回304 状态码。
4) 正因如此,在服务端就可以获取到“服务器对此文件的最后修改时间”,即 If-Modified-Since 的值,从而判断:该值+ 缓存时间> 当前时间,如果大于表示缓存已过期,则产生一个新的请求,否则输出缓存内容。(这只是我的个人理解)
5) 我们可以使用 Request.Headers["If-Modified-Since"] 来获取客户端传来的“服务器对此文件的最后修改时间”的值,获取上次对该请求设置的值。
8. 使用Response.Cache.SetCacheability() 方法也能实现同上的缓存功能,对应Last-Modified 与 If-Modified-Since(该方式对于强制刷新有效)(效果与“使用 OutputCache 命令”一样,就不截图了),看代码:
protected void Page_Load(object sender, EventArgs e)
{
Response.Cache.SetCacheability(HttpCacheability.Public); //这里不设置默认为 Private
Response.Cache.SetExpires(DateTime.Now.AddSeconds(10));
//设置服务器自动返回304状态码(该设置对于强制刷新 或 地址栏回车将有效)
Response.Cache.SetLastModified(DateTime.Now); //该时间表示服务器对此文件的最后修改时间
Response.Cache.SetLastModifiedFromFileDependencies(); //基于处理程序文件依赖项的时间戳设置Last-Modified HTTP 标头。
}
9. 使用Response.Cache.SetETagFromFileDependencies() 方法也能实现同上的缓存功能,与使用 Response.Cache.SetCacheability() 方法的功能类似,对应ETag 与If-None-Match(该方式对于强制刷新有效),且 ETag 优先级高于 Last-Modified(没测试)。
1) 添加如下代码:
protected void Page_Load(object sender, EventArgs e)
{
Response.Cache.SetCacheability(HttpCacheability.Public); //这里不设置默认为 Private
Response.Cache.SetExpires(DateTime.Now.AddSeconds(10));
//(该方式对于强制刷新有效)
Response.Cache.SetETagFromFileDependencies(); //基于处理程序文件依赖项的时间戳设置ETag HTTP 标头。
}
2) 发送请求:http://localhost:50441/Cache/One,并且在缓存期内连续两次刷新页面,将看到如下结果:
3) 同样,我们可以使用 Request.Headers["If-None-Match"] 来获取客户端传来的“服务器对此资源生成的缓存唯一标识”的值,获取上次对该请求设置的值。
10. 使用 System.Web.UI.HtmlControls.HtmlMeta 对象实现(该方式对于强制刷新无效),看代码(注意:这种方式仅支持 IE,其他4大浏览器都不支持):
protected void Page_Load(object sender, EventArgs e)
{
HtmlMeta htmlMeta = new HtmlMeta();
htmlMeta.HttpEquiv = "Expires";
htmlMeta.Content = DateTime.Now.AddSeconds(10).ToUniversalTime().ToString("R"); //这里需要转为GMT 时间
this.Header.Controls.Add(htmlMeta);
}