【Android】OkHttp 源码剖析系列(四)——连接的建立概述


前面的文章分析完了 OkHttp 中的缓存机制,现在让我们继续来研究其在 ConnectInterceptor 中所进行的连接建立的相关原理。由于连接建立的过程涉及到很多在 OkHttp 中非常重要的机制,因此将分为多篇文章进行介绍,这篇文章主要是对连接建立的大体流程进行介绍。

本源码剖析系列基于 OkHttp 3.14

文章目录:

【Android】OkHttp 源码剖析系列(一)——请求的发起及拦截器机制概述

【Android】OkHttp 源码剖析系列(二)——拦截器大体流程分析

【Android】OkHttp 源码剖析系列(三)——缓存机制

【Android】OkHttp 源码剖析系列(四)——连接的建立概述

【Android】OkHttp 源码剖析系列(五)——路由选择与代理机制

【Android】OkHttp 源码剖析系列(六)——连接复用机制及连接的建立

【Android】OkHttp 源码剖析系列(七)——请求的发起及响应的读取

连接建立流程概述

ConnectInterceptor.intercept 方法中真正实现了连接的建立的代码如下:

根据上面的代码我们可以推测,这个 Exchange 类与我们的连接是有一些关系的,真正连接的建立过程在 transmitter.newExchange 中实现。

我们看到 transmitter.newExchange 方法:

获取连接

上面首先通过 exchangeFinder.find 方法进行了对 ExchangeCodec 的查找,找到对应的 ExchangeCodec 对象,之后通过这个 codec 对象构建了一个 Exchange 对象并返回

那么什么是 ExchangeCodec 对象呢?我们先看到 exchangeFinder.find 方法:

可以看到这里调用到了 findHealthyConnection 方法从而获取 RealConnection 对象,看来这个就是我们的连接了,之后调用了 RealConnection.newCodec 方法获取 ExchangeCodec 对象。

寻找可用连接

我们先看到 findHealthyConnection 方法:

可以看到这里是一个循环,不断地在调用 findConnection 方法寻找连接,若找不到 Healthy(可用)的连接,则继续循环直到找到为止。

寻找连接

我们先看到 findConnection 方法:

这个寻找连接的过程是非常复杂的,主要是下列几个步骤:

  1. 尝试获取 transmitter 中已经存在的连接,也就是当前 Call 之前创建的连接。
  2. 若获取不到,则尝试从连接池中调用 transmitterAcquirePooledConnection 方法获取连接,传入的 routes 参数为 null
  3. 若仍获取不到连接,判断是否需要路由选择,如果需要,调用 routeSelector.next 进行路由选择
  4. 如果进行了路由选择,则再次尝试从连接池中调用 transmitterAcquirePooledConnection 方法获取连接,传入的 routes 为刚刚路由选择后所获取的路由列表
  5. 若仍然获取不到连接,则调用 RealConnection 的构造函数创建新的连接,并对其执行 TCP + TLS握手。
  6. TCP + TSL握手之后,会再次尝试从连接池中通过 transmitterAcquirePooledConnection 方法获取连接,这种情况只会出现在一个 Host 对应多个并发连接的情况下。(因为 HTTP/2 支持了多路复用,使得多个请求可以并发执行,此时可能有其他使用该 TCP 连接的请求也创建了连接,就不需要重新创建了)
  7. 若最后一次从连接池中获取连接获取成功,会释放之前创建的连接的相关资源。
  8. 若仍获取不到,则将该连接放入连接池,并将其设置为 transmitter 的连接。

可以看到,寻找连接的过程主要被分成了三种行为,分别是

  • 尝试获取 transmitter 中已经分配的连接
  • 尝试从线程池中调用 transmitterAcquirePooledConnection 获取连接
  • 创建新连接。

有点类似图片加载的三级缓存,显然自上而下是越来越消耗资源的,因此 OkHttp 更偏向于前面直接能够获取到连接,尤其是尝试从连接池进行获取连接这一操作进行了三次

不过我们现在只是知道了大体流程,还有许多疑问没有解开。比如路由选择是怎样的?OkHttp 中的连接池是如何实现的?连接的建立过程是如何实现的?等等疑问都还没有解开,我们将在后续文章中介绍到。

判断连接是否可用

我们接着看看 RealConnection.isHealthy 的实现,看看它是如何判断一个连接是否可用的:

可以看到,上面主要是对 SocketHTTP2连接Stream 进行了检测,从而判断该连接是否可用。

什么是 Exchange

现在我们已经知道了连接究竟是如何寻找到的,现在让我们回到 Exchange 类,让我们研究一下究竟什么是 Exchange,它是用来做什么的。

让我们先从它的 JavaDoc 看起:

Transmits a single HTTP request and a response pair. This layers connection management and events
on {@link ExchangeCodec}, which handles the actual I/O.

可以看到,这里讲到,Exchange 是一个用于发送 HTTP 请求和读取响应的类,而真正进行 I/O 的类是它的一个成员变量——ExchangeCodec 。在 Exchange 中暴露了许多对 Stream 进行读写的方法,如 writeRequestHeaderscreateRequestBody 等等,在 CallServerInterceptor 中就会通过 Exchange ,向服务器发起请求,并读取其所返回的响应。

什么是 ExchangeCodec

让我们看看 ExchangeCodec 又是什么:

可以看到,它仅仅是个接口,根据上面的 JavaDoc 可以看出,它的作用是用于对请求进行编码,以及对响应进行解码

我们看看它有哪些实现类,通过 Android Studio 我们可以很容易找到它有如下两个实现类:

  • Http1ExchangeCodec
  • Http2ExchangeCodec

看得出来,OkHttp 采用了一种非常典型的面向接口编程,将对 Http 请求的编码及解码等功能抽象成了接口,再通过不同的实现类来实现将相同的 Request 对象编码为 HTTP1 及 HTTP2 的格式的数据,将 HTTP1 及 HTTP2 格式的数据解码为相同格式的 Response 对象。通过这样的一种面向接口的设计,大大地提高了 OkHttp 的可扩展性,可以通过实现接口的形式对更多的应用层进行支持。

什么是 Transmitter

接下来我们看看贯穿了我们整个请求流程的 Transimitter,究竟是一个用来做什么的类。我们先从 JavaDoc 入手:

Bridge between OkHttp's application and network layers. This class exposes high-level application
layer primitives: connections, requests, responses, and streams.

根据上面的注释可以看出,Transmitter 是一座 OkHttp 中应用层与网络层沟通的桥梁。就像我们之前的连接创建,就是在应用层通过了 transmitter.newExchange 方法来通知网络层进行 Exchange 的获取,并返回给应用层。那么 Transmitter 是什么时候创建的呢?

可以看到,它是在 RealCall 被创建的时候进行创建的,也就是说一个 Trasmitter 对应了一个 Call。这个 Call 在应用层通过 trasnmitter 与它的网络层进行通信。


Android Developer in GDUT