缓存控制
关于缓存控制我们可以分为两部分讨论
1.服务端如何进行缓存控制
2.客户端如何进行缓存控制
服务端控制
服务端进行缓存控制主要依赖Cache-Control、及Expires请求头,其中Expires已经不推荐使用。其优先级如下所示:
- 附加一个Cache-Control: no-store首部到响应中去;
- 附加一个 Cache-Control: no-cache 首部到响应中去;
- 附加一个 Cache-Control: must-revalidate 首部到响应中去;
- 附加一个 Cache-Control: max-age 首部到响应中去;
- 附加一个 Expires 日期首部到响应中去;
Cache-Control: no-store,客户端禁止使用缓存
Cache-Control: no-cache,客户端可以进行缓存,但每次使用缓存时必须跟服务器进行再验证
Cache-Control: must-revalidate ,客户端可以进行缓存,在缓存过期后必须进行再验证,跟no-cache的区别在于must-revalidate强调的是缓存过期后的行为,因为在某些情况下为了提升效率客户端会使用已经过期的缓存,如果服务端指定了Cache-Control: must-revalidate ,那么缓存过期后不能直接使用,必须进行再验证。no-cache不论缓存是否过期都需要客户端发起再验证。
Cache-Control: max-age ,指明了缓存的有效期,是一个相对时间,单位为秒
Expires ,指明了缓存的有效,是一个决定时间。HTTP 设计者后来认为,由于很多服务器的时钟都不同步,或者不正确,所以最好还是用剩余秒 数,而不是绝对时间来表示过期时间,已经不推荐使用。
客户端控制
上面我们介绍了,服务器端如何在响应头中添加响应的字段来浏览来是否可以使用缓存,同样,客户端自己也可以控制,以浏览器为例,这里我们主要说三个场景:
1.浏览器刷新
即我们按F5刷新页面的时候,该页面的http请求中会添加:Cache-Control:max-age:0, 即说明缓存直接失效啦,就不走缓存了,直接从服务器端读取数据。
2.浏览器强制刷新
即我们按ctrl+f5强制刷新页面的时候,该页面的http请求会添加:Cache-Control:no-cache; 即表示此时要首先去服务器端验证资源是否有更新,如果有更新则直接返回最新资源,如果没有更新,则返回304,然后浏览器端判断是304的话,则从缓存中读取数据。
3.浏览器前进后退重定向
当我们点击浏览器的前进后退操作时,这个时候请求中不会有Cache-Control的字段,没有该字段,就表示会检查缓存,直接利用之前的资源,不再重新请求服务器。
httpClient缓存代码分析
需要引入HttpClinet缓存模块
测试代码如下:
public class CacheHttpClient { static CacheConfig cacheConfig = CacheConfig.custom().setMaxCacheEntries(1000).setMaxObjectSize(8192).build(); static CloseableHttpClient cachingClient = CachingHttpClients.custom().setCacheConfig(cacheConfig).build(); public static void main(String[] args) throws Exception { Scanner scanner = new Scanner(System.in); while (true) { scanner.nextLine(); HttpCacheContext context = HttpCacheContext.create(); HttpGet httpget = new HttpGet("http://www.mydomain.com/content/"); CloseableHttpResponse response = cachingClient.execute(httpget, context); try { CacheResponseStatus responseStatus = context.getCacheResponseStatus(); switch (responseStatus) { case CACHE_HIT: System.out.println( "A response was generated from the cache with " + "no requests sent upstream"); break; case CACHE_MODULE_RESPONSE: System.out.println( "The response was generated directly by the " + "caching module"); break; case CACHE_MISS: System.out.println("The response came from an upstream server"); break; case VALIDATED: System.out.println( "The response was generated from the cache " + "after validating the entry with the origin server"); break; default: // do nothing } } finally { response.close(); } } } }
缓存的核心处理逻辑位于org.apache.http.impl.client.cache.CachingExec#execute中,如下:
缓存处理的核心代码我在图中已经做了标注
1.是否启用缓存,代码很简单
2.从缓存中获取信息,这里的key实际就是访问时使用的URI,缓存底层默认使用的是一个Map
3.缓存未命中时会向服务器发送真正的请求,代码简单,不做分析
4.缓存命中,这时要处理两种情况:「缓存未过期」、「缓存过期+再验证」
对http协议了解后,这块的代码非常简单,所以笔者在这里也不赘述了
重定向
https://datatracker.ietf.org/doc/html/rfc2616#page-61
接下来我们聊聊重定向,跟重定向相关的响应码如下:
状态码 |
原因短语 |
含 义 |
301 |
Moved Permanently |
在请求的 URL 已被移除时使用。响应的 Location 首部中应该包含 资源现在所处的URL,【301代表永久重定向】,客户端在后续访问时应该将URL替换为本次Location首部标明的URL |
302 |
Found |
【302代表临时重定向】,客户端后续访问时不需要进行替换,仍然应该使用原来的URL |
303 |
See Other |
其主要目的是允许 POST 请求的响应将客户端定向到某个资源上去 |
307 |
Temporary Redirect |
也是临时重定向,跟302类似 |
你可能已经注意到 302、303 和 307 状态码之间存在一些交叉。这些状态码的用法有着细微的差别,大部分差别都源于 【HTTP/1.0 和 HTTP/1.1 应用程序对 这些状态码处理方式的不同】。
当 HTTP/1.0 客户端发起一个 POST 请求,并在响应中收到 302 重定向状态码时, 它会接受 Location 首部的重定向 URL,并向那个 URL 发起一个 GET 请求(而不 会像原始请求中那样发起 POST 请求)。
HTTP/1.0 服务器希望 HTTP/1.0 客户端这么做——如果 HTTP/1.0 服务器收到来自 HTTP/1.0 客户端的 POST 请求之后发送了 302 状态码,服务器就期望客户端能够接 受重定向 URL,并向重定向的 URL 发送一个 GET 请求。
问题出在 HTTP/1.1。HTTP/1.1 规范使用 303 状态码来实现同样的行为(服务器发 送 303 状态码来重定向客户端的 POST 请求,在它后面跟上一个 GET 请求)。
为了避开这个问题,HTTP/1.1 规范指出,对于 HTTP/1.1 客户端,用 307 状态码取 代 302 状态码来进行临时重定向。这样服务器就可以将 302 状态码保留起来,为 HTTP/1.0 客户端使用了。
HttpClient重定向代码分析
核心代码位于:org.apache.http.impl.execchain.RedirectExec#execute
重定向的处理策略都定义在redirectStrategy中,我们看下它的代码:
1.isRedirected方法,是否需要重定向
实际就是判断状态码是不是我们前文提到过的301、302、303、307。
2.getRedirect方法,重定向时需要封装的请求信息:请求方法+URL
写在最后
本来打算这篇文章作为《HTTP实战》的最后一篇的,实在太忙再加上篇幅原因,关于数据压缩、分块传输、范围请求就放到下篇文章吧~
参考:
《Http权威指南》
https://hc.apache.org/httpcomponents-client-4.5.x/current/tutorial/html/caching.html
https://datatracker.ietf.org/doc/html/rfc2616#page-74
https://datatracker.ietf.org/doc/html/rfc7232#page-13