当前位置: 首页 > news >正文

深圳建设合同备案 网站优化官网咨询

深圳建设合同备案 网站,优化官网咨询,php创建一个网站,软件开发工资一般多少大专游戏SDK架构设计之代码实现——网络框架 OKHttp 源码解析(一) OKHttp 源码解析(二)拦截器 前言 上一篇解读了OKHttp 的基本框架源码,其中 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.List<Interceptor> 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 允许开发者自定义拦截器并优先执行开发者定义的拦截器。具体做法如下

  1. 定义拦截器实现 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();}
    }
    
  2. 通过OkHttpClientaddInterceptor 接口添加到拦截器列表里

    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 {// We're 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+ " didn't 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);// Don't follow redirects to unsupported protocols.if (url == null) return null;// If configured, don't 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 don't 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:// 408's 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;}}

重试重定向拦截器总结:

  1. 开始一个无限循环,调用之后的拦截器获取响应结果response = ((RealInterceptorChain) chain).proceed(request, streamAllocation, null, null);
  2. 请求或链接过程中抛出异常,在异常内通过 recover 方法判断是否需要重试。否则返回priorResponse (链接之前的 response)。
  3. 根据响应码判断是否需要重定向 followUpRequest
  4. 不需要重定向结束请求,返回响应,释放资源,需要重定向执行有限次数重定向,超过一定次数抛出异常。

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 we're responsible for also decompressing// the transfer stream.boolean transparentGzip = false;if (userRequest.header("Accept-Encoding") == null) {transparentGzip = true;requestBuilder.header("Accept-Encoding", "gzip");}// 获取 客户端的cookie信息List<Cookie> 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桥连接拦截器总结:

  1. 添加请求头,将用户请求转换为能够进行网络访问的请求。
  2. 执行拦截器链的下一个拦截器方法。
  3. 获取响应,将响应转成应用可识别的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 wasn't applicable. Close it.}// If we're 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 don't need the network, we're done.
// 网络请求是空的,返回响应if (networkRequest == null) {return cacheResponse.newBuilder().cacheResponse(stripBody(cacheResponse)).build();}Response networkResponse = null;try {
// 执行请求networkResponse = chain.proceed(networkRequest);} finally {// If we're crashing on I/O or otherwise, don't leak the cache body.if (networkResponse == null && cacheCandidate != null) {closeQuietly(cacheCandidate.body());}}// If we have a cache response too, then we're 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;}}
  1. 判断本地是否有缓存;
  2. 根据

缓存策略对象

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()) {// We're 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 it's missing a required handshake.
// 缺少需要的握手,删除缓存的响应if (request.isHttps() && cacheResponse.handshake() == null) {return new CacheStrategy(request, null);}// If this response shouldn't 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// isn't, 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);}

连接拦截器:

  1. 根据地址、证书、DNS 、最大限制流、超空闲时间等条件确定一个连接是否能复用。
  2. 创建一个新的连接。
http://www.dinnco.com/news/86277.html

相关文章:

  • 软件最全网站百度扫一扫网页版
  • 淘宝官方网站登录注册今日刚刚发生的新闻
  • 嘉兴做网站设计aso优化{ }贴吧
  • 南宁广告网页设计招聘信息合肥网站优化
  • 做网站需要域名和什么今日头条重大消息
  • 宁波建设银行网站首页如何优化网站首页
  • 装饰行业网站模板seo权威入门教程
  • 抖音广告推广产品seo标题是什么
  • wdcp网站无法访问东莞seo推广
  • 做网站 报价 需要了解中国国家人事人才培训网
  • 宁波城乡建设委员会的网站seo 重庆
  • 福州网站建设 联系yanktcn 04信息服务平台有哪些
  • 郑州汉狮做网站报价qq引流推广软件免费
  • 空间数据云网站太原做推广营销
  • 建设公司网站建设报价怎么优化
  • 哪些公司做app开发东莞seo推广机构帖子
  • 网站主页用ps做seo工资
  • 怎样才能做自己的网站博客推广工具
  • 网站 动画 怎么做的中关村标准化协会
  • 蓝莓直播视频东莞网络优化哪家公司好
  • 诚信通网站怎么做广告推广费用
  • 甘肃做网站搜索引擎优化seo优惠
  • php做网站怎么布局上海百度推广公司排名
  • 橱柜衣柜做网站朝阳区seo技术
  • 公司建设网站记什么费用企点qq官网
  • 可以提供排版的网站免费推广网站大全下载安装
  • 太原哪里做网站好精准拓客软件哪个好
  • 怎么看出网站是dede做的武汉网站排名推广
  • asp网站开发全程视频百度快照是什么意思
  • 做视频官方网站网站制作报价表