本篇将解析ConnectionPool连接池类,此类的主要目的是解决TCP的3次握手与4次挥手的效率问题。
tcp的3次握手与4次挥手又有什么样的问题,如何去解决呢?
•导致问题:3次握手与4次挥手的效率问题:如果有大量的链接,每次链接与关闭时都要经历3次握手与4次挥手,那么很显然会造成性能低下的问题。
•解决方案:HTTP中有一种keepalive connection的机制,它可以在传输数据后任保持链接,当客户端需要再次获取数据时,直接使用刚刚空闲下来的链接而无需再次握手。
(一)ConnectionPool类:
public final class ConnectionPool {
//类似与缓存型的线程池
private static final Executor executor = new ThreadPoolExecutor(0 /* corePoolSize */,
Integer.MAX_VALUE /* maximumPoolSize */, 60L /* keepAliveTime */, TimeUnit.SECONDS,
new SynchronousQueue<Runnable>(), Util.threadFactory(“OkHttp ConnectionPool”, true));
/** The maximum number of idle connections for each address. */
private final int maxIdleConnections; //空闲socket的最大连接数
private final long keepAliveDurationNs; //socket的keepAlive时间
private final Runnable cleanupRunnable = new Runnable() {
@Override public void run() {
while (true) {
long waitNanos = cleanup(System.nanoTime());
if (waitNanos == -1) return;
if (waitNanos > 0) {
long waitMillis = waitNanos / 1000000L;
waitNanos -= (waitMillis * 1000000L);
synchronized (ConnectionPool.this) {
try {
ConnectionPool.this.wait(waitMillis, (int) waitNanos);
} catch (InterruptedException ignored) {
}
}
}
}
}
};
// Deque双向队列,双端队列同时具有队列和栈的性质,经常在缓存中被使用 --》核心
private final Deque<RealConnection> connections = new ArrayDeque<>();
final RouteDatabase routeDatabase = new RouteDatabase();
boolean cleanupRunning;
/**
* Create a new connection pool with tuning parameters appropriate for a single-user application.
* The tuning parameters in this pool are subject to change in future OkHttp releases. Currently
* this pool holds up to 5 idle connections which will be evicted after 5 minutes of inactivity.
*/
public ConnectionPool() {
this(5, 5, TimeUnit.MINUTES); //默认的空闲socket最大连接数为5,socket的keepAlive时间的5分钟
}
public ConnectionPool(int maxIdleConnections, long keepAliveDuration, TimeUnit timeUnit) {
this.maxIdleConnections = maxIdleConnections;
this.keepAliveDurationNs = timeUnit.toNanos(keepAliveDuration);
// Put a floor on the keep alive duration, otherwise cleanup will spin loop.
if (keepAliveDuration <= 0) {
throw new IllegalArgumentException("keepAliveDuration <= 0: " + keepAliveDuration);
}
}
………..
/**
* Returns a recycled connection to {@code address}, or null if no such connection exists. The
* route is null if the address has not yet been routed.
*/
//获取链接
@Nullable RealConnection get(Address address, StreamAllocation streamAllocation, Route route) {
assert (Thread.holdsLock(this));
for (RealConnection connection : connections) {
if (connection.isEligible(address, route)) {
streamAllocation.acquire(connection);
return connection;
}
}
return null;
}
@Nullable Socket deduplicate(Address address, StreamAllocation streamAllocation) {…. }
//放入链接
void put(RealConnection connection) {
assert (Thread.holdsLock(this));
if (!cleanupRunning) {
cleanupRunning = true;
executor.execute(cleanupRunnable);
}
connections.add(connection);
}
/**
* 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) {……….}
/** Close and remove all idle connections in the pool. */
//移除所有的链接操作
public void evictAll() { …………. }
………….
}
(二)okhttp缓存流程
Interceptor 的布局,在建立连接、和服务器通讯之前,就是 CacheInterceptor,在建立连接之前,我们检查响应是否已经被缓存、缓存是否可用,如果是则直接返回缓存的数据,否则就进行后面的流程,并在返回之前,把网络的数据写入缓存。