Okhttp之网络连接相关三大类RealConnection、ConnectionPool、StreamAllocation

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Okhttp之网络连接相关三大类RealConnection、ConnectionPool、StreamAllocation相关的知识,希望对你有一定的参考价值。

参考技术A Okhttp的浅层架构分析
Okhttp的责任链模式和拦截器分析
Okhttp之RetryAndFollowUpInterceptor拦截器分析
Okhttp之BridgeInterceptor拦截器分析
Okhttp之CacheInterceptor拦截器分析
Okhttp之ConnectInterceptor拦截器分析
Okhttp之网络连接相关三大类RealConnection、ConnectionPool、StreamAllocation
Okhttp之CallServerInterceptor拦截器分析
浅析okio的架构和源码实现

HTTP 1.1 默认启用长TCP连接,但所有的请求-响应都是按序进行的(这里的长连接可理解成半双工协议。即便是HTTP1.1引入了管道机制,也是如此)。复用同一个TCP连接期间,即便是通过管道同时发送了多个请求,服务端也是按请求的顺序依次给出响应的;而客户端在未收到之前所发出所有请求的响应之前,将会阻塞后面的请求(排队等待),这称为"队头堵塞"(Head-of-line blocking)。
HTTP2.0复用TCP连接则不同,虽然依然遵循请求-响应模式,但客户端发送多个请求和服务端给出多个响应的顺序不受限制,这样既避免了"队头堵塞",又能更快获取响应。在复用同一个TCP连接时,服务器同时(或先后)收到了A、B两个请求,先回应A请求,但由于处理过程非常耗时,于是就发送A请求已经处理好的部分, 接着回应B请求,完成后,再发送A请求剩下的部分。HTTP2.0长连接可以理解成全双工的协议。
HTTP2.0 使用 多路复用 的技术,多个 stream 可以共用一个 socket 连接。每个 tcp连接都是通过一个 socket 来完成的,socket 对应一个 host 和 port,如果有多个stream(即多个 Request) 都是连接在一个 host 和 port上,那么它们就可以共同使用同一个 socket ,这样做的好处就是 可以减少TCP的一个三次握手的时间。
在OKHttp里面,负责连接的是 RealConnection 。

RealConnection是Connection的实现类,代表着链接socket的链路,如果拥有了一个RealConnection就代表了我们已经跟服务器有了一条通信链路。与服务的三次握手也是在这里实现的。下面看看它的属性和构造函数。

下面看看核心方法connect():

connectTunnel()隧道链接

最终还是要调用到connectSocket():

1、创建Socket,非SOCKS代理的情况下,通过SocketFactory创建;在SOCKS代理则传入proxy手动new一个出来。
2、为Socket设置超时
3、完成特定于平台的连接建立
4、创建用于I/O的source和sink
至于代理的相关逻辑,这里暂时就不深究了,后续会再单独去了解。

继续看http2Connection.start()方法:

看看这个readerRunnable

从Realconnection调用connect()创建了socket连接之后(这里讨论走http2.0协议分支),创建了一个http2Connection 对象,启用了一个readerRunnable的线程,run()方法的主要工作是循环地执行reader.nextFrame()方法。
看看reader.nextFrame()干了啥:

再来重点看看 readHeaders(handler, length, flags, streamId)方法,因为在后面的CallServerInterceptor拦截器会追踪到,提前了解一下,是怎么读取response的headers的:

链接池,看名字就能联想到线程池之类的池设计,都是为了减少资源创建,提高资源复用率而设计的。连接池是用来管理http和http/2的链接复用,通过让同一个address将共享同一个connection,以便减少网络请求延迟。
成员变量和构造函数:

先搞明白那个清除cleanup(long now)方法:

pruneAndGetAllocationCount()方法:

再来看看清理的任务是什么时候执行的:

再来看看ConnectionPool的其他方法,看明白了这些方法也就大概了解了它的工作流程,get()方法:

connectionBecameIdle()方法:

deduplicate()方法:

evictAll()方法:

ConnectionPool的主要职责就是维护了一个RealConnection的双端队列,并且维护了一个定时清理空闲和多余connection的线程池,并提供了一些相应的操作方法来维护连接池的稳定性和提供相应的功能。

流分配,Connection是建立在Socket之上的物理通信信道,而Stream则是代表逻辑的流,至于Call是对一次请求过程的封装。之前也说过一个Call可能会涉及多个流(比如重定向或者auth认证等情况)。所以我们想一下,如果StreamAllocation要想解决上述问题,需要两个步骤,一是寻找连接,二是获取流。所以StreamAllocation里面应该包含一个Stream;还应该包含连接Connection。如果想找到合适的链接,还需要一个连接池ConnectionPool属性。所以应该有一个获取流的方法在StreamAllocation里面,还应该有完成请求任务的之后的方法来关闭流对象,还有终止和取消等方法,以及释放资源的方法。
成员变量及构造函数:

看到这些成员变量是不是很眼熟,就是之前讲过的链接以及连接池,路由这些,下面看看它的几个重要的方法,先看看在ConnectInterceptor里调用到的newStream()方法:

findHealthyConnection()方法

继续看下findConnection()方法:

1、循环获取connection实例,直到获取到健康可用的,获取实例的时候先找是否有已经存在的连接,如果有已经存在的连接,并且可以使用(!noNewStreams)则直接返回。
2、没有现成的就根据已知的address在connectionPool里面找,如果有连接,则返回
3、更换路由,更换线路,在connectionPool里面再次查找,如果有则返回。
4、如果以上条件都不满足则直接new一个RealConnection出来
5、new出来的RealConnection通过acquire关联到connection.allocations上
6、做去重判断,如果有重复的socket则关闭

其他方法暂时没用到,不做一一讲解,下篇分析最后一个拦截器CallServerInterceptor,最终跟服务器产生通信的阶段,结合这个拦截器再来重新组织起来看看这篇文章讲到的知识点。

以上是关于Okhttp之网络连接相关三大类RealConnection、ConnectionPool、StreamAllocation的主要内容,如果未能解决你的问题,请参考以下文章

okhttp-utils的封装之okhttp的使用

OKHttp源码解析之网络请求

okhttp 3.10连接复用原理

Android编程入门--开源框架OKHttp

卷积神经网络

Socket相关