目录
一、HttpClient流程
1. 请求流程
2. 响应流程
二、代码实例
1. 依赖jar包
2. HTTP缓存核心类
3. 调用代码
4. 验证
三、缓存状态
四、源码解析
1. CachingExec#execute
2. CachingExec#handleCacheHit
五、参考资料
一、HttpClient流程 1. 请求流程 2. 响应流程 二、代码实例 1. 依赖jar包
2. HTTP缓存核心类org.apache.httpcomponents httpclient4.5.13 org.apache.httpcomponents httpclient-cache4.5.6
package com.common.instance.test.config.cache; import org.apache.http.client.cache.HttpCacheStorage; import org.apache.http.client.config.RequestConfig; import org.apache.http.impl.client.CloseableHttpClient; import org.apache.http.impl.client.cache.*; import org.apache.http.impl.conn.PoolingHttpClientConnectionManager; import org.springframework.stereotype.Component; import java.util.Objects; @Component public class HttpClientCache { // http请求客户端 private static CloseableHttpClient httpClient; // 连接管理器 private static PoolingHttpClientConnectionManager connectionManager; static { connectionManager = new PoolingHttpClientConnectionManager(); // 设置最大连接数 connectionManager.setMaxTotal(500); // 默认每路由最高50并发 connectionManager.setDefaultMaxPerRoute(300); getHttpClient(); } // 获取http请求客户端 public static CloseableHttpClient getHttpClient(){ if (Objects.isNull(httpClient)){ // 缓存配置 CacheConfig cacheConfig = CacheConfig.custom() // 最大缓存条目 .setMaxCacheEntries(100) // 缓存对象最大2MB .setMaxObjectSize(2 * 1024 * 1024) // 异步更新缓存线程池最小空闲线程数 .setAsynchronousWorkersCore(5) // 异步更新缓存线程池最大线程数 .setAsynchronousWorkersMax(10) // 异步更新缓存线程池队列大小 .setRevalidationQueueSize(1000) .build(); // 缓存存储 HttpCacheStorage cacheStorage = new BasicHttpCacheStorage(cacheConfig); // 请求配置 RequestConfig requestConfig = RequestConfig.custom() // 获取数据超时时间 .setSocketTimeout(10000) // 远程建立连接的超时时间 .setConnectTimeout(10000) // 连接池获取连接的超时时间 .setConnectionRequestTimeout(10000) .build(); // 创建httpclient httpClient = CachingHttpClients.custom() .setCacheConfig(cacheConfig) .setHttpCacheStorage(cacheStorage) // 验证缓存时,缓存调度策略 .setSchedulingStrategy(new ImmediateSchedulingStrategy(cacheConfig)) .setConnectionManager(connectionManager) .setDefaultRequestConfig(requestConfig) .build(); } return httpClient; } }3. 调用代码
package com.common.instance.test.controller; import com.common.instance.test.config.cache.HttpClientCache; import com.common.instance.test.core.Response; import com.common.instance.test.core.serviceLevel.OneLevelAsyncContext; import com.common.instance.test.entity.WcPendantTab; import com.common.instance.test.service.WcPendantTabService; import com.google.common.collect.Maps; import io.swagger.annotations.Api; import io.swagger.annotations.ApiOperation; import lombok.extern.slf4j.Slf4j; import org.apache.http.HttpEntity; import org.apache.http.client.cache.CacheResponseStatus; import org.apache.http.client.cache.HttpCacheContext; import org.apache.http.client.methods.CloseableHttpResponse; import org.apache.http.client.methods.HttpGet; import org.apache.http.util.EntityUtils; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.PostMapping; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; import javax.annotation.Resource; import javax.servlet.http.HttpServletRequest; import java.io.IOException; import java.util.List; import java.util.Map; import java.util.concurrent.Callable; @Slf4j @RestController @RequestMapping("/tab") @Api(tags = "活动tab测试") public class WcPendantTabController { @Resource private WcPendantTabService wcPendantTabService; @GetMapping("/testHttpClientCache") @ApiOperation("测试HttpClient缓存") public Response4. 验证
如下结果所示,cacheResponseStatus是缓存状态,第一次缓存状态是CACHE_MISS(缓存未命中),第二次缓存状态CACHE_HIT(缓存命中),请求的资源必须是客户端可缓存的,否则缓存状态一直是CACHE_MISS。缓存状态见下章节。
// 第一次请求结果 { "success": true, "code": null, "message": null, "tip": null, "data": { "cacheResponseStatus": "CACHE_MISS", "data": "rndefine("TB_ROOT/js/localStorageObj",function(require,a){var c=!!window.localStorage;var d={init:function(a){return{expire:a||7,ts:"_timestamp"}},get:function(a){if(!c)return!1;var b=this.init();return localStorage.getItem(a+b.ts)},set:function(a,b){if(!c)return!1;var d=this.init(),e=(new Date).getTime();b=b||e,localStorage.setItem(a+d.ts,b)},del:function(a){var b=this.init();c&&localStorage.removeItem(a+b.ts)},check:function(a,b){var e,d=this.init(b);return c?(e=this.get(a),e?((new Date).getTime()-e)/1e3/60/60/24>d.expire?(this.del(a),!0):!1:!0):void 0}};a.localStorage=d});rn" } } // 第二次请求结果 { "success": true, "code": null, "message": null, "tip": null, "data": { "cacheResponseStatus": "CACHE_HIT", "data": "rndefine("TB_ROOT/js/localStorageObj",function(require,a){var c=!!window.localStorage;var d={init:function(a){return{expire:a||7,ts:"_timestamp"}},get:function(a){if(!c)return!1;var b=this.init();return localStorage.getItem(a+b.ts)},set:function(a,b){if(!c)return!1;var d=this.init(),e=(new Date).getTime();b=b||e,localStorage.setItem(a+d.ts,b)},del:function(a){var b=this.init();c&&localStorage.removeItem(a+b.ts)},check:function(a,b){var e,d=this.init(b);return c?(e=this.get(a),e?((new Date).getTime()-e)/1e3/60/60/24>d.expire?(this.del(a),!0):!1:!0):void 0}};a.localStorage=d});rn" } }三、缓存状态
org.apache.http.client.cache.CacheResponseStatus是个枚举类,如下表所示,各值的含义。
HttpClient使用职责链模式实现,使用org.apache.http.impl.client.cache.CachingExec#execute组件进行缓存 *** 作。
1. CachingExec#execute@Override public CloseableHttpResponse execute( final HttpRoute route, final HttpRequestWrapper request, final HttpClientContext context, final HttpExecutionAware execAware) throws IOException, HttpException { // 获取远程服务器地址 final HttpHost target = context.getTargetHost(); // 生成Via请求头 final String via = generateViaHeader(request.getOriginal()); // 默认响应缓存状态CACHE_MISS(缓存未命中) setResponseStatus(context, CacheResponseStatus.CACHE_MISS); // 判断请求方法是OPTIONS if (clientRequestsOurOptions(request)) { setResponseStatus(context, CacheResponseStatus.CACHE_MODULE_RESPONSE); return Proxies.enhanceResponse(new OptionsHttp11Response()); } // 默认未错误响应 final HttpResponse fatalErrorResponse = getFatallyNoncompliantResponse(request, context); if (fatalErrorResponse != null) { return Proxies.enhanceResponse(fatalErrorResponse); } // 使请求符合HTTP/1.1规范 requestCompliance.makeRequestCompliant(request); request.addHeader("Via",via); // 清除无效的缓存内容 if (!cacheableRequestPolicy.isServableFromCache(request)) { log.debug("Request is not servable from cache"); flushEntriesInvalidatedByRequest(context.getTargetHost(), request); return callBackend(route, request, context, execAware); } // 从缓存获取内容 final HttpCacheEntry entry = satisfyFromCache(target, request); // 缓存未命中,则请求上游服务 if (entry == null) { log.debug("Cache miss"); return handleCacheMiss(route, request, context, execAware); // 缓存命中 } else { return handleCacheHit(route, request, context, execAware, entry); } }2. CachingExec#handleCacheHit
private CloseableHttpResponse handleCacheHit( final HttpRoute route, final HttpRequestWrapper request, final HttpClientContext context, final HttpExecutionAware execAware, final HttpCacheEntry entry) throws IOException, HttpException { // 获取源服务器地址 final HttpHost target = context.getTargetHost(); // 记录缓存命中 recordCacheHit(target, request); CloseableHttpResponse out = null; // 当前时间 final Date now = getCurrentDate(); // 缓存内容是否可以直接响应 if (suitabilityChecker.canCachedResponseBeUsed(target, request, entry, now)) { log.debug("Cache hit"); // 命中后,生成响应 out = generateCachedResponse(request, context, entry, now); // 缓存不新鲜,若请求头有only-if-cached则响应504状态 } else if (!mayCallBackend(request)) { log.debug("Cache entry not suitable but only-if-cached requested"); out = generateGatewayTimeout(context); // 缓存不新鲜,若响应不是304状态码或则If-Modified-Since + If-None-Match,则需要请求上游服务 } else if (!(entry.getStatusCode() == HttpStatus.SC_NOT_MODIFIED && !suitabilityChecker.isConditional(request))) { log.debug("Revalidating cache entry"); return revalidateCacheEntry(route, request, context, execAware, entry, now); // 其他情况,直接请求上游服务 } else { log.debug("Cache entry not usable; calling backend"); return callBackend(route, request, context, execAware); } context.setAttribute(HttpClientContext.HTTP_ROUTE, route); context.setAttribute(HttpCoreContext.HTTP_TARGET_HOST, target); context.setAttribute(HttpCoreContext.HTTP_REQUEST, request); context.setAttribute(HttpCoreContext.HTTP_RESPONSE, out); context.setAttribute(HttpCoreContext.HTTP_REQ_SENT, Boolean.TRUE); return out; }五、参考资料
HttpClient详细使用示例_JustryDeng-CSDN博客_httpclient
HttpClient 4.3详细教程之HTTP缓存_liujiding的博客-CSDN博客
浏览器缓存缓存策略(看完就懂) - 掘金
欢迎分享,转载请注明来源:内存溢出
评论列表(0条)