前言
OkHttp同步或者异步都会通过getResponseWithInterceptorChain方法获取响应,此方法通过一系列的拦截器进行处理。这些拦截器分别有:RetryAndFollowUpInterceptor(重试或者重连)、BridgeInterceptor(request和response的转换)、 CacheInterceptor(缓存)、ConnectInterceptor(连接)、CallServerInterceptor(负责将请求数据写到输出流和从输入 流中获取响应数据
连接相关
- ConnectInterceptor#intercept:
public Response intercept(Chain chain) throws IOException {
RealInterceptorChain realChain = (RealInterceptorChain)chain;
Request request = realChain.request();
//1.通过Chain对象获取StreamAllocation
StreamAllocation streamAllocation = realChain.streamAllocation();
boolean doExtensiveHealthChecks = !request.method().equals("GET");
//2.通过StreamAllocation获取流对象(根据Http版本不同获取不同的HttpCodec)
HttpCodec httpCodec = streamAllocation.newStream(this.client, chain, doExtensiveHealthChecks);
//3.根据StreamAllocation获取连接
RealConnection connection = streamAllocation.connection();
return realChain.proceed(request, streamAllocation, httpCodec, connection);
}
ConnectInterceptor负责用StreamAllocation去创建连接和流对象,方便发起请求后面的使用。
- StreamAllocation
//StreamAllocation中的属性
public final Address address;//封装了URL地址
private Route route;
private final ConnectionPool connectionPool;//连接池
private final Object callStackTrace;
// State guarded by connectionPool.
private final RouteSelector routeSelector;
private int refusedStreamCount;
private RealConnection connection;//连接对象
private boolean released;
private boolean canceled;
private HttpCodec codec;//流对象
newStream方法:
public HttpCodec newStream(OkHttpClient client, Chain chain, boolean doExtensiveHealthChecks) {
......//一些列出参数,例如超时时间
try {
//寻找合适的连接
/*
1.从连接池中复用连接
2.选择一个合适的路径,并且在该路径上建立连接;然后将该连接加入到连接池
3.新建的连接发起连接,然后从路线黑名单移除掉该连接(因为该连接刚建立,是可用的)
*/
RealConnection resultConnection = this.findHealthyConnection(connectTimeout, readTimeout, writeTimeout, pingIntervalMillis, connectionRetryEnabled, doExtensiveHealthChecks);
//创建流对象
HttpCodec resultCodec = resultConnection.newCodec(client, chain, this);
ConnectionPool var11 = this.connectionPool;
synchronized(this.connectionPool) {
this.codec = resultCodec;
return resultCodec;
}
} catch (IOException var14) {
throw new RouteException(var14);
}
}
StreamAllocation通过寻找一个可用的连接,然后在连接上建立一个流,这个流根据不同的协议而不同(Http1Codec、Http2Codec等)。
- 连接(RealConnection)
RealConnection的成员
private static final String NPE_THROW_WITH_NULL = "throw with null exception";
private static final int MAX_TUNNEL_ATTEMPTS = 21;
private final ConnectionPool connectionPool;//连接池
private final Route route;
private Socket rawSocket;//基于Socket
private Socket socket;
private Handshake handshake;
private Protocol protocol;
private Http2Connection http2Connection;
private BufferedSource source;//输入流
private BufferedSink sink;//输出流
public boolean noNewStreams;//是否还可用标志
public int successCount;
public int allocationLimit = 1;//分配流的数量上限,默认为1,Http1.1版本
public final List<Reference<StreamAllocation>> allocations = new ArrayList();//维护着一个连接上有多少个流。
public long idleAtNanos = 9223372036854775807L;
...
connect方法:
public void connect(int connectTimeout, int readTimeout, int writeTimeout, int pingIntervalMillis, boolean connectionRetryEnabled, Call call, EventListener eventListener) {
......
//连接Socket
this.connectSocket(connectTimeout, readTimeout, call, eventListener);
......
//初始化协议
this.establishProtocol(connectionSpecSelector, pingIntervalMillis, call, eventListener);
......
}
connectSocket方法:
private void connectSocket(int connectTimeout, int readTimeout, Call call, EventListener eventListener) throws IOException {
Proxy proxy = this.route.proxy();
Address address = this.route.address();
//根据不同代理类型创建对应的Socket
this.rawSocket = proxy.type() != Type.DIRECT && proxy.type() != Type.HTTP ? new Socket(proxy) : address.socketFactory().createSocket();
eventListener.connectStart(call, this.route.socketAddress(), proxy);
this.rawSocket.setSoTimeout(readTimeout);
try {
//内部调用Socket的connect方法,建立Socket连接
Platform.get().connectSocket(this.rawSocket, this.route.socketAddress(), connectTimeout);
} catch (ConnectException var9) {
......
}
try {
//通过Socket初始化输入输出流
this.source = Okio.buffer(Okio.source(this.rawSocket));
this.sink = Okio.buffer(Okio.sink(this.rawSocket));
} catch (NullPointerException var10) {
......
}
}
此方法中根据代理和路由对象创建Socket对象,然后将Socket进行连接,连接后通过Socket初始化(封装)输入输出流。
- 连接池(ConnectionPool)
成员
public final class ConnectionPool {
private static final Executor executor;
private final int maxIdleConnections;//最大空闲数
private final long keepAliveDurationNs;//最长空闲时间
private final Runnable cleanupRunnable;//清除任务
private final Deque<RealConnection> connections;//连接队列
final RouteDatabase routeDatabase;//用于放置黑名单(不可用的路径)
boolean cleanupRunning;//清理标志
}
get方法:
@Nullable
RealConnection get(Address address, StreamAllocation streamAllocation, Route route) {
assert Thread.holdsLock(this);
Iterator var4 = this.connections.iterator();
RealConnection connection;
do {
if (!var4.hasNext()) {
return null;
}
connection = (RealConnection)var4.next();
//根据Address和Route来匹配寻找的Connection
} while(!connection.isEligible(address, route));
//将StreamAllocation存放到Connection的allocations中
streamAllocation.acquire(connection, true);
return connection;
}
通过访问资源地址、端口、协议、代理等进行匹配获取连接。然后将StreamAllocation添加进流列表中。
put方法:
void put(RealConnection connection) {
assert Thread.holdsLock(this);
if (!this.cleanupRunning) {
//如果没有清理任务,则出发清理任务
this.cleanupRunning = true;
executor.execute(this.cleanupRunnable);
}
//将连接添加到集合中去
this.connections.add(connection);
}
如果没有正在执行清理任务,则开始执行清理任务,然后将连接添加进连接列表中。
清理任务:
cleanupRunnable = new Runnable() {
public void run() {
//持续清理,需等待清理方法返回的时间
while(true) {
long waitNanos = ConnectionPool.this.cleanup(System.nanoTime());
if (waitNanos == -1L) {
return;
}
if (waitNanos > 0L) {
long waitMillis = waitNanos / 1000000L;
waitNanos -= waitMillis * 1000000L;
ConnectionPool var5 = ConnectionPool.this;
synchronized(ConnectionPool.this) {
try {
ConnectionPool.this.wait(waitMillis, (int)waitNanos);
} catch (InterruptedException var8) {
;
}
}
}
}
}
};
cleanup方法会统计空闲个数,如果空闲个数超过限制的空闲个数,或者某连接空闲时间超过最大的限制,则会清理闲置最长时间的连接。
CallServerInterceptor
发起请求于获取响应的拦截器。
public Response intercept(Chain chain) throws IOException {
RealInterceptorChain realChain = (RealInterceptorChain)chain;
HttpCodec httpCodec = realChain.httpStream();
StreamAllocation streamAllocation = realChain.streamAllocation();
RealConnection connection = (RealConnection)realChain.connection();
Request request = realChain.request();
long sentRequestMillis = System.currentTimeMillis();
realChain.eventListener().requestHeadersStart(realChain.call());
//1.写入请求头
httpCodec.writeRequestHeaders(request);
...
Builder responseBuilder = null;
if (HttpMethod.permitsRequestBody(request.method()) && request.body() != null) {
if ("100-continue".equalsIgnoreCase(request.header("Expect"))) {
//将请求头写到输出流去
httpCodec.flushRequest();
realChain.eventListener().responseHeadersStart(realChain.call());
//表示有请求体,所以里边会返回null
responseBuilder = httpCodec.readResponseHeaders(true);
}
if (responseBuilder == null) {
realChain.eventListener().requestBodyStart(realChain.call());
long contentLength = request.body().contentLength();
CallServerInterceptor.CountingSink requestBodyOut = new CallServerInterceptor.CountingSink(httpCodec.createRequestBody(request, contentLength));
BufferedSink bufferedRequestBody = Okio.buffer(requestBodyOut);
//2.将请求体写到输出流中
request.body().writeTo(bufferedRequestBody);
bufferedRequestBody.close();
realChain.eventListener().requestBodyEnd(realChain.call(), requestBodyOut.successfulCount);
} else if (!connection.isMultiplexed()) {
streamAllocation.noNewStreams();
}
}
//3.将请求写入到Socket的输出流
httpCodec.finishRequest();
if (responseBuilder == null) {
realChain.eventListener().responseHeadersStart(realChain.call());
responseBuilder = httpCodec.readResponseHeaders(false);
}
Response response = responseBuilder.request(request).handshake(streamAllocation.connection().handshake()).sentRequestAtMillis(sentRequestMillis).receivedResponseAtMillis(System.currentTimeMillis()).build();
int code = response.code();
if (code == 100) {
//4.获取响应头
responseBuilder = httpCodec.readResponseHeaders(false);
response = responseBuilder.request(request).handshake(streamAllocation.connection().handshake()).sentRequestAtMillis(sentRequestMillis).receivedResponseAtMillis(System.currentTimeMillis()).build();
code = response.code();
}
realChain.eventListener().responseHeadersEnd(realChain.call(), response);
if (this.forWebSocket && code == 101) {
response = response.newBuilder().body(Util.EMPTY_RESPONSE).build();
} else {
//5.从输入流中获取响应体
response = response.newBuilder().body(httpCodec.openResponseBody(response)).build();
}
if ("close".equalsIgnoreCase(response.request().header("Connection")) || "close".equalsIgnoreCase(response.header("Connection"))) {
streamAllocation.noNewStreams();
}
//没有响应体
if ((code == 204 || code == 205) && response.body().contentLength() > 0L) {
throw new ProtocolException("HTTP " + code + " had non-zero Content-Length: " + response.body().contentLength());
} else {
return response;
}
}
- 写入请求头,然后根据请求头是否有Expect字段,如果有,则表示询问服务端是否接受请求体。
- 构建请求体,然后将请求体写入到输出流中。
- 将请求写入到Socket的输出流中。
- 获取响应头。
- 获取响应体,判断某些特殊的响应码。
RetryAndFollowUpInterceptor
public Response intercept(Chain chain) throws IOException {
......
while(!this.canceled) {
boolean releaseConnection = true;
Response response;
try {
//1. 开始进行请求
response = realChain.proceed(request, streamAllocation, (HttpCodec)null, (RealConnection)null);
releaseConnection = false;
} catch (RouteException var19) {
//1.1 重试
if (!this.recover(var19.getLastConnectException(), streamAllocation, false, request)) {
throw var19.getFirstConnectException();
}
releaseConnection = false;
continue;
} catch (IOException var20) {
boolean requestSendStarted = !(var20 instanceof ConnectionShutdownException);
//1.2 重试
if (!this.recover(var20, streamAllocation, requestSendStarted, request)) {
throw var20;
}
releaseConnection = false;
continue;
} finally {
if (releaseConnection) {
streamAllocation.streamFailed((IOException)null);
streamAllocation.release();
}
}
......
Request followUp;
try {
//2. 判断是否重定向
followUp = this.followUpRequest(response, streamAllocation.route());
} catch (IOException var18) {
streamAllocation.release();
throw var18;
}
if (followUp == null) {
if (!this.forWebSocket) {
streamAllocation.release();
}
return response;
}
......
if (!this.sameConnection(response, followUp.url())) {
//3. 需要重定向,则释放原来的StreamAllcation,并根据新的路径重新创建一个新的
streamAllocation.release();
streamAllocation = new StreamAllocation(this.client.connectionPool(), this.createAddress(followUp.url()), call, eventListener, this.callStackTrace);
this.streamAllocation = streamAllocation;
}
......
}
- 重试,如果没有取消,则执行网络请求,请求过程如果抛出异常,会调用recover进行重试;如果网络请求正确,则会进行下面的重定向。
- 重定向,根据Response判断是否需要重定向,如果需要,则判断主机路径端口等是否相同,如果不同则释放先前的StreamAllocation然后重新创建一个。
以上便是OkHttp之中的其三个拦截器学习。
本文深入探讨了OkHttp的拦截器机制,详细介绍了RetryAndFollowUpInterceptor、ConnectInterceptor和CallServerInterceptor的工作原理。RetryAndFollowUpInterceptor负责重试和重定向,ConnectInterceptor创建连接和流对象,而CallServerInterceptor则处理请求与响应的交换。连接池的管理,包括获取和清理连接,确保高效使用。通过对这些拦截器的学习,可以更深入理解OkHttp的网络请求流程。
1376

被折叠的 条评论
为什么被折叠?



