Response getResponseWithInterceptorChain() throws IOException {
// 构建一整套拦截器
List interceptors = new ArrayList<>();
interceptors.addAll(client.interceptors());//1
interceptors.add(retryAndFollowUpInterceptor);//2
interceptors.add(new BridgeInterceptor(client.cookieJar()));//3
interceptors.add(new CacheInterceptor(client.internalCache()));//4
interceptors.add(new ConnectInterceptor(client));//5
//构建一个RealCall的时候我们传入的forWebSocket是false
if (!forWebSocket) {
interceptors.addAll(client.networkInterceptors());//6
}
interceptors.add(new CallServerInterceptor(forWebSocket));//7
//构建拦截器链
Interceptor.Chain chain = new RealInterceptorChain(interceptors, null, null,
null, 0,originalRequest, this, eventListener, client.connectTimeoutMillis(),
client.readTimeoutMillis(), client.writeTimeoutMillis());
//拦截器链处理请求
return chain.proceed(originalRequest);
}
CacheInterceptor(缓存拦截器)的intercept
方法中调用了RealInterceptorChain的proceed
方法,内部会调用ConnectInterceptor的intercept
方法。
ConnectInterceptor的intercept
方法。
public final class ConnectInterceptor implements Interceptor {
public final OkHttpClient client;
public ConnectInterceptor(OkHttpClient client) {
this.client = client;
}
@Override
public Response intercept(Chain chain) throws IOException {
RealInterceptorChain realChain = (RealInterceptorChain) chain;
Request request = realChain.request();
//注释1处,获取StreamAllocation
StreamAllocation streamAllocation = realChain.streamAllocation();
// We need the network to satisfy this request. Possibly for validating a conditional GET.
boolean doExtensiveHealthChecks = !request.method().equals("GET");
//注释2处,获取HttpCodec对象
HttpCodec httpCodec = streamAllocation.newStream(client, chain, doExtensiveHealthChecks);
//注释3处,获取RealConnection对象
RealConnection connection = streamAllocation.connection();
//交给下一个拦截器处理
return realChain.proceed(request, streamAllocation, httpCodec, connection);
}
}
注释1处,获取StreamAllocation,我们在RetryAndFollowUpInterceptor的intercept
方法中创建了StreamAllocation实例。
注释2处,获取HttpCodec对象,StreamAllocation的newStream方法。
public HttpCodec newStream(
OkHttpClient client, Interceptor.Chain chain, boolean doExtensiveHealthChecks) {
int connectTimeout = chain.connectTimeoutMillis();
int readTimeout = chain.readTimeoutMillis();
int writeTimeout = chain.writeTimeoutMillis();
int pingIntervalMillis = client.pingIntervalMillis();
//默认是true
boolean connectionRetryEnabled = client.retryOnConnectionFailure();
try {
//注释1处,获取健康的连接
RealConnection resultConnection = findHealthyConnection(connectTimeout, readTimeout,
writeTimeout, pingIntervalMillis, connectionRetryEnabled, doExtensiveHealthChecks);
//注释2处,返回HttpCodec对象。
HttpCodec resultCodec = resultConnection.newCodec(client, chain, this);
synchronized (connectionPool) {
//保存生成的HttpCodec对象
codec = resultCodec;
return resultCodec;
}
} catch (IOException e) {
throw new RouteException(e);
}
}
注释1处,获取健康的连接。
private RealConnection findHealthyConnection(int connectTimeout, int readTimeout,
int writeTimeout, int pingIntervalMillis, boolean connectionRetryEnabled,
boolean doExtensiveHealthChecks) throws IOException {
//死循环,直到找到一个连接
while (true) {
//注释1处,调用findConnection方法。
RealConnection candidate = findConnection(connectTimeout, readTimeout, writeTimeout,
pingIntervalMillis, connectionRetryEnabled);
// 如果这是一个全新的连接,跳过额外的健康检查。
synchronized (connectionPool) {
if (candidate.successCount == 0) {
return candidate;
}
}
//执行(可能较慢)检查以确认缓存的连接仍然良好。如果不是,把它从缓存中移除,然后重新while循环,获取健康的连接。
if (!candidate.isHealthy(doExtensiveHealthChecks)) {
noNewStreams();
continue;
}
return candidate;
}
}
findHealthyConnection方法的注释1处,StreamAllocation的findConnection方法。
/**
* 返回一个连接来承载新的stream。这会优先选择现有连接(如果存在),然后是缓存池里的连接,最后构建一个新连接。
*/
private RealConnection findConnection(int connectTimeout, int readTimeout, int writeTimeout,
int pingIntervalMillis, boolean connectionRetryEnabled) throws IOException {
boolean foundPooledConnection = false;
//要返回的连接
RealConnection result = null;
Route selectedRoute = null;
//要释放的连接
Connection releasedConnection;
//要关闭的socket
Socket toClose;
synchronized (connectionPool) {
if (released) throw new IllegalStateException("released");
if (codec != null) throw new IllegalStateException("codec != null");
if (canceled) throw new IOException("Canceled");
//先尝试使用一个已分配的连接。这里我们需要小心一点,
//因为我们已分配的连接可能已经禁止创建新的stream了。
releasedConnection = this.connection;
//注释1处,看看是否要释放当前的连接。如果要释放的话,toClose不为null,connection为null。
toClose = releaseIfNoNewStreams();
if (this.connection != null) {
//注释2处,connection不为null,说明我们有一个已分配的连接,并且是健康可用的。
result = this.connection;
releasedConnection = null;
}
if (!reportedAcquired) {
// If the connection was never reported acquired, don't report it as released!
releasedConnection = null;
}
if (result == null) {
// 注释3处,connection为null,则尝试从连接池里获取连接。如果从连接池获取到了连接,connection不为null
Internal.instance.get(connectionPool, address, this, null);
if (connection != null) {
foundPooledConnection = true;
//连接池里获取的连接
result = connection;
} else {
selectedRoute = route;
}
}
}
//关闭socket
closeQuietly(toClose);
if (releasedConnection != null) {
eventListener.connectionReleased(call, releasedConnection);
}
if (foundPooledConnection) {
eventListener.connectionAcquired(call, result);
}
if (result != null) {
//注释4处,返回已分配的连接或者从连接池中获取的连接。
return result;
}
// If we need a route selection, make one. This is a blocking operation.
boolean newRouteSelection = false;
if (selectedRoute == null && (routeSelection == null || !routeSelection.hasNext())) {
newRouteSelection = true;
routeSelection = routeSelector.next();
}
synchronized (connectionPool) {
if (canceled) throw new IOException("Canceled");
if (newRouteSelection) {
// 注释5处,如果有多个ip地址,更换路由,继续从连接池里查找。这个不是太了解。。。Now that we have a set of IP addresses, make another attempt at getting a connection from
// the pool. This could match due to connection coalescing.
List routes = routeSelection.getAll();
for (int i = 0, size = routes.size(); i < size; i++) {
Route route = routes.get(i);
Internal.instance.get(connectionPool, address, this, route);
if (connection != null) {
foundPooledConnection = true;
result = connection;
this.route = route;
break;
}
}
}
if (!foundPooledConnection) {
if (selectedRoute == null) {
selectedRoute = routeSelection.next();
}
// Create a connection and assign it to this allocation immediately. This makes it possible
// for an asynchronous cancel() to interrupt the handshake we're about to do.
route = selectedRoute;
refusedStreamCount = 0;
//注释6处,最终没有找到的话,新建一个连接,并在acquire方法中,会把创建的连接赋值给connection
result = new RealConnection(connectionPool, selectedRoute);
acquire(result, false);
}
}
// If we found a pooled connection on the 2nd time around, we're done.
if (foundPooledConnection) {
eventListener.connectionAcquired(call, result);
return result;
}
//注释7处,进行 TCP + TLS 握手。这是一个阻塞操作。
result.connect(connectTimeout, readTimeout, writeTimeout, pingIntervalMillis,
connectionRetryEnabled, call, eventListener);
routeDatabase().connected(result.route());
Socket socket = null;
synchronized (connectionPool) {
reportedAcquired = true;
//注释8处,将连接放进连接池。
Internal.instance.put(connectionPool, result);
//注释9处,如果同时创建了另一个到同一地址的多路复用连接,则释放当前连接并获取另外创建的连接。
if (result.isMultiplexed()) {
socket = Internal.instance.deduplicate(connectionPool, address, this);
result = connection;
}
}
closeQuietly(socket);
eventListener.connectionAcquired(call, result);
//注释10处,最后返回结果
return result;
}
注释1处,看看是否要释放当前的连接connection
。如果要释放的话,toClose不为null,StreamAllocation的成员变量connection
为null。
/**
*如果当前连接禁止创建新的stream,则释放当前持有的连接并返回一个需要被关闭的socket。对于`HTTP/2`来说的,多个请求共享一个连接,所以在重定向请求的时候,我们的连接禁止创建新的stream。
*/
private Socket releaseIfNoNewStreams() {
assert (Thread.holdsLock(connectionPool));
RealConnection allocatedConnection = this.connection;
//noNewStreams为true,表示该连接不能创建新的Stream了,就释放该连接。
if (allocatedConnection != null && allocatedConnection.noNewStreams) {
return deallocate(false, false, true);
}
return null;
}
StreamAllocation的deallocate方法。
/**
* 释放当前StreamAllocation持有的资源。如果分配了足够的资源,将断开或关闭连接。调用者必须持有连接池的锁。
* 返回一个Closeable对象,调用者应该在同步代码块结束的时候传递给 {@link Util#closeQuietly} 方法。
*/
private Socket deallocate(boolean noNewStreams, boolean released, boolean streamFinished) {
assert (Thread.holdsLock(connectionPool));
if (streamFinished) {
//将codec置为null
this.codec = null;
}
if (released) {
this.released = true;
}
Socket socket = null;
if (connection != null) {
if (noNewStreams) {
connection.noNewStreams = true;
}
if (this.codec == null && (this.released || connection.noNewStreams)) {
//注释1处,释放连接,会将connection置为null,并关闭socket连接。
release(connection);
if (connection.allocations.isEmpty()) {
connection.idleAtNanos = System.nanoTime();
if (Internal.instance.connectionBecameIdle(connectionPool, connection)) {
//连接从连接池里被移除了,并且应该被关闭。返回该连接的socket,调用者会关闭这个socket
socket = connection.socket();
}
}
//注意这里将connection置为null
connection = null;
}
}
return socket;
}
我们回到StreamAllocation的findConnection方法的注释2处,connection不为null,说明我们有一个已分配的连接,并且是健康可用的。
注释3处,connection为null,则尝试从连接池里获取连接。最终是调用ConnectionPool的get方法来获取的。
@Nullable
RealConnection get(Address address, StreamAllocation streamAllocation, Route route) {
assert (Thread.holdsLock(this));
for (RealConnection connection : connections) {
//注释1处,如果存在合适的连接,就返回。调用StreamAllocation注意这里会给StreamAllocation的connection赋值。
if (connection.isEligible(address, route)) {
streamAllocation.acquire(connection, true);
return connection;
}
}
return null;
}
StreamAllocation的acquire方法。
public void acquire(RealConnection connection, boolean reportedAcquired) {
assert (Thread.holdsLock(connectionPool));
if (this.connection != null) throw new IllegalStateException();
//给connection赋值
this.connection = connection;
this.reportedAcquired = reportedAcquired;
connection.allocations.add(new StreamAllocation.StreamAllocationReference(this, callStackTrace));
}
我们回到StreamAllocation的findConnection方法的注释4处,返回已分配的连接(就是StreamAllocation 的 connection成员变量)或者从连接池中获取的连接。
注释5处,如果有多个ip地址,更换路由,继续从连接池里查找。这个不是太了解。。。暂时略过。
注释6处,最终没有找到的话,新建一个连接,并在acquire方法中,会把创建的连接赋值给connection。
注释7处,进行 TCP + TLS 握手。这是一个阻塞操作。 这里就是发生三次握手的地方。(八股文:能说一下三次握手和四次挥手的原理吗,哈哈。)
注释8处,将连接放进连接池ConnectionPool。
注释9处,如果同时创建了另一个到同一地址的多路复用连接,则释放当前连接并获取另外创建的连接。
注释10处,最后返回结果。
我们回到StreamAllocation的newStream方法的注释2处,返回HttpCodec对象。
ConnectInterceptor的intercept
方法的注释3处,获取StreamAllocation的connection对象交给下一个拦截器进行处理。