美文网首页
Android Okhttp笔记:ConnectionPool

Android Okhttp笔记:ConnectionPool

作者: silencefun | 来源:发表于2020-03-05 14:15 被阅读0次

Anroid OKhttp笔记1 流程分析
Android OkhttpInterceptor 笔记:RetryAndFollowUpInterceptor
Android OkhttpInterceptor 笔记:BridgeInterceptor
Android OkhttpInterceptor 笔记:ConnectInterceptor
Android OkhttpInterceptor 笔记:CacheInterceptor
Android OkhttpInterceptor 笔记:CallServerInterceptor
Android Okhttp笔记:ConnectionPool
Android Okhttp3:Dispatcher分析笔记


一、综述

1.综述

ConnectionPool 即连接池,用来管理 HTTP 和 HTTP/2 连接的重用,以减少网络延迟。
在okhttp中,客户端与服务端的连接被抽象为一个个的Connection,实现类是RealConnection。而ConnectionPool就是专门用来管理Connection的类。

Manages reuse of HTTP and HTTP/2 connections for reduced network latency. HTTP requests that
share the same {@link Address} may share a {@link Connection}. This class implements the policy
of which connections to keep open for future use.

ConnectionPool用来管理connections的复用,以减少网络的延迟。一些共享一个地址(Address)的HTTP requests可能也会共享一个Connection。ConnectionPool设置这样的策略:让一些connections保持打开状态,以备将来使用。

2.使用

二、源码

1.成员变量

 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));

  /** The maximum number of idle connections for each address. */
  private final int maxIdleConnections;
  private final long keepAliveDurationNs;
  private final Runnable cleanupRunnable  ;//省略runable方法体 

  private final Deque<RealConnection> connections = new ArrayDeque<>();
  final RouteDatabase routeDatabase = new RouteDatabase();
  boolean cleanupRunning;

其中:
maxIdleConnections 每个地址闲置的connections 的最大数量
keepAliveDurationNs 每个空闲连接的存活时间的纳秒数
executor 为线程池,用来检测闲置socket并对其进行清理。
connections : connection缓存池。Deque是一个双端列表,支持在头尾插入元素,这里用作LIFO(后进先出)堆栈,多用于缓存数据。
routeDatabase :用来记录连接失败router,路线。
cleanupRunning 是否是清理过期连接的标记位

runable的源代码,清除过期连接的任务

   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) {
        }
      }
    }
  }
}
 };

2.构造方法

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

最大空闲连接数为5,每个连接最大空闲时间为5分钟。

3.方法概览

(简书不能折叠).png

4.分析

1.synchronized 关键字的两个方法

  /** Returns the number of idle connections in the pool. */
   public synchronized int idleConnectionCount() {
     int total = 0;
     for (RealConnection connection : connections) {
       if (connection.allocations.isEmpty()) total++;
     }
     return total;
   }


   public synchronized int connectionCount() {
      return connections.size();
    }

public synchronized int idleConnectionCount()
获取当前空闲连接的个数
public synchronized int connectionCount()
获取当前连接的个数
2.get put
2.1
get() 方法遍历 connections 中的所有 RealConnection 寻找同时满足条件的RealConnection。

  RealConnection get(Address address, StreamAllocation streamAllocation, Route route) {
//断言,判断线程是不是被自己锁住了
    assert (Thread.holdsLock(this));
// 遍历已有连接集合
    for (RealConnection connection : connections) { 
   //如果connection和需求中的"地址"和"路由"匹配
      if (connection.isEligible(address, route)) {
   //复用这个连接
        streamAllocation.acquire(connection);
        return connection;
      }
    }
    return null;
  }

2.2
put()异步触发清理任务,然后将连接添加到队列中

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

3.deduplicate()如果可能,将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;
}

4.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());
     }
   }

5.connectionBecameIdle
告诉连接池,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;
  }
}
  1. cleanup 清除过期的空闲连接。

a. 循环遍历connections 主要是寻找队列中 正在使用的连接inUseConnectionCount、空闲连接的个数idleConnectionCount、空闲等待最久的连接longestIdleConnection
b.空闲等待最久的连接等待时间超过了keepAliveDurationNs,或者 空闲连总数大于了允许的最大空闲连接数maxIdleConnections,则从队列中移除当前连接,并关闭,然后cleanupRunnable继续执行 cleanup(long now).
c.如果有空闲的连接,则cleanupRunnable等待时间为keepAliveDurationNs - longestIdleDurationNs
d.如果连接都在使用中,则cleanupRunnable等待时间为keepAliveDurationNs
e.cleanupRunnable任务完成

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

Anroid OKhttp笔记1 流程分析
Android OkhttpInterceptor 笔记:RetryAndFollowUpInterceptor
Android OkhttpInterceptor 笔记:BridgeInterceptor
Android OkhttpInterceptor 笔记:ConnectInterceptor
Android OkhttpInterceptor 笔记:CacheInterceptor
Android OkhttpInterceptor 笔记:CallServerInterceptor
Android Okhttp笔记:ConnectionPool
Android Okhttp3:Dispatcher分析笔记

相关文章

网友评论

      本文标题:Android Okhttp笔记:ConnectionPool

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