OkHttp3源码分析,复用连接池

2019-10-01 14:36 来源:未知

OkHttp系列文章如下

相关文章
Android网络编程(一)HTTP协议原理
Android网络编程(二)HttpClient与HttpURLConnection
Android网络编程(三)Volley用法全解析
Android网络编程(四)从源码解析volley
Android网络编程(五)OkHttp2.x用法全解析
Android网络编程(六)OkHttp3用法全解析
Android网络编程(七)源码解析OkHttp前篇[请求网络]

原文出处: 刘望舒   

  • OkHttp3源码分析[综述]
  • OkHttp3源码分析[复用连接池]
  • OkHttp3源码分析[缓存策略]
  • OkHttp3源码分析[DiskLruCache]
  • OkHttp3源码分析[任务队列]

1.引子

在了解OkHttp的复用连接池之前,我们首先要了解几个概念。

  • Android 网络编程(1): HTTP协议原理
  • Android 网络编程(2): HttpClient与HttpURLConnection
  • Android 网络编程(3): Volley用法全解析
  • Android 网络编程(4): 从源码解析volley
  • Android 网络编程(5): OkHttp2.x用法全解析
  • Android 网络编程(6): OkHttp3用法全解析
  • Android 网络编程(7): 源码解析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状态;

  • 第三次握手:客户端收到服务器的SYN+ACK报文段。然后将Acknowledgment Number设置为y+1,向服务器发送ACK报文段,这个报文段发送完毕以后,客户端和服务器端都进入ESTABLISHED状态,完成TCP三次握手。

1.引子

在了解OkHttp的复用连接池之前,我们首先要了解几个概念。

1. 概述

HTTP中的keepalive连接在网络性能优化中,对于延迟降低与速度提升的有非常重要的作用。

通常我们进行http连接时,首先进行tcp握手,然后传输数据,最后释放

图源: Nginx closed

这种方法的确简单,但是在复杂的网络内容中就不够用了,创建socket需要进行3次握手,而释放socket需要2次握手(或者是4次)。重复的连接与释放tcp连接就像每次仅仅挤1mm的牙膏就合上牙膏盖子接着再打开接着挤一样。而每次连接大概是TTL一次的时间(也就是ping一次),在TLS环境下消耗的时间就更多了。很明显,当访问复杂网络时,延时(而不是带宽)将成为非常重要的因素。

当然,上面的问题早已经解决了,在http中有一种叫做keepalive connections的机制,它可以在传输数据后仍然保持连接,当客户端需要再次获取数据时,直接使用刚刚空闲下来的连接而不需要再次握手

图源: Nginx keep_alive

在现代浏览器中,一般同时开启6~8个keepalive connections的socket连接,并保持一定的链路生命,当不需要时再关闭;而在服务器中,一般是由软件根据负载情况(比如FD最大值、Socket内存、超时时间、栈内存、栈数量等)决定是否主动关闭。

Okhttp支持5个并发KeepAlive,默认链路生命为5分钟(链路空闲后,保持存活的时间)

当然keepalive也有缺点,在提高了单个客户端性能的同时,复用却阻碍了其他客户端的链路速度,具体来说如下

  1. 根据TCP的拥塞机制,当总水管大小固定时,如果存在大量空闲的keepalive connections(我们可以称作僵尸连接或者泄漏连接),其它客户端们的正常连接速度也会受到影响,这也是运营商为何限制P2P连接数的道理
  2. 服务器/防火墙上有并发限制,比如apache服务器对每个请求都开线程,导致只支持150个并发连接(数据来源于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回一个ACK报文段,Acknowledgment Number为Sequence

  • 第三次分手:主机2向主机1发送FIN报文段,请求关闭连接,同时主机2进入LAST_ACK状态;

  • 第四次分手:主机1收到主机2发送的FIN报文段,向主机2发送ACK报文段,然后主机1进入TIME_WAIT状态;主机2收到主机1的ACK报文段以后,就关闭连接;此时,主机1等待2MSL后依然没有收到回复,则证明Server端已正常关闭,那好,主机1也可以关闭连接了。

来看下面的图加强下理解:

图片 2

TCP三次握手

通常我们进行HTTP连接网络的时候我们会进行TCP的三次握手,然后传输数据,然后再释放连接。

图片 3

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状态;
  • 第三次握手:客户端收到服务器的SYN+ACK报文段。然后将Acknowledgment Number设置为y+1,向服务器发送ACK报文段,这个报文段发送完毕以后,客户端和服务器端都进入ESTABLISHED状态,完成TCP三次握手。

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的机制,它可以在传输数据后仍然保持连接,当客户端需要再次获取数据时,直接使用刚刚空闲下来的连接而不需要再次握手。

图片 4

Okhttp支持5个并发KeepAlive,默认链路生命为5分钟(链路空闲后,保持存活的时间)。

TCP四次分手

当客户端和服务器通过三次握手建立了TCP连接以后,当数据传送完毕,断开连接就需要进行TCP四次分手:

  • 第一次分手:主机1(可以使客户端,也可以是服务器端),设置Sequence Number和Acknowledgment
    Number,向主机2发送一个FIN报文段;此时,主机1进入FIN_WAIT_1状态;这表示主机1没有数据要发送给主机2了;
  • 第二次分手:主机2收到了主机1发送的FIN报文段,向主机1回一个ACK报文段,Acknowledgment Number为Sequence
  • 第三次分手:主机2向主机1发送FIN报文段,请求关闭连接,同时主机2进入LAST_ACK状态;
  • 第四次分手:主机1收到主机2发送的FIN报文段,向主机2发送ACK报文段,然后主机1进入TIME_WAIT状态;主机2收到主机1的ACK报文段以后,就关闭连接;此时,主机1等待2MSL后依然没有收到回复,则证明Server端已正常关闭,那好,主机1也可以关闭连接了。

来看下面的图加强下理解:

图片 5

TAG标签:
版权声明:本文由金沙澳门官网4166发布于中国史,转载请注明出处:OkHttp3源码分析,复用连接池