HttpClient API 文档:6. HTTP 缓存

6.1. 一般概念

HttpClient Cache 提供了用 HttpClient(等效浏览器缓存的 Java 实现)来兼容 HTTP / 1.1 的缓存层。该实现遵循责任链模式。HttpClient 缓存的实现类可以替代默认无缓存的 HttpClient;完全可以通过缓存实现的请求将不会触发实际的原始请求。在使用条件 GETs 和 If-Modified-Since 和/或 If-None-Match 请求头的情况下,尽可能使用源自动验证过时的缓存条目。

通常,HTTP / 1.1 缓存被设计为在语义上透明;也就是说,缓存不应该更改客户端和服务器之间的请求-响应交换的含义。因此,将缓存的 HttpClient 放到现有的兼容客户端-服务器关系中应该是安全的。尽管从 HTTP 协议的角度来看,缓存模块是客户端的一部分,但是该实现旨在与透明缓存代理上的要求兼容。

最后,HttpClient 缓存实现类包括对 RFC 5861 指定的 Cache-Control 扩展的支持(过时错误和过时重新验证)。

当缓存 HttpClient 执行一个请求时,它会执行以下流程:

  1. 检查要求是否符合基本的 HTTP 1.1 协议并尝试正确的请求。
  2. 刷新当前请求导致无效的一些缓存项。
  3. 确定当前请求可以从缓存中提供。如果不能, 则直接将这个请求发送给原始服务器并换回响应,如果合适的话保存缓存.
  4. 如果是 cache-servable 请求时,将尝试从缓存中读取。当缓存中没有的情况下,调用原始的服务器,如果可以的话将响应缓存。
  5. 如果缓存中的响应能够被当做一个响应提供服务,构造一个包含 ByteArrayEntity 的 BasicHttpResponse 并返回。否则,尝试重新验证对原始服务器的缓存项。
  6. 当一个缓存的响应不能重新生效的情况下,调用原始服务器,如果可以的话缓存响应。

当缓存 HttpClient 收到响应时,它会执行以下流程:

  1. 根据协议内容,检查响应。
  2. 确定响应是否可以被缓存。
  3. 如果可以被缓存,读取配置中允许的最多内容,并将其保存在缓存中。
  4. 如果对缓存来说响应过大,则重新构建被消费的部分响应,并跳过缓存流程直接返回。

需要注意的是,HttpClient Cache 本身并不是一个 HttpClient 的不同实现,而是通过插件的方式作为一个额外管道执行请求。

6.2. RFC-2616 规范

我们相信 HttpClient 缓存是无条件地符合 RFC-2616。就是说,无论规范在什么地方指示对于 HTTP 缓存都是“必须”,“不得”,“应该”或“不应”,缓存层都将尝试以满足那些要求的方式运行。这意味着当您放入缓存模块时,它不会产生不正确的行为。

6.3. 使用样例

下面是一个如何建立一个基本的缓存 HttpClient 例子。根据配置,他会存储一个最大数量为 1000 的缓存实例,每个最多能够保存 8192 bytes。这里的数字仅仅是个例子,并不是规定,也不能被当做建议。

CacheConfig cacheConfig = CacheConfig.custom()
        .setMaxCacheEntries(1000)
        .setMaxObjectSize(8192)
        .build();
RequestConfig requestConfig = RequestConfig.custom()
        .setConnectTimeout(30000)
        .setSocketTimeout(30000)
        .build();
CloseableHttpClient cachingClient = CachingHttpClients.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 Cache 继承了所有非缓存实现的配置项和参数(包括像超时时间、连接池大小这样的配置选项)。对于特殊的缓存配置,可以通过定义 CacheConfig 实例来自定义行为,它包含以下的配置:

  • Cache size(缓存大小)。后端存储支持这些限制,可以像最大的可缓存响应体大小一样指定缓存项的数量。

  • Public/private caching(公共/私有缓存)。 在默认情况下,缓存模块本身是一个共享缓存,例如,不会缓存带有授权请求头的响应和标记为 Cache-Control: private 的响应。然而,如果缓存仅仅被应用于一个“用户”(类似于浏览器缓存的行为)逻辑,那么它将关闭共享缓存设置。

  • Heuristic caching(启发式缓存)。按照 RFC2616,,缓存可以缓存特定的缓存条目,即使没有显示原始设置的缓存控制头。这种行为在默认情况下是关闭的,但是如果你处理的是一个没有完全设置原始请求头的请求,你可能想要打开它。你需要一个可以启发式的缓存,然后指定一个默认的新生命周期,并且/或者资源被最后修改后的一小段时间。参考 HTTP/1.1 RFC 的 13.2.2 和 13.2.4 部分,可以获得启发式缓存的更多细节。

  • Background validation(后台验证)。 缓存模块支持 RFC5861 的 stale-while-revalidate 指令,允许后台特定的缓存项重新生效。可以需要调整设置后台工作线程的最小和最大工作数量,以及他们回收前闲置的最大时间。没有足够的 worker 来跟上需求时,您还可以控制队列的大小用于重新生效线程。

6.5. 后端存储

默认的 HttpClient Cache 的实现会存储缓存项,并且在应用的 JVM 内存中缓存响应体。虽然这些能够提供高性能,但是有内存大小的限制,或者因为缓存项在应用重启后会失效,所以可能并不适用于您的应用。当前版本允许将缓存项保存到硬盘上,或者使用其他外部进程 EhCache 和 Memcached 存储缓存项。

如果这些选项都满足不了你的应用,可以通过实现 HttpCachedStorage 提供你自己的后台存储,并且在构建的时候提供给缓存 HttpClient。在这种情况下,缓存项会通过你自己的框架存储,但是会重用所有 HTTP/1.1 协议的逻辑和缓存处理。一般来说,应该能够创建一个支持原子操作的 key/value 存储(类似于 java 的 Map 接口)的 HttpCachedStorage 实现。

最后,通过一些额外的努力完全有可能建立一个多层缓存层次结构;比如,包装一个内存中的缓存 HttpClient 在磁盘上的存储缓存条目,在 memcached 中远程存储,后一种模式类似于虚拟内存,L1 / L2 处理器缓存等。

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 203,456评论 5 477
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,370评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 150,337评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,583评论 1 273
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,596评论 5 365
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,572评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,936评论 3 395
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,595评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,850评论 1 297
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,601评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,685评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,371评论 4 318
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,951评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,934评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,167评论 1 259
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 43,636评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,411评论 2 342

推荐阅读更多精彩内容