主要代码
Override public Response intercept(Chain chain) throws IOException {
RealInterceptorChain realChain = (RealInterceptorChain) chain;
Request request = realChain.request();
//从拦截器链获取StreamAllocation对象,这里的StreamAllocation对象是在第一个拦截器中
初始化完成的(设置了连接池、url路径等),真正使用的地方在这里。
StreamAllocation streamAllocation = realChain.streamAllocation();
boolean doExtensiveHealthChecks = !request.method().equals("GET");
//创建HttpCodec对象 HttpCodec:用来编码HTTP request和解码HTTP response
HttpCodec httpCodec = streamAllocation.newStream(client, doExtensiveHealthChecks);
//获取RealConnection
RealConnection connection = streamAllocation.connection();
//执行下一个拦截器,返回response
return realChain.proceed(request, streamAllocation, httpCodec, connection);
}
StreamAllocation.java
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();
boolean connectionRetryEnabled = client.retryOnConnectionFailure();
try {
RealConnection resultConnection = findHealthyConnection(connectTimeout, readTimeout,
writeTimeout, pingIntervalMillis, connectionRetryEnabled, doExtensiveHealthChecks);
HttpCodec resultCodec = resultConnection.newCodec(client, chain, this);
synchronized (connectionPool) {
codec = resultCodec;
return resultCodec;
}
} catch (IOException e) {
throw new RouteException(e);
}
}
StreamAllocation对象有两个关键角色
- RealConnection:真正建立socket连接的对象
- ConnectionPool:连接池,用来管理和复用socket连接
ConnectionPool.java
public ConnectionPool() {
//连接池最多保存5个连接的keep-alive,每个时长5分钟
this(5, 5, TimeUnit.MINUTES);
}
public ConnectionPool(int maxIdleConnections, long keepAliveDuration, TimeUnit timeUnit) {
this.maxIdleConnections = maxIdleConnections;
this.keepAliveDurationNs = timeUnit.toNanos(keepAliveDuration);
if (keepAliveDuration <= 0) {
throw new IllegalArgumentException("keepAliveDuration <= 0: " + keepAliveDuration);
}
}
...
void put(RealConnection connection) {
assert (Thread.holdsLock(this));
if (!cleanupRunning) {
cleanupRunning = true;
//清理无效的连接
executor.execute(cleanupRunnable);
}
connections.add(connection);
}
//获取可复用的连接
@Nullable RealConnection get(Address address, StreamAllocation streamAllocation, Route route) {
assert (Thread.holdsLock(this));
for (RealConnection connection : connections) {
//要拿到的连接与连接池中的连接,连接的配置(DNS、代理、SSL证书、服务器域名、端口等)一致,\
就可以复用
if (connection.isEligible(address, route)) {
streamAllocation.acquire(connection, true);
return connection;
}
}
return null;
}
long cleanup(long now) {
int inUseConnectionCount = 0;
int idleConnectionCount = 0;
RealConnection longestIdleConnection = null;
long longestIdleDurationNs = Long.MIN_VALUE;
synchronized (this) {
for (Iterator<RealConnection> i = connections.iterator(); i.hasNext(); ) {
RealConnection connection = i.next();
//检查连接是否正在使用
if (pruneAndGetAllocationCount(connection, now) > 0) {
inUseConnectionCount++;
continue;
}
//否则记录闲置连接数
idleConnectionCount++;
//计算这个连接已经闲置多久
long idleDurationNs = now - connection.idleAtNanos;
//找到闲置最久的连接
if (idleDurationNs > longestIdleDurationNs) {
longestIdleDurationNs = idleDurationNs;
longestIdleConnection = connection;
}
}
//keep-alive时间>=5分钟||连接池内闲置连接>5,立即移除
if (longestIdleDurationNs >= this.keepAliveDurationNs
|| idleConnectionCount > this.maxIdleConnections) {
connections.remove(longestIdleConnection);
} else if (idleConnectionCount > 0) {
//池内存在闲置连接,就等待 (保活时间-最长闲置时间,即到期时间)
return keepAliveDurationNs - longestIdleDurationNs;
} else if (inUseConnectionCount > 0) {
//有正在使用的连接,5分钟后再清理
return keepAliveDurationNs;
} else {
//无连接,停止清理(下次put会再次启动)
cleanupRunning = false;
return -1;
}
}
//关闭连接,返回时间0,立即再次进行请求(cleanupRunnable的while (true)会立即执行)
closeQuietly(longestIdleConnection.socket());
return 0;
}
RealConnection的创建和连接的建立
streamAllocation.newStream—>findHealthyConnection—>findConnection
private RealConnection findConnection(int connectTimeout, int readTimeout, int writeTimeout,
boolean connectionRetryEnabled) throws IOException {
Route selectedRoute;
synchronized (connectionPool) {
if (released) throw new IllegalStateException("released");
if (codec != null) throw new IllegalStateException("codec != null");
if (canceled) throw new IOException("Canceled");
//①StreamAllocation的connection如果可以复用则复用
RealConnection allocatedConnection = this.connection;
if (allocatedConnection != null && !allocatedConnection.noNewStreams) {
return allocatedConnection;
}
//②如果connection不能复用,则从连接池中获取RealConnection对象,获取成功则返回
Internal.instance.get(connectionPool, address, this, null);
if (connection != null) {
return connection;
}
selectedRoute = route;
}
if (selectedRoute == null) {
selectedRoute = routeSelector.next();
}
RealConnection result;
synchronized (connectionPool) {
if (canceled) throw new IOException("Canceled");
Internal.instance.get(connectionPool, address, this, selectedRoute);
if (connection != null) {
route = selectedRoute;
return connection;
}
route = selectedRoute;
refusedStreamCount = 0;
//③如果连接池里没有,则new一个RealConnection对象
result = new RealConnection(connectionPool, selectedRoute);
acquire(result);
}
// Do TCP + TLS handshakes. This is a blocking operation.
//④调用RealConnection的connect()方法发起请求
result.connect(connectTimeout, readTimeout, writeTimeout, connectionRetryEnabled);
routeDatabase().connected(result.route());
Socket socket = null;
synchronized (connectionPool) {
// ⑤将RealConnection对象存进连接池中,以便下次复用
Internal.instance.put(connectionPool, result);
if (result.isMultiplexed()) {
socket = Internal.instance.deduplicate(connectionPool, address, this);
result = connection;
}
}
closeQuietly(socket);
//⑥返回RealConnection对象
return result;
}
建立连接
public void connect(
int connectTimeout, int readTimeout, int writeTimeout, boolean connectionRetryEnabled) {
//...
//进行Socket连接
connectSocket(connectTimeout, readTimeout);
//...
}
private void connectSocket(int connectTimeout, int readTimeout) throws IOException {
Proxy proxy = route.proxy();
Address address = route.address();
rawSocket = proxy.type() == Proxy.Type.DIRECT || proxy.type() == Proxy.Type.HTTP
? address.socketFactory().createSocket()
: new Socket(proxy);
rawSocket.setSoTimeout(readTimeout);
try {
//建立socket连接 最终调用Java里的套接字Socket里的connect()方法。
Platform.get().connectSocket(rawSocket, route.socketAddress(), connectTimeout);
} catch (ConnectException e) {
ConnectException ce = new ConnectException("Failed to connect to " + route.socketAddress());
ce.initCause(e);
throw ce;
}
总结:
- ConnectInterceptor拦截器从拦截器链中获取StreamAllocation对象,这个对象在第一个拦截器中创建,在ConnectInterceptor中才用到。
- 执行StreamAllocation对象的newStream方法创建HttpCodec对象,用来编码HTTP request和解码HTTP response。
- newStream方法里面通过findConnection方法返回了一个RealConnection对象。
- StreamAllocation对象的connect方法拿到上面返回的RealConnection对象,这个RealConnection对象是用来进行实际的网络IO传输的。