okhttp源码解析--连接池ConnectionPool
2019-03-15 本文已影响3人
二妹是只猫
Okhttpclient流程图.png
okhttp中所有的连接(RealConnection)都是通过ConnectionPool来管理,所以链接池在okhttp中也是比较中要的。
ConnectionPool的put()和get()
因为put方法和线程池回收的紧密这里就一些写了:
put()、线程池回收:
void put(RealConnection connection) {
assert (Thread.holdsLock(this));
if (!cleanupRunning) {
cleanupRunning = true;
executor.execute(cleanupRunnable);
}
connections.add(connection);
}
- 使用断言判断此线程是否上锁
- 执行cleanupRunnable清除无用连接
- 连接队列
connections
中添加连接
executor.execute(cleanupRunnable):
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) {
}
}
}
}
}
};
- 这里是一个死循环,通过
cleanup
清除无用线程并算出下次需要清理需要等待的时间 - 在ConnectionPool.this.wait设置了等待时间,当时间到时会在此调用此cleanupRunnable
cleanup(long now)
long cleanup(long now) {
int inUseConnectionCount = 0;
int idleConnectionCount = 0;
RealConnection longestIdleConnection = null;
long longestIdleDurationNs = Long.MIN_VALUE;
// Find either a connection to evict, or the time that the next eviction is due.
synchronized (this) {
for (Iterator<RealConnection> i = connections.iterator(); i.hasNext(); ) {
RealConnection connection = i.next();
// If the connection is in use, keep searching.
5 if (pruneAndGetAllocationCount(connection, now) > 0) {
inUseConnectionCount++;
continue;
}
idleConnectionCount++;
// If the connection is ready to be evicted, we're done.
long idleDurationNs = now - connection.idleAtNanos;
if (idleDurationNs > longestIdleDurationNs) {
longestIdleDurationNs = idleDurationNs;
longestIdleConnection = connection;
}
}
1 if (longestIdleDurationNs >= this.keepAliveDurationNs
|| idleConnectionCount > this.maxIdleConnections) {
// We've found a connection to evict. Remove it from the list, then close it below (outside
// of the synchronized block).
connections.remove(longestIdleConnection);
2 } else if (idleConnectionCount > 0) {
// A connection will be ready to evict soon.
return keepAliveDurationNs - longestIdleDurationNs;
3 } else if (inUseConnectionCount > 0) {
// All connections are in use. It'll be at least the keep alive duration 'til we run again.
return keepAliveDurationNs;
} else {
// No connections, idle or in use.
cleanupRunning = false;
4 return -1;
}
}
closeQuietly(longestIdleConnection.socket());
// Cleanup again immediately.
return 0;
}
- 这里的清除算法类似于java gc的标记清除算法(首先标记出最不活跃的连接(泄漏的连接),然后执行清除流程)
- 1.空闲的连接大于设定的数量,清除
- 2.当连接全部是活跃连接是,返回keepAliveDurationNs - longestIdleDurationNs的时间
- 3.正在使用的连接大于0,返回keepAliveDurationNs供下次使用
- 4.如果没有可清除连接,返回-1,跳出死循环
标记5的pruneAndGetAllocationCount 方法:
private int pruneAndGetAllocationCount(RealConnection connection, long now) {
List<Reference<StreamAllocation>> references = connection.allocations;
for (int i = 0; i < references.size(); ) {
Reference<StreamAllocation> reference = references.get(i);
if (reference.get() != null) {
I++;
continue;
}
// We've discovered a leaked allocation. This is an application bug.
StreamAllocation.StreamAllocationReference streamAllocRef =
(StreamAllocation.StreamAllocationReference) reference;
String message = "A connection to " + connection.route().address().url()
+ " was leaked. Did you forget to close a response body?";
Platform.get().logCloseableLeak(message, streamAllocRef.callStackTrace);
references.remove(i);
connection.noNewStreams = true;
// If this was the last allocation, the connection is eligible for immediate eviction.
if (references.isEmpty()) {
connection.idleAtNanos = now - keepAliveDurationNs;
return 0;
}
}
return references.size();
}
通过便利弱引用的StreamAllocation列表,如果为空就从列表中remove掉,当references为空时直接返回0,否者返回references的数量。这个方法的核心是StreamAllocation逐渐减少的情况下会被线程池检测并回收,这样来保持健康的keep-alive连接
get():
/** Returns a recycled connection to {@code address}, or null if no such connection exists. */
RealConnection get(Address address, StreamAllocation streamAllocation) {
assert (Thread.holdsLock(this));
for (RealConnection connection : connections) {
if (connection.allocations.size() < connection.allocationLimit
&& address.equals(connection.route().address)
&& !connection.noNewStreams) {
streamAllocation.acquire(connection);
return connection;
}
}
return null;
}
get方法就比较简单了,通过便利连接池和判断返回符合需求的连接