okhttp之ConnectionPool

转载请以链接形式标明出处:
本文出自:103style的博客

base on 3.12.0


目录

  • 简介
  • ConnectionPool 的成员变量
  • ConnectionPool 的构造函数
  • ConnectionPool 的相关方法
  • 小结

简介

ConnectionPool 即连接池,用来管理 HTTPHTTP/2 连接的重用,以减少网络延迟。

相同的 HTTP 请求可以共用一个连接(RealConnection)ConnectionPool 实现了将哪些连接保持打开状态以备将来使用的策略。

ConnectionPool 是用来管理 RealConnection 用的。


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));
    
  • 最大空闲连接数,在cleanup(...)清理的时候用到
    private final int maxIdleConnections;
    
  • 每个空闲连接的存活时间的纳秒数
    private final long keepAliveDurationNs;
    
  • 清除过期连接的任务
    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) {
              }
            }
          }
        }
      }
    };
    
  • 保存可复用连接的队列
    private final Deque<RealConnection> connections = new ArrayDeque<>();
    
  • 连接地址要避免的失败路由黑名单
    final RouteDatabase routeDatabase = new RouteDatabase();
    
  • 是否是清理过期连接的标记位
    boolean cleanupRunning;
    

ConnectionPool 的构造函数

默认配置的 每个地址的空闲连接数为 5个,每个空闲连接的存活时间为 5分钟.

public ConnectionPool() {
    this(5, 5, TimeUnit.MINUTES);
}
public ConnectionPool(int maxIdleConnections, long keepAliveDuration, TimeUnit timeUnit) {
    this.maxIdleConnections = maxIdleConnections;
    this.keepAliveDurationNs = timeUnit.toNanos(keepAliveDuration);

    if (keepAliveDuration <= 0) {
        throw new IllegalArgumentException("keepAliveDuration <= 0: " + keepAliveDuration);
    }
}

ConnectionPool 的相关方法

公开方法
  • public synchronized int idleConnectionCount()
    获取当前空闲连接的个数

    public synchronized int idleConnectionCount() {
      int total = 0;
      for (RealConnection connection : connections) {
        if (connection.allocations.isEmpty()) total++;
      }
      return total;
    }
    
  • public synchronized int connectionCount()
    获取当前连接的个数

    public synchronized int connectionCount() {
      return connections.size();
    }
    
  • public void evictAll()
    关闭和移除连接池中所有的空闲连接

    public void evictAll() {
      List<RealConnection> evictedConnections = new ArrayList<>();
      synchronized (this) {
        for (Iterator<RealConnection> i = connections.iterator(); i.hasNext(); ) {
          RealConnection connection = i.next();
          if (connection.allocations.isEmpty()) {
            connection.noNewStreams = true;
            evictedConnections.add(connection);
            i.remove();
          }
        }
      }
      for (RealConnection connection : evictedConnections) {
        closeQuietly(connection.socket());
      }
    }
    
私有方法
  • RealConnection get(Address address, StreamAllocation streamAllocation, Route route)
    获取可复用的连接,没有则返回null.

    @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, true);
          return connection;
        }
      }
      return null;
    }
    
  • Socket deduplicate(Address address, StreamAllocation streamAllocation)
    如果可能,将streamAllocation保留的连接替换为共享连接。

    @Nullable Socket deduplicate(Address address, StreamAllocation streamAllocation) {
      assert (Thread.holdsLock(this));
      for (RealConnection connection : connections) {
        if (connection.isEligible(address, null)
            && connection.isMultiplexed()
            && connection != streamAllocation.connection()) {
          return streamAllocation.releaseAndAcquire(connection);
        }
      }
      return null;
    }
    
  • void put(RealConnection connection)
    先清除过期的连接,再将可复用的连接保存到队列connections

    void put(RealConnection connection) {
      assert (Thread.holdsLock(this));
      if (!cleanupRunning) {
        cleanupRunning = true;
        executor.execute(cleanupRunnable);
      }
      connections.add(connection);
    }
    
  • boolean connectionBecameIdle(RealConnection connection)
    告诉连接池,connection已变为空闲连接。
    如果配置了 RealConnection.noNewStreams= true 不允许复用 或者 maxIdleConnections==0 不允许有空闲连接,则直接从队列中删除该连接。

    boolean connectionBecameIdle(RealConnection connection) {
      assert (Thread.holdsLock(this));
      if (connection.noNewStreams || maxIdleConnections == 0) {
        connections.remove(connection);
        return true;
      } else {
        notifyAll(); 
        return false;
      }
    }
    
  • long cleanup(long now)
    清除过期的空闲连接。

    • 1.0 循环遍历connections 主要是寻找队列中 正在使用的连接inUseConnectionCount、空闲连接的个数idleConnectionCount、空闲等待最久的连接longestIdleConnection
    • 2.0 空闲等待最久的连接等待时间超过了keepAliveDurationNs,或者 空闲连总数大于了允许的最大空闲连接数maxIdleConnections,则从队列中移除当前连接,并关闭,然后cleanupRunnable继续执行 cleanup(long now).
    • 3.0 如果有空闲的连接,则cleanupRunnable等待时间为keepAliveDurationNs - longestIdleDurationNs
    • 4.0 如果连接都在使用中,则cleanupRunnable等待时间为keepAliveDurationNs
    • 5.0 否则cleanupRunnable任务完成
    long cleanup(long now) {
      int inUseConnectionCount = 0;
      int idleConnectionCount = 0;
      RealConnection longestIdleConnection = null;
      long longestIdleDurationNs = Long.MIN_VALUE;
      synchronized (this) {
        // 1.0
        for (Iterator<RealConnection> i = connections.iterator(); i.hasNext(); ) {
          RealConnection connection = i.next();
          if (pruneAndGetAllocationCount(connection, now) > 0) {
            inUseConnectionCount++;
            continue;
          }
          idleConnectionCount++;
          long idleDurationNs = now - connection.idleAtNanos;
          if (idleDurationNs > longestIdleDurationNs) {
            longestIdleDurationNs = idleDurationNs;
            longestIdleConnection = connection;
          }
        }
        if (longestIdleDurationNs >= this.keepAliveDurationNs
            || idleConnectionCount > this.maxIdleConnections) { //2.0
          connections.remove(longestIdleConnection);
        } else if (idleConnectionCount > 0) {//3.0
          return keepAliveDurationNs - longestIdleDurationNs;
        } else if (inUseConnectionCount > 0) {//4.0
          return keepAliveDurationNs;
        } else {//5.0
          cleanupRunning = false;
          return -1;
        }
      }
      closeQuietly(longestIdleConnection.socket());//关闭连接
      return 0;
    }
    

小结

  • 连接池ConnectionPool是用来管理 HTTPHTTP/2 连接的重用,以减少网络延迟。

  • 连接池默认时每个地址的空闲连接数为 5个,每个空闲连接的存活时间为 5分钟.

  • 连接池每次添加一个新的连接时,都会先清理当前连接池中过期的连接,通过 清理线程池executor 执行清理任务cleanupRunnable


以上

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值