美文网首页android
okhttp源码解析--连接池ConnectionPool

okhttp源码解析--连接池ConnectionPool

作者: 二妹是只猫 | 来源:发表于2019-03-15 10:46 被阅读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方法就比较简单了,通过便利连接池和判断返回符合需求的连接

相关文章

网友评论

    本文标题:okhttp源码解析--连接池ConnectionPool

    本文链接:https://www.haomeiwen.com/subject/oqgkmqtx.html