HttpClient 4.3教程 第六章 HTTP缓存


6.1.基本概念

HttpClient的缓存机制提供一个与HTTP/1.1标准兼容的缓存层 – 相当于Java的浏览器缓存。HttpClient缓存机制的实现遵循责任链(Chain of Responsibility)设计原则,默认的HttpClient是没有缓存的,有缓存机制的HttpClient可以用来临时替代默认的HttpClient,如果开启了缓存,我们的请求结果就会从缓存中获取,而不是从目标服务器中获取。如果在Get请求头中设置了If-Modified-Since或者If-None-Match参数,那么HttpClient会自动向服务器校验缓存是否过期。

HTTP/1.1版本的缓存是语义透明的,意思是无论怎样,缓存都不应该修改客户端与服务器之间传输的请求/响应数据包。因此,在existing compliant client-server relationship中使用带有缓存的HttpClient也应该是安全的。虽然缓存是客户端的一部分,但是从Http协议的角度来看,缓存机制是为了兼容透明的缓存代理。

最后,HttpClient缓存也支持RFC 5861规定的Cache-Control拓展(stale-if-error'和stale-while-revalidate`)。

当开启缓存的HttpClient执行一个Http请求时,会经过下面的步骤:

  • 检查http请求是否符合HTTP 1.1的基本要求,如果不符合就尝试修正错误。
  • 刷新该请求无效的缓存项。(Flush any cache entries which would be invalidated by this request.)
  • 检测该请求是否可以从缓存中获取。如果不能,直接将请求发送给目标服务器,获取响应并加入缓存。
  • 如果该请求可以从缓存中获取,HttpClient就尝试读取缓存中的数据。如果读取失败,就会发送请求到目标服务器,如果可能的话,就把响应缓存起来。
  • 如果HttpClient缓存的响应可以直接返回给请求,HttpClient就会构建一个包含ByteArrayEntityBasicHttpResponse对象,并将它返回给http请求。否则,HttpClient会向服务器重新校验缓存。
  • 如果HttpClient缓存的响应,向服务器校验失败,就会向服务器重新请求数据,并将其缓存起来(如果合适的话)。
    当开启缓存的HttpClient收到服务器的响应时,会经过下面的步骤:
  • 检查收到的响应是否符合协议兼容性
  • 确定收到的响应是否可以缓存
  • 如果响应是可以缓存的,HttpClient就会尽量从响应消息中读取数据(大小可以在配置文件进行配置),并且缓存起来。
  • 如果响应数据太大,缓存或者重构消耗的响应空间不够,就会直接返回响应,不进行缓存。
    需要注意的是,带有缓存的HttpClient不是HttpClient的另一种实现,而是通过向http请求执行管道中插入附加处理组件来实现的。

6.2. RFC-2616 Compliance

HttpClient的缓存机制和RFC-2626文档规定是无条件兼容的。也就是说,只要指定了MUSTMUST NOTSHOULD或者SHOULD NOT这些Http缓存规范,HttpClient的缓存层就会按照指定的方式进行缓存。即当我们使用HttpClient的缓存机制时,HttpClient的缓存模块不会产生异常动作。

6.3. 使用范例

下面的例子讲述了如何创建一个基本的开启缓存的HttpClient。并且配置了最大缓存1000个Object对象,每个对象最大占用8192字节数据。代码中出现的数据,只是为了做演示,而过不是推荐使用的配置。

    CacheConfig cacheConfig = CacheConfig.custom()
            .setMaxCacheEntries(1000)
            .setMaxObjectSize(8192)
            .build();
    RequestConfig requestConfig = RequestConfig.custom()
            .setConnectTimeout(30000)
            .setSocketTimeout(30000)
            .build();
    CloseableHttpClient cachingClient = caching HttpClients.custom()
            .setCacheConfig(cacheConfig)
            .setDefaultRequestConfig(requestConfig)
            .build();

    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;
        }
    } finally {
        response.close();
     }

6.4. 配置

有缓存的HttpClient继承了非缓存HttpClient的所有配置项和参数(包括超时时间,连接池大小等配置项)。如果需要对缓存进行具体配置,可以初始化一个CacheConfig对象来自定义下面的参数:

  • Cache size(缓存大小). 如果后台存储支持,我们可以指定缓存的最大条数,和每个缓存中存储的response的最大size。
  • Public/private cacheing(公用/私有 缓存). 默认情况下,缓存模块会把缓存当做公用的缓存,所以缓存机制不会缓存带有授权头消息或者指定Cache-Control:private的响应。但是如果缓存只会被一个逻辑上的用户使用(和浏览器饿缓存类似),我们可能希望关闭缓存共享机制。
  • Heuristic caching(启发式缓存)。即使服务器没有明确设置缓存控制headers信息,每个RFC2616缓存也会存储一定数目的缓存。这个特征在HttpClient中默认是关闭的,如果服务器不设置控制缓存的header信息,但是我们仍然希望对响应进行缓存,就需要在HttpClient中打开这个功能。激活启发式缓存,然后使用默认的刷新时间或者自定义刷新时间。更多启发式缓存的信息,可以参考Http/1.1 RFC文档的13.2.2小节,13.2.4小节。
  • Background validation(后台校验)。HttpClient的缓存机制支持RFC5861的stale-while-revalidate指令,它允许一定数目的缓存在后台校验是否过期。我们可能需要调整可以在后台工作的最大和最小的线程数,以及设置线程在回收前最大的空闲时间。当没有足够线程来校验缓存是否过期时,我们可以指定排队队列的大小。

6.4.存储介质

默认,HttpClient缓存机制将缓存条目和缓存的response放在本地程序的jvm内存中。这样虽然提供高性能,但是当我们的程序内存有大小限制的时候,这就会变得不太合理。因为缓存的生命中期很短,如果程序重启,缓存就会失效。当前版本的HttpClient使用EhCache和memchached来存储缓存,这样就支持将缓存放到本地磁盘或者其他存储介质上。如果内存、本地磁盘、外地磁盘,都不适合你的应用程序,HttpClient也支持自定义存储介质,只需要实现HttpCacheStorage接口,然后在创建HttpClient时,使用这个接口的配置。这种情况,缓存会存储在自定义的介质中,但是you will get to reuse all of the logic surrounding HTTP/1.1 compliance and cache handling. 一般来说,可以创建出支持任何键值对指定存储(类似Java Map接口)的HttpCacheStorage,用于进行原子更新。

最后,通过一些额外的工作,还可以建立起多层次的缓存结构;磁盘中的缓存,远程memcached中的缓存,虚拟内存中的缓存,L1/L2处理器中的缓存等。

 

发表评论

电子邮件地址不会被公开。 必填项已用*标注

(Spamcheck Enabled)