ConnectInterceptor:
ConnectIntercepter 拦截器的代码比较简单,实际上它是调用了其它实现类,连接拦截器通过 输入输出流进行网络连接
通过 streamAllocation.newStream 创建一个httpCodec:
@Override
public Response intercept(Chain chain) throws IOException {
RealInterceptorChain realChain = (RealInterceptorChain) chain;
Request request = realChain.request();
StreamAllocation streamAllocation = realChain.streamAllocation();
// We need the network to satisfy this request. Possibly for validating a conditional GET.
boolean doExtensiveHealthChecks = !request.method().equals("GET");
HttpCodec httpCodec = streamAllocation.newStream(client, chain, doExtensiveHealthChecks);
RealConnection connection = streamAllocation.connection();
return realChain.proceed(request, streamAllocation, httpCodec, connection);
}
StreamAllocation:这个类协调Connections,Streams(逻辑HTTP请求/响应对是分层的连接。每个连接都有自己的分配限制,它定义了多少连接可以携带的并发流。HTTP / 1.x 连接可以携带1个流一次,HTTP/2通常携带多个),Calls 三个实体之间的关系:
httpCodec:编码HTTP请求和解码HTTP响应。该类包含了 输出流的创建,HTTP引擎的sentRequestMillis字段的更新,将请求刷新到基础套接字,从HTTP传输中解析响应头的字节,响应流的处理
new Stream() 处理流程:
findHealthyConnection -> findConnection----> releaseIfNoNewStreams ---->deallocate ----> release -->connectionBecameIdle->
new Stream 调用了 findHealthyConnection -> findConnection 查找一个健康的连接:
toClose = releaseIfNoNewStreams();
这里判断持有的RealConnction 对象是否可以创建一个新的传输流,如果不能创建新的流进行释放:
private Socket releaseIfNoNewStreams() {
assert (Thread.holdsLock(connectionPool));
RealConnection allocatedConnection = this.connection;
if (allocatedConnection != null && allocatedConnection.noNewStreams) {
return deallocate(false, false, true);
}
return null;
}
在releaseIfNoNewStreams 方法中调用了 deallocate 方法,该方法是对连接进行释放 移除链接池:
private Socket deallocate(boolean noNewStreams, boolean released, boolean streamFinished) {
assert (Thread.holdsLock(connectionPool));
if (streamFinished) {
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)) {
release(connection); // todo 释放连接
if (connection.allocations.isEmpty()) {
connection.idleAtNanos = System.nanoTime();
//链接用完了重新变为闲置
if (Internal.instance.connectionBecameIdle(connectionPool, connection)) {
socket = connection.socket();
}
}
connection = null;
}
}
return socket;
}
connectionBecameIdle : 连接用完了重新变为闲置
/**
* todo 连接用完了,重新变为闲置
* Notify this pool that {@code connection} has become idle. Returns true if the connection has
* been removed from the pool and should be closed.
*/
boolean connectionBecameIdle(RealConnection connection) {
assert (Thread.holdsLock(this));
//todo 比如 服务器返回 Connection: close ,那就会把这个连接关掉 (noNewStreams 设置为true)
if (connection.noNewStreams || maxIdleConnections == 0) {
connections.remove(connection);
return true;
} else {
//todo 唤醒wait的清理任务 开始工作
notifyAll(); // Awake the cleanup thread: we may have exceeded the idle connection
// limit.
return false;
}
}
当连接被重置为闲置状态时 随后关闭该连接 这里有个前提条件就是 connection.noNewStream 为true 是 否则 不进行连接关闭进行返回
如果连接 connection 实例对象为null 时 分为两种情况:
1、查找连接池 是否有可复用的连接对象:
//todo 根据代理和不同的ip从连接池中找可复用的连接
List<Route> 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;
}
}
2、新建一个RealConnection 连接对象 新建完成后放入连接池中以供复用
//todo 还是没找到,必须新建一个连接了
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;
//创建一个新的连接对象
result = new RealConnection(connectionPool, selectedRoute);
acquire(result, false);
}
....
synchronized (connectionPool) {
reportedAcquired = true;
// Pool the connection.
//todo 将新创建的连接放到连接池中
Internal.instance.put(connectionPool, result);
// If another multiplexed connection to the same address was created concurrently, then
// release this connection and acquire that one.
if (result.isMultiplexed()) { //todo 是否是多路复用
socket = Internal.instance.deduplicate(connectionPool, address, this);
result = connection;
}
}
最后调用
streamAllocation.connection(); //connection 方法仅仅返回了一个连接对象