成都的网站建设公司哪家好,企业为什么要创新,建设网站服务,最新免费网站源码游戏SDK架构设计之代码实现——网络框架 OKHttp 源码解析#xff08;一#xff09; OKHttp 源码解析#xff08;二#xff09;拦截器
前言
上一篇解读了OKHttp 的基本框架源码#xff0c;其中 OKHttp 发送请求的核心是调用 getResponseWithInterceptorChain 构建拦截器链…游戏SDK架构设计之代码实现——网络框架 OKHttp 源码解析一 OKHttp 源码解析二拦截器
前言
上一篇解读了OKHttp 的基本框架源码其中 OKHttp 发送请求的核心是调用 getResponseWithInterceptorChain 构建拦截器链遍历拦截器执行请求执行完成时返回结果。这篇看一下 OKHttp 的拦截器链。
本文查看 OKHttp 源码的版本是 3.4.2.
OkHttp 的拦截器使用了责任链设计模式使得每个处理者都有机会处理请求关于责任链设计模式的介绍见文章
拦截器源码解析
无论是同步请求还是异步请求OkHttp 都是先调用 getResponseWithInterceptorChain 方法。添加拦截器的顺序就是执行的顺序。
private Response getResponseWithInterceptorChain() throws IOException {// Build a full stack of interceptors.ListInterceptor interceptors new ArrayList();// 添加开发者自定义的拦截器interceptors.addAll(client.interceptors());// 失败重连拦截器interceptors.add(retryAndFollowUpInterceptor);// 桥接和适配器interceptors.add(new BridgeInterceptor(client.cookieJar()));//缓存interceptors.add(new CacheInterceptor(client.internalCache()));// 链接interceptors.add(new ConnectInterceptor(client));if (!retryAndFollowUpInterceptor.isForWebSocket()) {// 网络interceptors.addAll(client.networkInterceptors());}// 请求服务interceptors.add(new CallServerInterceptor(retryAndFollowUpInterceptor.isForWebSocket()));// 创建拦截器链Interceptor.Chain chain new RealInterceptorChain(interceptors, null, null, null, 0, originalRequest);// 拦截器链执行结果return chain.proceed(originalRequest);}1、开发者自定义的拦截器
OkHttp 允许开发者自定义拦截器并优先执行开发者定义的拦截器。具体做法如下 定义拦截器实现 OkHttp 的拦截器接口 Interceptor public class RetryInterceptor implements Interceptor {public Response intercept(Chain chain) throws IOException {// ...}
}// 官网解释观察、修改并潜在地短路发出的请求和返回的相应响应。
// 通常拦截器会添加、删除或转换请求或响应的标头。
public interface Interceptor {// 实现方法处理请求Response intercept(Chain chain) throws IOException;// 处理者串成的链责任链模式中的一个角色让每个处理者都有机会处理请求interface Chain {// 获取当前的请求Request request();// 处理请求Response proceed(Request request) throws IOException;// 网络链接Connection connection();}
}通过OkHttpClient 的 addInterceptor 接口添加到拦截器列表里 public Builder addInterceptor(Interceptor interceptor) {interceptors.add(interceptor);return this;
}2、RetryAndFollowUpInterceptor 重试重定向拦截器
RetryAndFollowUpInterceptor 在调用 newCall 时返回 RealCall 对象时创建。
protected RealCall(OkHttpClient client, Request originalRequest) {this.client client;this.originalRequest originalRequest;this.retryAndFollowUpInterceptor new RetryAndFollowUpInterceptor(client);}/*** This interceptor recovers from failures and follows redirects as necessary. It may throw an* {link IOException} if the call was canceled.
主要管理请求重试次数和重定向的*/
public final class RetryAndFollowUpInterceptor implements Interceptor {/*** How many redirects and auth challenges should we attempt? Chrome follows 21 redirects; Firefox,* curl, and wget follow 20; Safari follows 16; and HTTP/1.0 recommends 5.*/// 定义最大重试和重定向次数private static final int MAX_FOLLOW_UPS 20;Override public Response intercept(Chain chain) throws IOException {Request request chain.request();// 这是一个路由、链接池和流的调用逻辑管理类负责调度协调streamAllocation new StreamAllocation(client.connectionPool(), createAddress(request.url()));int followUpCount 0;Response priorResponse null;// 无限循环while (true) {// 如果取消掉释放链接池if (canceled) {streamAllocation.release();throw new IOException(Canceled);}Response response null;boolean releaseConnection true;try {// 执行拦截器链获取结果response ((RealInterceptorChain) chain).proceed(request, streamAllocation, null, null);releaseConnection false;} catch (RouteException e) {// The attempt to connect via a route failed. The request will not have been sent.// 通过 recover 方法判断是否需要进行重试否则中断此次循环开始下一次循环// 如果是一些永久故障则不尝试。1.设置了不允许重试2.不可再次发送请求异常 3. 致命的异常 4.没有更多的路由可以尝试if (!recover(e.getLastConnectException(), true, request)) throw e.getLastConnectException();releaseConnection false;continue;} catch (IOException e) {// An attempt to communicate with a server failed. The request may have been sent.// 处理同上if (!recover(e, false, request)) throw e;releaseConnection false;continue;} finally {// Were throwing an unchecked exception. Release any resources.// 不是以上异常情况不进行重试if (releaseConnection) {streamAllocation.streamFailed(null);streamAllocation.release();}}// Attach the prior response if it exists. Such responses never have a body.// 关联前一个响应 责任链模式的特点之一之前的请求结果统一返回if (priorResponse ! null) {response response.newBuilder().priorResponse(priorResponse.newBuilder().body(null).build()).build();}// 处理重定向Request followUp followUpRequest(response);// 不需要重定向此次请求结束释放资源if (followUp null) {if (!forWebSocket) {streamAllocation.release();}return response;}closeQuietly(response.body());
// 重定向次数限制超过一定次数抛出异常if (followUpCount MAX_FOLLOW_UPS) {streamAllocation.release();throw new ProtocolException(Too many follow-up requests: followUpCount);}if (followUp.body() instanceof UnrepeatableRequestBody) {throw new HttpRetryException(Cannot retry streamed HTTP body, response.code());}if (!sameConnection(response, followUp.url())) {streamAllocation.release();streamAllocation new StreamAllocation(client.connectionPool(), createAddress(followUp.url()));} else if (streamAllocation.stream() ! null) {throw new IllegalStateException(Closing the body of response didnt close its backing stream. Bad interceptor?);}request followUp;priorResponse response;}}}followUpRequest 根据结果响应码处理是否需要重定向。
当服务端数据迁移网址以后客户端使用原地址请求数据时服务端会将迁移后的新地址通过 header 返回给客户端同时携带 3 开头的错误码通知客户端目标地址已修改需要重定向地址。
客户端收到后根据响应码和新地址重新发出请求获取目标数据。
/*** Figures out the HTTP request to make in response to receiving {code userResponse}. This will* either add authentication headers, follow redirects or handle a client request timeout. If a* follow-up is either unnecessary or not applicable, this returns null.*/private Request followUpRequest(Response userResponse) throws IOException {if (userResponse null) throw new IllegalStateException();Connection connection streamAllocation.connection();Route route connection ! null? connection.route(): null;int responseCode userResponse.code();final String method userResponse.request().method();switch (responseCode) {//407 客户端使用HTTP代理服务器需要代理身份认证case HTTP_PROXY_AUTH:Proxy selectedProxy route ! null? route.proxy(): client.proxy();if (selectedProxy.type() ! Proxy.Type.HTTP) {throw new ProtocolException(Received HTTP_PROXY_AUTH (407) code while not using proxy);}return client.proxyAuthenticator().authenticate(route, userResponse);// 401 身份验证服务器需要验证者身份case HTTP_UNAUTHORIZED:return client.authenticator().authenticate(route, userResponse);// 3xx 重定向case HTTP_PERM_REDIRECT: case HTTP_TEMP_REDIRECT:// If the 307 or 308 status code is received in response to a request other than GET// or HEAD, the user agent MUST NOT automatically redirect the request
// 看以上注释307、308 除 get 、head 外不会重定向if (!method.equals(GET) !method.equals(HEAD)) {return null;}// fall-throughcase HTTP_MULT_CHOICE: //300case HTTP_MOVED_PERM: //301case HTTP_MOVED_TEMP: // 302case HTTP_SEE_OTHER: //303// Does the client allow redirects? 客户端是否允许重定向if (!client.followRedirects()) return null;String location userResponse.header(Location);if (location null) return null;// 将旧地址替换为新地址HttpUrl url userResponse.request().url().resolve(location);// Dont follow redirects to unsupported protocols.if (url null) return null;// If configured, dont follow redirects between SSL and non-SSL.// http 和 https 之间的切换不允许重定向boolean sameScheme url.scheme().equals(userResponse.request().url().scheme());if (!sameScheme !client.followSslRedirects()) return null;// Redirects dont include a request body.Request.Builder requestBuilder userResponse.request().newBuilder();if (HttpMethod.permitsRequestBody(method)) {// 除 PROPFIND 之外的所有请求都应重定向到 GET 请求。if (HttpMethod.redirectsToGet(method)) {requestBuilder.method(GET, null);} else {requestBuilder.method(method, null);}// 将请求体中有关body的信息删除requestBuilder.removeHeader(Transfer-Encoding);requestBuilder.removeHeader(Content-Length);requestBuilder.removeHeader(Content-Type);}// When redirecting across hosts, drop all authentication headers. This// is potentially annoying to the application layer since they have no// way to retain them.// 当跨主机重定向时,删除请求头Authorization身份验证信息if (!sameConnection(userResponse, url)) {requestBuilder.removeHeader(Authorization);}return requestBuilder.url(url).build();
// 4xx 不是重定向case HTTP_CLIENT_TIMEOUT:// 408s are rare in practice, but some servers like HAProxy use this response code. The// spec says that we may repeat the request without modifications. Modern browsers also// repeat the request (even non-idempotent ones.)// 408 很少见。但一些服务器如 HAProxy使用此响应代码。 规范说我们可以不加修改地重复请求。 现代浏览器也会重复请求甚至是非幂等的。if (userResponse.request().body() instanceof UnrepeatableRequestBody) {return null;}return userResponse.request();default:return null;}}重试重定向拦截器总结
开始一个无限循环调用之后的拦截器获取响应结果response ((RealInterceptorChain) chain).proceed(request, streamAllocation, null, null);请求或链接过程中抛出异常在异常内通过 recover 方法判断是否需要重试。否则返回priorResponse 链接之前的 response。根据响应码判断是否需要重定向 followUpRequest不需要重定向结束请求返回响应释放资源需要重定向执行有限次数重定向超过一定次数抛出异常。
3、BridgeInterceptor桥连接拦截器
桥梁拦截器是应用程序和HTTP网络的桥梁。根据用户请求构建网络请求发送到服务器。将服务器的响应转换为用户能够识别的响应。
public final class BridgeInterceptor implements Interceptor {private final CookieJar cookieJar;public BridgeInterceptor(CookieJar cookieJar) {this.cookieJar cookieJar;}Override public Response intercept(Chain chain) throws IOException {Request userRequest chain.request();Request.Builder requestBuilder userRequest.newBuilder();RequestBody body userRequest.body();if (body ! null) { // 请求体不为空时将请求体设置到请求头中MediaType contentType body.contentType();if (contentType ! null) {requestBuilder.header(Content-Type, contentType.toString());}long contentLength body.contentLength();if (contentLength ! -1) {requestBuilder.header(Content-Length, Long.toString(contentLength));requestBuilder.removeHeader(Transfer-Encoding);} else {
// 请求长度不确定时使用分块传输 chunked减少资源消耗提高效率requestBuilder.header(Transfer-Encoding, chunked);requestBuilder.removeHeader(Content-Length);}}
// 向header中添加 url 域名if (userRequest.header(Host) null) {requestBuilder.header(Host, hostHeader(userRequest.url(), false));}
// 默认设置长链接。长链接就是在 tcp 发送完消息后不关闭连接而是持续连接
// 因为TCP 连接需要三次握手四次挥手为节省资源减少消耗在后续传输中重用if (userRequest.header(Connection) null) {requestBuilder.header(Connection, Keep-Alive);}// If we add an Accept-Encoding: gzip header field were responsible for also decompressing// the transfer stream.boolean transparentGzip false;if (userRequest.header(Accept-Encoding) null) {transparentGzip true;requestBuilder.header(Accept-Encoding, gzip);}// 获取 客户端的cookie信息ListCookie cookies cookieJar.loadForRequest(userRequest.url());if (!cookies.isEmpty()) {requestBuilder.header(Cookie, cookieHeader(cookies));}
// 请求的用户信息if (userRequest.header(User-Agent) null) {requestBuilder.header(User-Agent, Version.userAgent());}Response networkResponse chain.proceed(requestBuilder.build());
// 根据返回的信息是否保存到cookie中
// CookieJar.NO_COOKIES 或者没有要保存的信息时不保存
// 否则调用 saveFromResponse 由客户端保存HttpHeaders.receiveHeaders(cookieJar, userRequest.url(), networkResponse.headers());Response.Builder responseBuilder networkResponse.newBuilder().request(userRequest);
// 如果有响应体且是gzip类型将响应体解压为 gzip 对象if (transparentGzip gzip.equalsIgnoreCase(networkResponse.header(Content-Encoding)) HttpHeaders.hasBody(networkResponse)) {GzipSource responseBody new GzipSource(networkResponse.body().source());Headers strippedHeaders networkResponse.headers().newBuilder().removeAll(Content-Encoding).removeAll(Content-Length).build();responseBuilder.headers(strippedHeaders);responseBuilder.body(new RealResponseBody(strippedHeaders, Okio.buffer(responseBody)));}return responseBuilder.build();}BridgeInterceptor桥连接拦截器总结
添加请求头将用户请求转换为能够进行网络访问的请求。执行拦截器链的下一个拦截器方法。获取响应将响应转成应用可识别的response。
4、CacheInterceptor缓存拦截器
public final class CacheInterceptor implements Interceptor {public CacheInterceptor(InternalCache cache) {this.cache cache;}Override public Response intercept(Chain chain) throws IOException {
// 如果客户端实现缓存对象则根据url获取本地缓存对象Response cacheCandidate cache ! null? cache.get(chain.request()): null;long now System.currentTimeMillis();// 获取缓存策略对象见下方介绍CacheStrategy strategy new CacheStrategy.Factory(now, chain.request(), cacheCandidate).get();Request networkRequest strategy.networkRequest;Response cacheResponse strategy.cacheResponse;if (cache ! null) {cache.trackResponse(strategy);}if (cacheCandidate ! null cacheResponse null) {closeQuietly(cacheCandidate.body()); // The cache candidate wasnt applicable. Close it.}// If were forbidden from using the network and the cache is insufficient, fail.
// 不需要访问网络也不需要缓存返回 504if (networkRequest null cacheResponse null) {return new Response.Builder().request(chain.request()).protocol(Protocol.HTTP_1_1).code(504).message(Unsatisfiable Request (only-if-cached)).body(EMPTY_BODY).sentRequestAtMillis(-1L).receivedResponseAtMillis(System.currentTimeMillis()).build();}// If we dont need the network, were done.
// 网络请求是空的返回响应if (networkRequest null) {return cacheResponse.newBuilder().cacheResponse(stripBody(cacheResponse)).build();}Response networkResponse null;try {
// 执行请求networkResponse chain.proceed(networkRequest);} finally {// If were crashing on I/O or otherwise, dont leak the cache body.if (networkResponse null cacheCandidate ! null) {closeQuietly(cacheCandidate.body());}}// If we have a cache response too, then were doing a conditional get.
// 本地缓存不为空可以继续使用本地资源if (cacheResponse ! null) {if (validate(cacheResponse, networkResponse)) {Response response cacheResponse.newBuilder().headers(combine(cacheResponse.headers(), networkResponse.headers())).cacheResponse(stripBody(cacheResponse)).networkResponse(stripBody(networkResponse)).build();networkResponse.body().close();// Update the cache after combining headers but before stripping the// Content-Encoding header (as performed by initContentStream()).cache.trackConditionalCacheHit();cache.update(cacheResponse, response);return response;} else {closeQuietly(cacheResponse.body());}}Response response networkResponse.newBuilder().cacheResponse(stripBody(cacheResponse)).networkResponse(stripBody(networkResponse)).build();if (HttpHeaders.hasBody(response)) {CacheRequest cacheRequest maybeCache(response, networkResponse.request(), cache);response cacheWritingResponse(cacheRequest, response);}return response;}}判断本地是否有缓存根据
缓存策略对象
public Factory(long nowMillis, Request request, Response cacheResponse) {this.nowMillis nowMillis;this.request request;this.cacheResponse cacheResponse;if (cacheResponse ! null) {
// 发送请问的时间this.sentRequestMillis cacheResponse.sentRequestAtMillis();
// 获取响应的时间
// 记录时间是为了计算当前缓存是否有效this.receivedResponseMillis cacheResponse.receivedResponseAtMillis();Headers headers cacheResponse.headers();for (int i 0, size headers.size(); i size; i) {String fieldName headers.name(i);String value headers.value(i);// 请求发送时间if (Date.equalsIgnoreCase(fieldName)) {servedDate HttpDate.parse(value);servedDateString value;
// 缓存过期时间} else if (Expires.equalsIgnoreCase(fieldName)) {expires HttpDate.parse(value);
// 目标资源最后修改时间} else if (Last-Modified.equalsIgnoreCase(fieldName)) {lastModified HttpDate.parse(value);lastModifiedString value;
// 目标资源的标识如果目标资源被修改过之后标识会变客户端需要用标识对比是否一致不一致说明资源已被修改需要重新请求} else if (ETag.equalsIgnoreCase(fieldName)) {etag value;
// 缓存的年龄} else if (Age.equalsIgnoreCase(fieldName)) {ageSeconds HttpHeaders.parseSeconds(value, -1);}}}
}// 获取缓存策略对象
public CacheStrategy get() {CacheStrategy candidate getCandidate();// 如果需要网络请求且客户端只取缓存信息条件冲突返回一个if (candidate.networkRequest ! null request.cacheControl().onlyIfCached()) {// Were forbidden from using the network and the cache is insufficient.return new CacheStrategy(null, null);}return candidate;}根据不同的条件返回缓存策略对象重点
private CacheStrategy getCandidate() {// No cached response.
//没有缓存请求网络if (cacheResponse null) {return new CacheStrategy(request, null);}// Drop the cached response if its missing a required handshake.
// 缺少需要的握手删除缓存的响应if (request.isHttps() cacheResponse.handshake() null) {return new CacheStrategy(request, null);}// If this response shouldnt have been stored, it should never be used// as a response source. This check should be redundant as long as the// persistence store is well-behaved and the rules are constant./*凡是响应码为200, 203, 204, 300, 301, 404, 405, 410, 414, 501, 308,若请求头或响应头中不包含noStore(不允许存储缓存),返回true凡是响应码为303,307,响应头中包含Expires(值为缓存过期时间),或Cache-Control中带有max-age,public,private其中一个,同样不包含noStore,返回true否则,一律返回false*/if (!isCacheable(cacheResponse, request)) {return new CacheStrategy(request, null);}/* Cache-Control:noCache,请求头信息,noCache并不是表面看起来的不缓存数据,数据也会进行缓存,只是每次在使用本地缓存前需要先进行一次网络请求验证缓存If-Modified-Since:请求头中携带的存储客户端缓存最后修改的时间,服务器将实际文件修改时间与请求头中时间进行对比,若相同返回304码表示目标资源未更改,客户端可以使用本地缓存,若不同返回200,同时将目标资源返回给客户端If-None-Match:与If-Modified-Since类似作用,value为缓存资源的ETag值(资源唯一标识),到服务端时同样会进行比较,相同返回304,不同返回目标资源*/
// 请求包含noCache请求头或If-Modified-Since或者有If-None-Match,访问网络CacheControl requestCaching request.cacheControl();if (requestCaching.noCache() ||hasConditions(request)) {return new CacheStrategy(request, null);}// 缓存产生到现在的时间long ageMillis cacheResponseAge();
// 响应缓存最小可用时间long freshMillis computeFreshnessLifetime();if (requestCaching.maxAgeSeconds() ! -1) {freshMillis Math.min(freshMillis,SECONDS.toMillis(requestCaching.maxAgeSeconds()));}
//客户端设置的缓存剩余有效可用时间long minFreshMillis 0;if (requestCaching.minFreshSeconds() ! -1) {minFreshMillis SECONDS.toMillis(requestCaching.minFreshSeconds());}
// 修改过期后还可以使用的时长未设置时表示过期多久都可以使用long maxStaleMillis 0;CacheControl responseCaching cacheResponse.cacheControl();if (!responseCaching.mustRevalidate() requestCaching.maxStaleSeconds() ! -1) {maxStaleMillis SECONDS.toMillis(requestCaching.maxStaleSeconds());}
// 如果不需要访问网络且缓存年龄客户端认为的最小缓存有效时间缓存实际有效时长缓存后仍可使用时长 ———— 可以使用缓存if (!responseCaching.noCache() ageMillis minFreshMillis freshMillis maxStaleMillis) {Response.Builder builder cacheResponse.newBuilder();
// 缓存年龄客户端认为的最小缓存有效时间 超过响应缓存的最小有效时间if (ageMillis minFreshMillis freshMillis) {builder.addHeader(Warning, 110 HttpURLConnection \Response is stale\);}
// 缓存年龄超过1天且没有设置过时间抛出警告long oneDayMillis 24 * 60 * 60 * 1000L;if (ageMillis oneDayMillis isFreshnessLifetimeHeuristic()) {builder.addHeader(Warning, 113 HttpURLConnection \Heuristic expiration\);}return new CacheStrategy(null, builder.build());}// Find a condition to add to the request. If the condition is satisfied, the response body// will not be transmitted.
// 需要请求网络请求网络验证头信息String conditionName;String conditionValue;if (etag ! null) {conditionName If-None-Match;conditionValue etag;} else if (lastModified ! null) {conditionName If-Modified-Since;conditionValue lastModifiedString;} else if (servedDate ! null) {conditionName If-Modified-Since;conditionValue servedDateString;} else {return new CacheStrategy(request, null); // No condition! Make a regular request.}Headers.Builder conditionalRequestHeaders request.headers().newBuilder();Internal.instance.addLenient(conditionalRequestHeaders, conditionName, conditionValue);Request conditionalRequest request.newBuilder().headers(conditionalRequestHeaders.build()).build();return new CacheStrategy(conditionalRequest, cacheResponse);
}
缓存拦截器作用是请求网络获取新数据还是使用缓存的数据缓存拦截器的核心在于返回一个什么样的缓存策略。
5、ConnectInterceptor 链接拦截器
Override public Response intercept(Chain chain) throws IOException {RealInterceptorChain realChain (RealInterceptorChain) chain;Request request realChain.request();
// 获取一个 StreamAllocationStreamAllocation streamAllocation realChain.streamAllocation();// We need the network to satisfy this request. Possibly for validating a conditional GET.boolean doExtensiveHealthChecks !request.method().equals(GET);
// 创建获取一个健康的链接 findHealthyConnectionHttpStream httpStream streamAllocation.newStream(client, doExtensiveHealthChecks);RealConnection connection streamAllocation.connection();return realChain.proceed(request, streamAllocation, httpStream, connection);
}private RealConnection findHealthyConnection(int connectTimeout, int readTimeout,int writeTimeout, boolean connectionRetryEnabled, boolean doExtensiveHealthChecks)throws IOException {
// 无限循环获取 connection 链接while (true) {RealConnection candidate findConnection(connectTimeout, readTimeout, writeTimeout,connectionRetryEnabled);// If this is a brand new connection, we can skip the extensive health checks.
// 如果是一个没有过链接记录的链接无需检查直接返回synchronized (connectionPool) {if (candidate.successCount 0) {return candidate;}}// Do a (potentially slow) check to confirm that the pooled connection is still good. If it// isnt, take it out of the pool and start again.
// 检查查找到的链接是否健康比如是否已经closed、shutdown、outputshutdown链接是否超时等问题if (!candidate.isHealthy(doExtensiveHealthChecks)) {noNewStreams();continue;}return candidate;}}**StreamAllocation**是链接、流、路由之间的桥梁。官方解释如下
连接到远程服务器的物理套接字连接。 这些建立起来可能很慢因此有必要能够取消当前正在连接的连接。 流在连接上分层的逻辑 HTTP 请求/响应对。 每个连接都有自己的分配限制它定义了该连接可以承载多少个并发流。 HTTP/1.x 连接一次可以携带 1 个流SPDY 和 HTTP/2 通常携带多个。 调用流的逻辑序列通常是初始请求及其后续请求。 我们更愿意将单个呼叫的所有流保持在同一连接上以获得更好的行为和位置。
此类的实例代表调用在一个或多个连接上使用一个或多个流。 此类具有用于释放上述每个资源的 API noNewStreams() 防止连接在未来被用于新的流。 在 Connection: close 标头之后或连接可能不一致时使用它。 streamFinished() 从此分配中释放活动流。 请注意在给定时间只有一个流可能处于活动状态因此有必要在使用 newStream() 创建后续流之前调用 streamFinished()。 release() 删除呼叫对连接的保持。 请注意如果仍然存在流这不会立即释放连接。 当调用完成但其响应主体尚未完全消耗时就会发生这种情况。
支持异步取消。
ConnectionPool 管理 HTTP 和 SPDY 连接的重用以减少网络延迟。 共享同一地址的 HTTP 请求可能共享一个连接。 此类实现了哪些连接保持打开以供将来使用的策略。
public ConnectionPool() {
// 一个连接池最多有个 5 个空闲链接每个链接超出 5 分钟无动作被移除this(5, 5, TimeUnit.MINUTES);
}
// 循环遍历获取连接
RealConnection get(Address address, StreamAllocation streamAllocation) {assert (Thread.holdsLock(this));for (RealConnection connection : connections) {
// 1.达到最大不可复用限制
// 2.保证和上次请求的是一个地址
// 3.链接不能添加新的流if (connection.allocations.size() connection.allocationLimit address.equals(connection.route().address) !connection.noNewStreams) {streamAllocation.acquire(connection);return connection;}}return null;}// 放入链接
void put(RealConnection connection) {assert (Thread.holdsLock(this));
// 先判断是否有需要清理的连接 if (!cleanupRunning) {cleanupRunning true;
// 执行清理回收算法。
// 内部逻辑找到最不活跃的连接当空闲的连接超过5个后删除这个不活跃的连接。
// 内部是死循环当所有连接都是活跃状态时暂停执行回收机制直到池中无连接。executor.execute(cleanupRunnable);}
// 将连接添加到连接池connections.add(connection);}连接拦截器
根据地址、证书、DNS 、最大限制流、超空闲时间等条件确定一个连接是否能复用。创建一个新的连接。 文章转载自: http://www.morning.lpzyq.cn.gov.cn.lpzyq.cn http://www.morning.rrjzp.cn.gov.cn.rrjzp.cn http://www.morning.njqpg.cn.gov.cn.njqpg.cn http://www.morning.fjglf.cn.gov.cn.fjglf.cn http://www.morning.rwlsr.cn.gov.cn.rwlsr.cn http://www.morning.gbfuy28.cn.gov.cn.gbfuy28.cn http://www.morning.nrqtk.cn.gov.cn.nrqtk.cn http://www.morning.tkcz.cn.gov.cn.tkcz.cn http://www.morning.nnwpz.cn.gov.cn.nnwpz.cn http://www.morning.tlnbg.cn.gov.cn.tlnbg.cn http://www.morning.srmdr.cn.gov.cn.srmdr.cn http://www.morning.dtzxf.cn.gov.cn.dtzxf.cn http://www.morning.4r5w91.cn.gov.cn.4r5w91.cn http://www.morning.ffrys.cn.gov.cn.ffrys.cn http://www.morning.gjmll.cn.gov.cn.gjmll.cn http://www.morning.pqqzd.cn.gov.cn.pqqzd.cn http://www.morning.brlgf.cn.gov.cn.brlgf.cn http://www.morning.xlyt.cn.gov.cn.xlyt.cn http://www.morning.paoers.com.gov.cn.paoers.com http://www.morning.slmbg.cn.gov.cn.slmbg.cn http://www.morning.drcnn.cn.gov.cn.drcnn.cn http://www.morning.zhnyj.cn.gov.cn.zhnyj.cn http://www.morning.pghfy.cn.gov.cn.pghfy.cn http://www.morning.wqcz.cn.gov.cn.wqcz.cn http://www.morning.czqqy.cn.gov.cn.czqqy.cn http://www.morning.wrlcy.cn.gov.cn.wrlcy.cn http://www.morning.ghxkm.cn.gov.cn.ghxkm.cn http://www.morning.lynmt.cn.gov.cn.lynmt.cn http://www.morning.nyfyq.cn.gov.cn.nyfyq.cn http://www.morning.wjhqd.cn.gov.cn.wjhqd.cn http://www.morning.ryxdf.cn.gov.cn.ryxdf.cn http://www.morning.phcqk.cn.gov.cn.phcqk.cn http://www.morning.ruyuaixuexi.com.gov.cn.ruyuaixuexi.com http://www.morning.mwjwy.cn.gov.cn.mwjwy.cn http://www.morning.yfmxn.cn.gov.cn.yfmxn.cn http://www.morning.rqfkh.cn.gov.cn.rqfkh.cn http://www.morning.xqndf.cn.gov.cn.xqndf.cn http://www.morning.qsbcg.cn.gov.cn.qsbcg.cn http://www.morning.xlyt.cn.gov.cn.xlyt.cn http://www.morning.pfmsh.cn.gov.cn.pfmsh.cn http://www.morning.hctgn.cn.gov.cn.hctgn.cn http://www.morning.rysmn.cn.gov.cn.rysmn.cn http://www.morning.fnfhs.cn.gov.cn.fnfhs.cn http://www.morning.rgkd.cn.gov.cn.rgkd.cn http://www.morning.cgstn.cn.gov.cn.cgstn.cn http://www.morning.lblsx.cn.gov.cn.lblsx.cn http://www.morning.mlfgx.cn.gov.cn.mlfgx.cn http://www.morning.qqhfc.cn.gov.cn.qqhfc.cn http://www.morning.qyxnf.cn.gov.cn.qyxnf.cn http://www.morning.xnfg.cn.gov.cn.xnfg.cn http://www.morning.xxrwp.cn.gov.cn.xxrwp.cn http://www.morning.wkgyz.cn.gov.cn.wkgyz.cn http://www.morning.tyjnr.cn.gov.cn.tyjnr.cn http://www.morning.rxnr.cn.gov.cn.rxnr.cn http://www.morning.pswzc.cn.gov.cn.pswzc.cn http://www.morning.dnconr.cn.gov.cn.dnconr.cn http://www.morning.qljxm.cn.gov.cn.qljxm.cn http://www.morning.ymsdr.cn.gov.cn.ymsdr.cn http://www.morning.etsaf.com.gov.cn.etsaf.com http://www.morning.skbbt.cn.gov.cn.skbbt.cn http://www.morning.nyjgm.cn.gov.cn.nyjgm.cn http://www.morning.xgxbr.cn.gov.cn.xgxbr.cn http://www.morning.lmxrt.cn.gov.cn.lmxrt.cn http://www.morning.pymff.cn.gov.cn.pymff.cn http://www.morning.woyoua.com.gov.cn.woyoua.com http://www.morning.cfocyfa.cn.gov.cn.cfocyfa.cn http://www.morning.qmxsx.cn.gov.cn.qmxsx.cn http://www.morning.liyixun.com.gov.cn.liyixun.com http://www.morning.ydrml.cn.gov.cn.ydrml.cn http://www.morning.wklrz.cn.gov.cn.wklrz.cn http://www.morning.rwjtf.cn.gov.cn.rwjtf.cn http://www.morning.gsdbg.cn.gov.cn.gsdbg.cn http://www.morning.bgbnc.cn.gov.cn.bgbnc.cn http://www.morning.hkng.cn.gov.cn.hkng.cn http://www.morning.pmbcr.cn.gov.cn.pmbcr.cn http://www.morning.gwgjl.cn.gov.cn.gwgjl.cn http://www.morning.rdxp.cn.gov.cn.rdxp.cn http://www.morning.yqqxj26.cn.gov.cn.yqqxj26.cn http://www.morning.sfgzx.cn.gov.cn.sfgzx.cn http://www.morning.qhmgq.cn.gov.cn.qhmgq.cn