Okhttp清理空闲连接的源码解读

先看连接池类ConnectionPool:

在这里插入图片描述
由上可知,默认情况下,ConnectionPool 最多保存 5个 处于空闲状态的连接,且连接的默认保活时间为 5分钟。也就是默认支持5个并发Socket连接,默认的keepAlive时间为5分钟。
以下是具体的连接池类RealConnectionPool的关键代码:

void put(RealConnection connection) {
assert (Thread.holdsLock(this));
  if (!cleanupRunning) {
    cleanupRunning = true;
    executor.execute(cleanupRunnable);
}
connections.add(connection);
}

从这个if判断可知清理连接是由首次执行put方法时发起的,具体清理的动作在线程池的cleanupRunnable任务中进行的:

private final Runnable cleanupRunnable = () -> {
while (true) {
    long waitNanos = cleanup(System.nanoTime());
    if (waitNanos == -1) return;
    if (waitNanos >0) {
    long waitMillis = waitNanos / 1000000L;
    waitNanos -= (waitMillis * 1000000L);
    synchronized (RealConnectionPool.this) {
    try {
       RealConnectionPool.this.wait(waitMillis, (int) waitNanos);
    } catch (InterruptedException ignored) {
        }
      }
    }
  }
};

可知,是由while+wait(log l,int i)来形成一个定时循环器,每隔waitMillis+1秒通过cleanup(System.nanoTime())执行一次清理连接。现在分析cleanup(System.nanoTime())函数:

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.
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;
}
 }

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);
} else if (idleConnectionCount >0) {
// A connection will be ready to evict soon.
return keepAliveDurationNs - longestIdleDurationNs;
} 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;
      return -1;
}
  }

closeQuietly(longestIdleConnection.socket());

// Cleanup again immediately.
return 0;
}

在for循环里+
if (idleDurationNs > longestIdleDurationNs) {
longestIdleDurationNs = idleDurationNs;
longestIdleConnection = connection;
}
这个选择排序算法来找到空闲最长的连接。接着通过

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);
}
判断当前连接属于第五个以后的空闲连接或者当前连接空闲时间大于指定的keepAliveDurationNs后,将当前的连接移除。

在cleanup方法中,使用连接数目inUseConnectionCount 和闲置连接数目idleConnectionCount 的计数累加值都是通过pruneAndGetAllocationCount() 是否大于0来控制的。那么很显然pruneAndGetAllocationCount() 方法就是用来识别对应连接是否闲置的。大于0则不闲置。否则就是闲置的连接。现在分析pruneAndGetAllocationCount()方法:

private int pruneAndGetAllocationCount(RealConnection connection, long now) {
  List<Reference<Transmitter>> references = connection.transmitters;
  for (int i = 0; i < references.size(); ) {
    Reference<Transmitter> reference = references.get(i);

    if (reference.get() != null) {
      i++;
      continue;
}

// We've discovered a leaked transmitter. This is an application bug.
TransmitterReference transmitterRef = (TransmitterReference) reference;
String message = "A connection to " + connection.route().address().url()
        + " was leaked. Did you forget to close a response body?";
Platform.get().logCloseableLeak(message, transmitterRef.callStackTrace);

references.remove(i);
connection.noNewExchanges = true;

// If this was the last allocation, the connection is eligible for immediate eviction.
if (references.isEmpty()) {
      connection.idleAtNanos = now - keepAliveDurationNs;
      return 0;
}
  }

遍历Transmitter弱引用链表,移除为空的引用,遍历结束后返回链表中弱引用的数量。所以可以看出List<Reference> 就是一个记录connection活跃情况的,大于0表示活跃 ,等于0 表示空闲。StreamAllocation 在列表中的数量就是就是物理socket被引用的次数。
解释:Transmitter被高层反复执行aquire与release。这两个函数在执行过程中其实是在一直在改变Connection中的该弱引用链表大小。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值