Android网络编制程序(四)从源码解析volley,OkHttp类别小说如下

OkHttp类别作品如下

连带小说
Android网络编制程序(一)HTTP协议原理
Android互联网编制程序(二)HttpClient与HttpU汉兰达LConnection
Android网络编制程序(三)Volley用法全分析
Android互联网编制程序(四)从源码解析volley
Android网络编制程序(五)OkHttp2.x用法全分析
Android网络编制程序(六)OkHttp3用法全分析
Android互联网编制程序(七)源码解析OkHttp前篇[请求网络]

1.引子

在领悟OkHttp的复用连接池在此之前,大家首先要打听多少个概念。


TCP三遍握手

普通大家进行HTTP连接网络的时候我们会议及展览开TCP的一回握手,然后传输数据,然后再自由连接。

澳门金冠开户 1

TCP二次握手的进度为:

  • 第一次握手:建立连接。客户端发送连接请求报文段,将SYN地点为1,Sequence
    Number为x;然后,客户端进入SYN_SEND状态,等待服务器的肯定;

  • 第②遍握手:服务器收到客户端的SYN报文段,须求对这一个SYN报文段举办确认,设置Acknowledgment
    Number为x+1(Sequence
    Number+1);同时,本身团结还要发送SYN请求音讯,将SYN地方为1,Sequence
    Number为y;服务器端将上述全部信息放到贰个报文段(即SYN+ACK报文段)中,一并发送给客户端,此时服务器进入SYN_RECV状态;

  • 其3回握手:客户端收到服务器的SYN+ACK报文段。然后将Acknowledgment
    Number设置为y+1,向服务器发送ACK报文段,那一个报文段发送完成之后,客户端和服务器端都进入ESTABLISHED状态,完毕TCP二遍握手。

1. 概述

HTTP中的keepalive连接在互联网品质优化中,对于延迟降低与进程提高的有那些关键的机能。

万般大家进行http连接时,首先进行tcp握手,然后传输数据,最后获释

图源: Nginx closed

那种措施确实不难,可是在错综复杂的网络内容中就不够用了,成立socket须求举行三次握手,而自由socket须要三次抓手(或许是七遍)。重复的连年与释放tcp连接就像每一次唯有挤1mm的牙膏就合上牙膏盖子接着再打开接着挤一样。而每趟延续大约是TTL二遍的大运(也正是ping二遍),在TLS环境下消耗的年月就越来越多了。很强烈,当访问复杂网络时,延时(而不是带宽)将变为非凡重要的因素。

自然,上面的难点早已经化解了,在http中有一种名叫keepalive connections的编制,它能够在传输数据后依旧维持几次三番,当客户端供给再行获取数据时,直接采用刚刚空闲下来的连天而不须求再度握手

图源: Nginx keep_alive

在现世浏览器中,一般同时打开6~九个keepalive connections的socket连接,并保证一定的链路生命,当不须要时再关闭;而在服务器中,一般是由软件依据负荷处境(比如FD最大值、Socket内部存款和储蓄器、超时时间、栈内部存款和储蓄器、栈数量等)决定是或不是主动关闭。

Okhttp支持陆个并发KeepAlive,暗中认可链路生命为四分钟(链路空闲后,保持现有的小时)

本来keepalive也有瑕疵,在增高了单个客户端质量的还要,复用却阻止了其余客户端的链路速度,具体来说如下

  1. 据书上说TCP的不通机制,当总水管大小固定时,要是存在大气悠然的keepalive connections(我们能够称为僵尸连接或者泄漏连接),其它客户端们的常规连接速度也会惨遭震慑,那也是营业商为啥限制P2P连接数的道理
  2. 服务器/防火墙上有出现限制,比如apache服务器对种种请求都开线程,导致只辅助1肆17个冒出连接(数据来源nginx官网),可是那么些瓶颈随着高并发server软硬件的进化(golang/分布式/IO多路复用)将会越来越少
  3. 多量的DDOS发生的僵尸连接大概被用来恶意抨击服务器,耗尽财富

好了,以上科学普及完结,本文首假如写客户端的,服务端不再介绍。

下文借使服务器是透过正规的运营配置好的,它暗许开启了keep-alive,并不主动关闭连接

TCP七遍分离

当客户端和服务器通过二遍握手建立了TCP连接未来,当数码传送完结,断开连接就要求实行TCP四回分别:

  • 先是次分离:主机1(能够使客户端,也足以是劳务器端),设置Sequence
    Number和Acknowledgment
    Number,向长机2出殡和埋葬一个FIN报文段;此时,主机1进来FIN_WAIT_1状态;那代表主机1从未有过多少要发送给主机2了;

  • 其次次分别:主机2接收了主机1发送的FIN报文段,向长机1次3个ACK报文段,Acknowledgment
    Number为Sequence

  • 其3回分离:主机2向长机1出殡和埋葬FIN报文段,请求关闭连接,同时主机2跻身LAST_ACK状态;

  • 第九遍分别:主机1接受主机2发送的FIN报文段,向长机2出殡和埋葬ACK报文段,然后主机1进来TIME_WAIT状态;主机2收受主机1的ACK报文段以往,就关闭连接;此时,主机1等候2MSL后还是没有收到回复,则证实Server端已健康关闭,那好,主机1也足以关闭连接了。

来看上边包车型客车图抓实下明白:

澳门金冠开户 2

2. 连接池的选择与分析

率先先说下源码中主要性的目的:

  • Call: 对http的呼吁封装,属于程序员能够接触的上层高级代码
  • Connection:
    对jdk的socket物理连接的卷入,它里面有List<WeakReference<StreamAllocation>>的引用
  • StreamAllocation: 表示Connection被上层高级代码的引用次数
  • ConnectionPool:
    Socket连接池,对三番五次缓存实行回收与管理,与CommonPool有相近的统一筹划
  • Deque:
    Deque也正是双端队列,双端队列同时具备队列和栈性质,平时在缓存中被利用,这一个是java基础

在okhttp中,连接池对用户,甚至开发者都以晶莹的。它自动创制连接池,自动进行泄漏连接回收,自动帮您管理线程池,提供了put/get/clear的接口,甚至里头调用都帮您写好了。

在在此以前的内部存款和储蓄器泄露分析作品中本人写到,大家清楚在socket连接中,也正是Connection中,本质是包装好的流操作,除非手动close掉连接,基本不会被GC掉,十分不难引发内部存款和储蓄器败露。所以当提到到并发socket编制程序时,大家就会杰出忐忑,往往写出来的代码都以try/catch/finally的迷之缩进,却又对这么的代码无可如何。

在okhttp中,在高层代码的调用中,使用了看似于引用计数的措施跟踪Socket流的调用,那里的计数对象是StreamAllocation,它被一再实践aquirerelease操作(点击函数能够进去github查看),那八个函数其实是在改动Connection中的List<WeakReference<StreamAllocation>>大小。List中Allocation的数目约等于物理socket被引述的计数(Refference
Count),要是计数为0的话,表达此一而再没有被使用,是悠闲的,供给经过下文的算法达成回收;假诺上层代码依旧引用,就不必要关闭连接。

引用计数法:给目的中添加八个引用计数器,每当有二个地点引用它时,计数器值就加1;当引用失效时,计数器值就减1;任何时刻计数器为0的靶子正是不也许再被使用。它不可能处理循环引用的题材。

keepalive connections

本来大批量的接连每回再而三关闭都要二回握手八遍分离的很醒目会导致质量低下,因而http有一种名叫keepalive
connections的建制,它能够在传输数据后依然维持接二连三,当客户端须要再行获取数据时,直接使用刚刚空闲下来的总是而不须求重新握手。

澳门金冠开户 3

Okhttp扶助陆个并发Keep阿里ve,默许链路生命为6分钟(链路空闲后,保持现有的时间)。

2.1. 实例化

在源码中,大家先找ConnectionPool实例化的职分,它是一向new出来的,而它的种种操作却在OkHttpClientstatic区实现了Internal.instance接口作为ConnectionPool的包装。

至于何以供给如此见惯不惊的分段包装,主倘诺为了让外部包的分子访问非public办法,详见那里注释

2.连接池(ConnectionPool)分析

2.2. 构造

  1. 连接池内部维护了二个叫做OkHttp ConnectionPoolThreadPool,专门用来淘汰最后一位的socket,当满足以下原则时,就会议及展览开倒数一位淘汰,相当像GC

    1. 并发socket空闲连接超过5个
    2. 某个socket的keepalive时间大于5分钟
    
  2. 护卫着叁个Deque<Connection>,提供get/put/remove等数据结构的效能

  3. 保卫安全着1个RouteDatabase,它用来记录连接退步的Route的黑名单,当连接退步的时候就会把破产的线路加进去(本文不研究)

引用计数

在okhttp中,在高层代码的调用中,使用了近似于引用计数的法子跟踪Socket流的调用,这里的计数对象是StreamAllocation,它被反复实践aquire与release操作,那五个函数其实是在转移RealConnection中的List<Reference<StreamAllocation>>
的大小。(StreamAllocation.java)

  public void acquire(RealConnection connection) {
    connection.allocations.add(new WeakReference<>(this));
  }

  private void release(RealConnection connection) {
    for (int i = 0, size = connection.allocations.size(); i < size; i++) {
      Reference<StreamAllocation> reference = connection.allocations.get(i);
      if (reference.get() == this) {
        connection.allocations.remove(i);
        return;
      }
    }
    throw new IllegalStateException();
  }

RealConnection是socket物理连接的包装,它在这之中维护了List<Reference<StreamAllocation>>的引用。List中StreamAllocation的多寡相当于socket被引用的计数,假使计数为0的话,表达此再而三没有被使用正是悠闲的,须求经过下文的算法实现回收;若是计数不为0,则表示上层代码依旧引用,就不须求关闭连接。

2.3 put/get操作

在连接池中,提供如下的操作,那里能够看成是对deque的三个简易的包装

//从连接池中获取
get
//放入连接池
put
//线程变成空闲,并调用清理线程池
connectionBecameIdle
//关闭所有连接
evictAll

乘胜上述操作被更尖端的指标调用,Connection中的StreamAllocation被频频的aquirerelease,也就是List<WeakReference<StreamAllocation>>的大小将无时无刻变化

主要变量

连接池的类位于okhttp3.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));

  /** The maximum number of idle connections for each address. */
  //空闲的socket最大连接数
  private final int maxIdleConnections;
  //socket的keepAlive时间
  private final long keepAliveDurationNs;
  // 双向队列
  private final Deque<RealConnection> connections = new ArrayDeque<>();
  final RouteDatabase routeDatabase = new RouteDatabase();
  boolean cleanupRunning;

根本的变量有必不可少说圣元下:

  • executor线程池,类似于CachedThreadPool,要求留意的是那种线程池的做事行列选拔了未曾体积的SynchronousQueue,不打听它的请查看Java并发编制程序(六)阻塞队列那篇作品。
  • Deque<RealConnection>,双向队列,双端队列同时拥有队列和栈性质,常常在缓存中被使用,里面维护了RealConnection也正是socket物理连接的卷入。
  • RouteDatabase,它用来记录连接失利的Route的黑名单,当连接战败的时候就会把破产的线路加进去。

2.4 Connection自动回收的贯彻

java内部有垃圾回收GC,okhttp有socket的回收;垃圾回收是基于目的的引用树完成的,而okhttp是依据RealConnection的虚引用StreamAllocation澳门金冠开户,引用计数是不是为0实现的。我们先看代码

cleanupRunnable:

当用户socket连接成功,向连接池中put新的socket时,回收函数会被主动调用,线程池就会执行cleanupRunnable,如下

//Socket清理的Runnable,每当put操作时,就会被主动调用
//注意put操作是在网络线程
//而Socket清理是在`OkHttp ConnectionPool`线程池中调用
while (true) {
  //执行清理并返回下场需要清理的时间
  long waitNanos = cleanup(System.nanoTime());
  if (waitNanos == -1) return;
  if (waitNanos > 0) {
    synchronized (ConnectionPool.this) {
      try {
        //在timeout内释放锁与时间片
        ConnectionPool.this.wait(TimeUnit.NANOSECONDS.toMillis(waitNanos));
      } catch (InterruptedException ignored) {
      }
    }
  }
}

那段死循环实际上是二个梗阻的清理义务,首先举行清理(clean),并赶回下次急需清理的间隔时间,然后调用wait(timeout)进展等待以自由锁与时间片,当等待时间到了后,再次开展清理,并赶回下次要理清的间隔时间…

Cleanup:

cleanup运用了类似于GC的标记-清除算法,也正是率先标记出最不活跃的连日(大家得以称为泄漏连接,或者空闲连接),接着举办破除,流程如下:

long cleanup(long now) {
  int inUseConnectionCount = 0;
  int idleConnectionCount = 0;
  RealConnection longestIdleConnection = null;
  long longestIdleDurationNs = Long.MIN_VALUE;

  //遍历`Deque`中所有的`RealConnection`,标记泄漏的连接
  synchronized (this) {
    for (RealConnection connection : connections) {
      // 查询此连接内部StreamAllocation的引用数量
      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) {
      //如果(`空闲socket连接超过5个`
      //且`keepalive时间大于5分钟`)
      //就将此泄漏连接从`Deque`中移除
      connections.remove(longestIdleConnection);
    } else if (idleConnectionCount > 0) {
      //返回此连接即将到期的时间,供下次清理
      //这里依据是在上文`connectionBecameIdle`中设定的计时
      return keepAliveDurationNs - longestIdleDurationNs;
    } else if (inUseConnectionCount > 0) {
      //全部都是活跃的连接,5分钟后再次清理
      return keepAliveDurationNs;
    } else {
      //没有任何连接,跳出循环
      cleanupRunning = false;
      return -1;
    }
  }

  //关闭连接,返回`0`,也就是立刻再次清理
  closeQuietly(longestIdleConnection.socket());
  return 0;
}

太长不想看的话,正是之类的流水生产线:

  1. 遍历Deque中有所的RealConnection,标记泄漏的连天
  2. 假若被标记的连年满意(空闲socket连接超过5个&&keepalive时间大于5分钟),就将此一连从Deque中移除,并关闭连接,再次来到0,也正是快要执行wait(0),提示立时再一次扫描
  3. 如果(目前还可以塞得下5个连接,但是有可能泄漏的连接(即空闲时间即将达到5分钟)),就赶回此延续即将到期的剩余时间,供下次清理
  4. 如果(全部都是活跃的连接),就回来暗中认可的keep-alive时光,也正是五分钟后再履行清理
  5. 如果(没有任何连接),就返回-1,跳出清理的死循环

双重注意:那里的“并发”==(“空闲”+“活跃”)==5,而不是说并发连接就必定是活跃的接连

pruneAndGetAllocationCount:

怎么样标记并找到最不活跃的连日呢,这里运用了pruneAndGetAllocationCount方法,它根本依照弱引用是不是为null而判断那个延续是或不是泄漏

//类似于引用计数法,如果引用全部为空,返回立刻清理
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);
    //如果正在被使用,跳过,接着循环
    //是否置空是在上文`connectionBecameIdle`的`release`控制的
    if (reference.get() != null) {
      //非常明显的引用计数
      i++;
      continue;
    }

    //否则移除引用
    references.remove(i);
    connection.noNewStreams = true;

    //如果所有分配的流均没了,标记为已经距离现在空闲了5分钟
    if (references.isEmpty()) {
      connection.idleAtNanos = now - keepAliveDurationNs;
      return 0;
    }
  }

  return references.size();
}
  1. 遍历RealConnection连年中的StreamAllocationList,它珍贵着1个弱引用列表
  2. 查看此StreamAllocation是不是为空(它是在线程池的put/remove手动控制的),假使为空,表达已经没有代码引用这几个指标了,供给在List中删除
  3. 遍历结束,借使List中维护的StreamAllocation删空了,就返回0,表示这一个一而再已经远非代码引用了,是泄漏的连接;不然再次来到非0的值,表示那一个依然被引用,是生动活泼的连天。

上述达成的超负荷保守,实际上用filter就足以大致达成,伪代码如下

return references.stream().filter(reference -> {
    return !reference.get() == null;
}).count();

构造函数

  public ConnectionPool() {
  //默认空闲的socket最大连接数为5个,socket的keepAlive时间为5秒
    this(5, 5, TimeUnit.MINUTES);
  }
  public ConnectionPool(int maxIdleConnections, long keepAliveDuration, TimeUnit timeUnit) {
    this.maxIdleConnections = maxIdleConnections;
    this.keepAliveDurationNs = timeUnit.toNanos(keepAliveDuration);

    // Put a floor on the keep alive duration, otherwise cleanup will spin loop.
    if (keepAliveDuration <= 0) {
      throw new IllegalArgumentException("keepAliveDuration <= 0: " + keepAliveDuration);
    }
  }

透过构造函数能够看来ConnectionPool私下认可的空闲的socket最浦那接数为三个,socket的keepAlive时间为5秒。

总结

经过上边的剖析,大家能够计算,okhttp使用了就如于引用计数法与标记擦除法的混杂使用,当连接空闲只怕释放时,StreamAllocation的数码会慢慢变成0,从而被线程池监测到并回收,那样就能够维持五个常规的keep-alive连接,Okhttp的黑科学和技术正是那样完毕的。

最后推荐一本《图解HTTP》,马来人写的,看起来很不利。

再引进阅读开源Redis客户端Jedis的源码,能够看下它的JedisFactory的实现。

假如你希望越多高品质的稿子,不妨关怀笔者要么点赞吧!

实例化

ConnectionPool实例化是在OkHttpClient实例化时开始展览的:

  public OkHttpClient() {
    this(new Builder());
  }

在OkHttpClient的构造函数中调用了new Builder():

  public Builder() {
      dispatcher = new Dispatcher();
     ...省略
      connectionPool = new ConnectionPool();
     ...省略
    }

Ref

  1. https://www.nginx.com/blog/http-keepalives-and-web-performance/

缓存操作

ConnectionPool提供对Deque<RealConnection>举办操作的法子分别为put、get、connectionBecameIdle和evictAll多少个操作。分别对应放入连接、获取连接、移除连接和移除全体连接操作,这里大家举例put和get操作。

put操作

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

在增进到Deque<RealConnection>在此之前率先要理清空闲的线程,那些前面会讲到。

get操作

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

遍历connections缓存列表,当有个别连接计数的次数小于限制的大小并且request的地点和缓存列表中此延续的地址完全合营。则直接复用缓存列表中的connection作为request的三番五次。

机关回收连接

okhttp是基于StreamAllocation引用计数是不是为0来贯彻机关回收连接的。大家在put操作前首先要调用executor.execute(cleanupRunnable)来清理闲置的线程。大家来探视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来展开清理,并回到下次内需清理的间隔时间,然后调用wait举办等待以释放锁与时间片,当等待时间到了后,再一次展开清理,并重返下次要清理的间隔时间,如此循环往复下去,接下去看看cleanup方法:

  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();
        //查询此连接的StreamAllocation的引用数量,如果大于0则inUseConnectionCount数量加1,否则idleConnectionCount加1
        if (pruneAndGetAllocationCount(connection, now) > 0) {
          inUseConnectionCount++;
          continue;
        }
        idleConnectionCount++;
        long idleDurationNs = now - connection.idleAtNanos;
        if (idleDurationNs > longestIdleDurationNs) {
          longestIdleDurationNs = idleDurationNs;
          longestIdleConnection = connection;
        }
      }
      //如果空闲连接keepAlive时间超过5分钟,或者空闲连接数超过5个,则从Deque中移除此连接
      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);
       //如果空闲连接大于0,则返回此连接即将到期的时间
      } else if (idleConnectionCount > 0) {
        // A connection will be ready to evict soon.
        return keepAliveDurationNs - longestIdleDurationNs;
        //如果没有空闲连接,并且活跃连接大于0则返回5分钟
      } 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 {
      //如果没有任何连接则跳出循环
        cleanupRunning = false;
        return -1;
      }
    }

    closeQuietly(longestIdleConnection.socket());

    // Cleanup again immediately.
    return 0;
  }

cleanup所做的简短总计正是基于连年中的引用计数来测算空闲连接数和活泼连接数,然后标记出空闲的接连,如若空闲连接keepAlive时间超越4分钟,或许空闲连接数超越多个,则从Deque中移除此一而再。接下来根据空闲连接或许活跃接连来回到下次亟待清理的日子数:若是空闲连接大于0则赶回此延续即将到期的时光,假若都以生动活泼接连并且大于0则赶回暗许的keepAlive时间六分钟,假如没有其余连接则跳出循环并回到-1。在上述代码中的第一3行,通过pruneAndGetAllocationCount方法来判定连接是或不是闲置的,要是pruneAndGetAllocationCount方法重回值大于0则是悠闲连接,否则就是虎虎有生气接连,让我们来探望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);
      //若StreamAllocation被使用则接着循环
      if (reference.get() != null) {
        i++;
        continue;
      }

      // We've discovered a leaked allocation. This is an application bug.
      Internal.logger.warning("A connection to " + connection.route().address().url()
          + " was leaked. Did you forget to close a response body?");
      //若StreamAllocation未被使用则移除引用
      references.remove(i);
      connection.noNewStreams = true;

      // If this was the last allocation, the connection is eligible for immediate eviction.
      //如果列表为空则说明此连接没有被引用了,则返回0,表示此连接是空闲连接
      if (references.isEmpty()) {
        connection.idleAtNanos = now - keepAliveDurationNs;
        return 0;
      }
    }
    //否则返回非0的数,表示此连接是活跃连接
    return references.size();
  }

pruneAndGetAllocationCount方法首先遍历传进来的RealConnection的StreamAllocation列表,假如StreamAllocation被采用则接着遍历下三个StreamAllocation,如若StreamAllocation未被接纳则从列表中移除。假如列表为空则表达此再而三没有引用了,则再次来到0,表示此接二连三是悠闲连接,不然就重回非0的数表示此延续是生动活泼接连。

总结

能够看出连接池复用的主干就是用Deque<RealConnection>来存款和储蓄连接,通过put、get、connectionBecameIdle和evictAll多少个操作来对Deque进行操作,此外通过判断连接中的计数对象StreamAllocation来开展活动回收连接。

参考资料
okhttp3源码
简析TCP的贰遍握手与八回分别
TCP一次握手进度
短连接、长连接与keep-alive
OkHttp3源码分析[复用连接池]
okhttp连接池复用机制

相关文章