源码分析基于 3.14.4
ConnectInterceptor作用
主要负责建立或者复用Socket连接;
//ConnectInterceptor类中
@Override public Response intercept(Chain chain) throws IOException {
......
Exchange exchange = transmitter.newExchange(chain, doExtensiveHealthChecks);//1
return realChain.proceed(request, transmitter, exchange);//2
}
- 注释1:主要逻辑都在transmitter.newExchange里面;
- 注释2:调用下一个拦截器;
//Transmitter类中
Exchange newExchange(Interceptor.Chain chain, boolean doExtensiveHealthChecks) {
......
ExchangeCodec codec = exchangeFinder.find(client, chain, doExtensiveHealthChecks);//调用下面的函数,查找一个可以使用的Connection
......
}
//ExchangeFinder类中
public ExchangeCodec find(
......
RealConnection resultConnection = findHealthyConnection(connectTimeout, readTimeout,
writeTimeout, pingIntervalMillis, connectionRetryEnabled, doExtensiveHealthChecks);//最终会调到ExchangeFinder.findConnection
......
}
//ExchangeFinder类中
private RealConnection findConnection(int connectTimeout, int readTimeout, int writeTimeout,
int pingIntervalMillis, boolean connectionRetryEnabled) throws IOException {
.......
if (connectionPool.transmitterAcquirePooledConnection(address, transmitter, null, false)) {//1
foundPooledConnection = true;
result = transmitter.connection;
}
.......
if (result != null) {//找到可以复用的连接,则返回
return result;
}
......
result = new RealConnection(connectionPool, selectedRoute);//2
connectingConnection = result;
......
result.connect(connectTimeout, readTimeout, writeTimeout, pingIntervalMillis,
connectionRetryEnabled, call, eventListener);//3
......
return result;
}
- 注释1: 从连接池里查找可复用的Connection,查找条件是:HTTP版本、支持加密版本、TLS版本、代理、SSL工厂、端口、Host等,找到了直接返回;
- 连接池默认保存5个空闲连接,超时时间5分钟,如果5分钟内没有任何连接,则断开连接;
- 注释2:如果没有找到,则构建一个RealConnection;
- 注释3:result.connect,建立TCP+TLS连接;内部调用RealConnection.connectSocket建立Socket连接,也就是TCP;内部调用RealConnection.establishProtocol建立TLS连接,也就是HTTPS;
RealConnection.connect建立连接
//RealConnection类中
public void connect(int connectTimeout, int readTimeout, int writeTimeout,
int pingIntervalMillis, boolean connectionRetryEnabled, Call call,
EventListener eventListener) {
.......
while (true) {
try {
if (route.requiresTunnel()) {//1
connectTunnel(connectTimeout, readTimeout, writeTimeout, call, eventListener);
......
} else {//2
connectSocket(connectTimeout, readTimeout, call, eventListener);
}
......
}
- 注释1:表示需要用到隧道,通常是在Http代理连接上发送https请求;
//普通连接的请求行
GET /user HTTP/1.1
//使用HTTP代理发送HTTPS请求
GET CONNECT www.baidu.com HTTP/1.1
- 如果使用http代理发送https请求,建立Socket连接以后发出CONNECT ,成功了就表示可以正常发送请求,后续的请求行就跟普通的一样;
- 注释2:如果不使用隧道则connectSocket,建立Socket连接;
RealConnection.connectSocket建立Socket连接
private void connectSocket(int connectTimeout, int readTimeout, Call call,
EventListener eventListener) throws IOException {
Proxy proxy = route.proxy();
Address address = route.address();
rawSocket = proxy.type() == Proxy.Type.DIRECT || proxy.type() == Proxy.Type.HTTP
? address.socketFactory().createSocket()//1
: new Socket(proxy);//2
......
Platform.get().connectSocket(rawSocket, route.socketAddress(),
connectTimeout);//3
......
}
- 注释1:创建普通连接;
- 注释2:创建代理的连接new Socket(proxy);
- 注释3:建立Socket连接;
连接池
public final class ConnectionPool {
final RealConnectionPool delegate;
public ConnectionPool() {
this(5, 5, TimeUnit.MINUTES);//1
}
public ConnectionPool(int maxIdleConnections, long keepAliveDuration, TimeUnit timeUnit) {
this.delegate = new RealConnectionPool(maxIdleConnections, keepAliveDuration, timeUnit);
}
- 空闲连接数大于5个或空闲时长大于5分钟,则清理;
1.缓存连接
//RealConnectionPool类中
void put(RealConnection connection) {
assert (Thread.holdsLock(this));
if (!cleanupRunning) {
cleanupRunning = true;
executor.execute(cleanupRunnable);//1
}
connections.add(connection);//2
}
- 注释1:启动cleanupRunnable线程开始清理空闲连接;
- 注释2:缓存连接;
- 清理空闲连接
//RealConnectionPool类中
private final Runnable cleanupRunnable = () -> {
while (true) {
long waitNanos = cleanup(System.nanoTime());//调用下面的cleanup函数
if (waitNanos == -1) return;//7
if (waitNanos > 0) {
long waitMillis = waitNanos / 1000000L;
waitNanos -= (waitMillis * 1000000L);
synchronized (RealConnectionPool.this) {
try {
RealConnectionPool.this.wait(waitMillis, (int) waitNanos);//8
} catch (InterruptedException ignored) {
}
}
}
}
};
//RealConnectionPool类中
long cleanup(long now) {
synchronized (this) {
for (Iterator<RealConnection> i = connections.iterator(); i.hasNext(); ) {//1
RealConnection connection = i.next();
if (pruneAndGetAllocationCount(connection, now) > 0) {//判断连接是否使用,每个连接都有一个弱引用计数,当计数大于0,则表示在使用中
inUseConnectionCount++;
continue;
}
idleConnectionCount++;//空闲连接计数
long idleDurationNs = now - connection.idleAtNanos;//计算空闲了多久
if (idleDurationNs > longestIdleDurationNs) {//记录空闲最久的连接
longestIdleDurationNs = idleDurationNs;
longestIdleConnection = connection;
}
}
if (longestIdleDurationNs >= this.keepAliveDurationNs
|| idleConnectionCount > this.maxIdleConnections) {//2
connections.remove(longestIdleConnection);
} else if (idleConnectionCount > 0) {//3
return keepAliveDurationNs - longestIdleDurationNs;
} else if (inUseConnectionCount > 0) {//4
return keepAliveDurationNs;
} else {
cleanupRunning = false;
return -1;//5
}
}
```
closeQuietly(longestIdleConnection.socket());//6
return 0;//9
}
- 注释1:遍历的目的,统计空闲连接数以及查找空闲最长的连接;
- 注释2:当空闲连接闲置时间等于5分钟,则从集合移除;当空闲连接数大于5个,也从集合移除;
- 注释3:当有空闲连接且注释2两个条件不满足,则返回等待时间(keepAliveDurationNs - longestIdleDurationNs),再开始清理,对应注释8;
- 注释4:当没有空闲连接且注释2两个条件不满足,则返回等待时间5分钟,再开始清理;
- 注释5:返回-1,表示即没有使用也没有空闲的,退出清理,对应注释7;
- 注释6:关闭从集合移除的连接;
- 注释9:返回0,不wait,继续查找;
总结
- ConnectInterceptor连接拦截器,主要负责建立Socket连接,其实Okttp本身用的就是Socket;
- 如果连接池中有可以使用的,则复用,复用的条件是:HTTP版本、支持加密版本、TLS版本、代理、SSL工厂、端口、Host等;
- 连接池清理由cleanupRunnable 任务实现;
以上分析有不对的地方,请指出,互相学习,谢谢哦!
网友评论