OkHttp 内部还维护了一个连接池,用于缓存一定数量的连接,以减少与服务器建立连接时的资源开销。同时,为了保证缓存的连接数在一个合理的水平,连接池有一个最多闲置连接数量和最长连接闲置时长。这里我们还是通过分析一些关键方法来分析它的连接池机制:
ConnectionPool#get()
这个方法用于从连接池中获取一个可用的连接。
1 2 3 4 5 6 7 8 9
| RealConnection get(Address address, StreamAllocation streamAllocation, Route route) { for (RealConnection connection : connections) { if (connection.isEligible(address, route)) { streamAllocation.acquire(connection); return connection; } } return null; }
|
这里的实现也很简单,遍历连接池,找到可用的就返回,没找到就返回null。
ConnectionPool#put()
每次新建一个连接,都会往这个连接池里面丢。
1 2 3 4 5 6 7
| void put(RealConnection connection) { if (!cleanupRunning) { cleanupRunning = true; executor.execute(cleanupRunnable); } connections.add(connection); }
|
这里首先会判断有没有正在清理 多余 或者 闲置过久 的连接,没有的话,就先清理一波,然后把连接丢进去。
连接池的维护
连接池内部有一个专门负责清理冗余连接的线程。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57
| 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) { } } } } } };
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) continue; idleConnectionCount++; long idleDurationNs = now - connection.idleAtNanos; if (idleDurationNs > longestIdleDurationNs) { longestIdleDurationNs = idleDurationNs; longestIdleConnection = connection; } }
if (longestIdleDurationNs >= this.keepAliveDurationNs || idleConnectionCount > this.maxIdleConnections) { connections.remove(longestIdleConnection); } else if (idleConnectionCount > 0) { return keepAliveDurationNs - longestIdleDurationNs; } else if (inUseConnectionCount > 0) { return keepAliveDurationNs; } else { cleanupRunning = false; return -1; } }
closeQuietly(longestIdleConnection.socket()); return 0; }
|
大概的意思就是这个清理线程一直运行,它会不断的检查是否有过期的连接并进行关闭,然后暂停特定时间来进行下一次清理。
其他
OkHttp 内部有一个很重要的类– StreamAllocation
,这个类协调了 Connection
、Streams
、Calls
之间的关系,作为一次请求( Call )的代表,在一个或多个连接( Connection )上传输一个或多个数据流( Stream )。
整个请求流程中,这个类最先出现在 RetryAndFollowUpInterceptor
里面,当简单了解这个类的内部实现之后,对请求的逻辑也能更好的理解。