网络原理|OkHttp源码解析

OkHttp 是什么

  • 官方解释:适用于 Android 和 Java 应用程序的 HTTP 和 HTTP / 2 的客户端
  • OKHTTP 是一个高效的 HTTP 库
  • 网络请求发展:HttpURLConnection —> Apache HTTP Client —> Volley —-> okHttp
  • SPDY -> HTTP 2
HTTP 是现代应用网络的方式。这就是我们交换数据和媒体的方式。有效地执行 HTTP 可以加快您的负载并节省带宽。
OkHttp 是一个默认有效的 HTTP 客户端:
1. HTTP / 2 支持允许对同一主机的所有请求共享套接字。 2. 连接池减少了请求延迟(如果 HTTP / 2 不可用)。 3. 透明 GZIP 缩小了下载大小。 4. 响应缓存可以完全避免网络重复请求。 5. 当网络很麻烦时,OkHttp 坚持不懈:它将从常见的连接问题中无声地恢复。 6. 如果您的服务有多个 IP 地址,如果第一次连接失败,OkHttp 将尝试备用地址。这对于 IPv4 + IPv6 和 冗余数据中心中托管的服务是必需的。 7. OkHttp 支持现代 TLS 功能( TLS1.3,ALPN,证书固定 )。它可以配置为回退以实现广泛的连接。

使用 OkHttp 很容易。它的 请求 / 响应 API 采用流畅的构建器和不变性设计。它支持同步阻塞调用和带回调的异步调用。
OkHttp 支持 Android 5.0+(API 级别 21+)和 Java 8+。
OkHttpClient 该类继承了接口 Cloneable, Call.Factory, WebSocket.Factory
Call
  • execute() 是同步方法
  • enqueue(responseCallback: Callback) 是异步方法
  • 同步请求生成的是 RealCall 对象,而异步请求生成的是 AsyncCall 对象。AsyncCall 说到底其实就是 Runnable 的子类
  • 如果可以执行,则对当前请求添加监听器等操作,然后将请求 Call 对象放入调度器 Dispatcher 中。最后由拦截器链中的各个拦截器来对该请求进行处理,返回最终的 Response
Dispatcher调度器
  • Dispatcher 是保存同步和异步 Call 的地方,并负责执行异步 AsyncCall
  • 针对同步请求,Dispatcher 使用了一个 Deque 保存了同步任务;针对异步请求,Dispatcher 使用了两个 Deque,一个保存准备执行的请求,一个保存正在执行的请求,为什么要用两个呢?因为 Dispatcher 默认支持最大的并发请求是 64 个,单个 Host 最多执行 5 个并发请求,如果超过,则 Call 会先被放入到 readyAsyncCall 中,当出现空闲的线程时,再将 readyAsyncCall 中的线程移入到 runningAsynCalls 中,执行请求,以下是流程图
    网络原理|OkHttp源码解析
    文章图片

WebSocket
  • 网络复习
Transmitter 应用层和网络层间的桥梁
Interceptor
  • BridgeInterceptor:为请求(request before)添加请求头,为响应(Response Before)添加响应头
    采用 Gzip 压缩后可以大幅减少传输内容大小,这样可以提高传输速度,减少流量的使用。OkHttp 是默认支持 Gzip 解压缩的,不需要额外配置的,若用户自己添加了压缩则需要自己添加解压缩。客户端发起请求时在请求头里增加 Accept-Encoding:gzip,服务端响应时在返回的头信息里增加 Content-Encoding:gzip,这表示传输的数据是采用 Gzip 压缩的。
  • CallServerInterceptor: 该拦截器就是利用 HttpCodec 完成最终请求的发送。
  • ConnectionInterceptor:获得一个安全有效的连接。
  • CacheInterceptor:处理 ETag、Last-Modified 等缓存相关的请求头,若缓存可用,则使用 Gzip 进行处理,CacheStrategy 是一个缓存策略类,该类告诉 CacheInterceptor 是使用缓存还是使用网络请求 (DiskLruCache)
根据缓存策略类返回的结果:
1.如果网络不可用并且无可用的有效缓存,则返回504错误; 2.继续,如果不需要网络请求,则直接使用缓存; 3.继续,如果需要网络可用,则进行网络请求; 4.继续,如果有缓存,并且网络请求返回 HTTP_NOT_MODIFIED,说明缓存还是有效的,则合并网络响应和缓存结果。同时更新缓存; 5.继续,如果没有缓存,则写入新的缓存;

缓存策略的判断流程:
1.没有缓存,直接网络请求; 2.如果是 https,但没有握手,直接网络请求; 3.不可缓存,直接网络请求; 4.请求头 nocache 或者请求头包含 If-Modified-Since 或者 If-None-Match,则需要服务器验证本地缓存是不是 还能继续使用,直接网络请求; 5.可缓存,并且 ageMillis + minFreshMillis < freshMillis + maxStaleMillis(意味着虽过期,但可用, 只是会在响应头添加 warning),则使用缓存; 6.缓存已经过期,添加请求头:If-Modified-Since 或者 If-None-Match,进行网络请求;

ConnectionSpec 具体的安全性 vs 连通性的决定是由ConnectionSpec实现的 JavaDoc OkHttp 尝试平衡两个相互竞争的要素:
连通性 (Connectivity):连接到尽可能多的服务器。这包括运行最新版本 boringssl 的服务器和不太过时的老版本 OpenSSL 的服务器。 连接的安全性 (Security):这包括远程web服务器证书验证,和对私密数据交换的强加密。

在与 HTTPS 服务器协商一个连接时,OkHttp 需要知道提供哪种 TLS 版本 (TLSversions) 和密码套件 (cipher suites)。一个希望最大化连通性的客户端会包含废弃的 TLS 版本和弱设计的密码套件。一个希望最大化安全性的严格的客户端将会限制只使用最新的 TLS 版本和最强的密码套件。
OkHttp 包含三种内置的连接策略:
MODERN_TLS:连接到最新的 HTTPS 服务器的安全配置。 COMPATIBEL_TLS: 连接到过时的 HTTPS 服务器的安全配置。 CLEARTEXT:用于 http:// 开头的 URL 的非安全配置。

默认情况下,OkHttp 将会尝试 MODERN_TLS 连接,如果当前配置失败,会退回到 COMPATIBLE_TLS 连接。
每种连接策略中,具体的 TLS 版本和密码套件在每个版本中都可能会变。例如,在 OkHttp2.2 中,我们禁用了 SSL 3.0,以应对 POODLE攻击;在 OkHttp2.3 中我们禁用了 RC4。同你的桌面 web 浏览器一样,使用最新的 OkHttp 版本是保证安全的最好方法。
你可以建立自己的连接策略,使用自定义的 TLS 版本和密码套件。
关于自签名证书 Retrofit 使用自签名证书进行 HTTPS 请求的教程
  • 若证书未经过审核,则需要去 CA 平台申请证书
整体结构 【网络原理|OkHttp源码解析】网络原理|OkHttp源码解析
文章图片

Kotlin知识点 由于新版本的 OkHttp 使用的是 Kotlin,所以此处同时记录关于 Kotlin 的知识点
  • companion object 类的内部类(伴生对象)的声明关键字,外部调用可以忽略内部类名,直接使用类名调用内部类的成员。声明时也可以省略内部类名,调用时使用 companion
class MyClass { companion object { } } val x = MyClass.Companion

参考文章
  • OkHttp 源码解析

    推荐阅读