OkHttp 逮到你了!

138 阅读5分钟

一起养成写作习惯!这是我参与「掘金日新计划 · 4 月更文挑战」的第20天,点击查看活动详情

OkHttp

Dispatcher

  • 记录同步任务、异步任务及等待执行的异步任务。
  • 线程池管理异步任务。
  • 发起/取消网络请求API:execute、enqueue、cancel。

使用三个双端队列,管理任务。

# Dispatcher
//异步任务等待队列
private val readyAsyncCalls = ArrayDeque<AsyncCall>()
//异步任务队列
private val runningAsyncCalls = ArrayDeque<AsyncCall>()
//同步任务队列
private val runningSyncCalls = ArrayDeque<RealCall>()

Note:为什么不使用LinkedList?因为LinkedList内存不连续,查找起来慢。

放入运行时队列条件:当前请求数量小于64,单个host支持的最大并发量5.

放入时机:当有新的请求入队和当前请求完成后会调用finish,并从readyAsyncCalls中添加新的任务到运行时队列,最后交到线程池中去执行。

线程池

executorServiceOrNull = ThreadPoolExecutor(0, Int.MAX_VALUE, 60, TimeUnit.SECONDS,
        SynchronousQueue(), threadFactory("OkHttp Dispatcher", false))
  }

核心线程数0,非核心线无穷大,阻塞队列SynchronousQueue(当添加一个元素时,必须等待一个消费线程取出它,否则一直阻塞)。
实际上这个线程池就是newCacheThreadPool,短时间内可创建无限多的线程,保障最大吞吐量。

拦截器

#RealCall
fun getResponseWithInterceptorChain(): Response {
    //创建拦截器数组
    val interceptors = mutableListOf<Interceptor>()
    //添加应用拦截器
    interceptors += client.interceptors
    //添加重试和重定向拦截器
    interceptors += RetryAndFollowUpInterceptor(client)
    //添加桥接拦截器
    interceptors += BridgeInterceptor(client.cookieJar)
    //添加缓存拦截器
    interceptors += CacheInterceptor(client.cache)
    //添加连接拦截器
    interceptors += ConnectInterceptor
    if (!forWebSocket) {
      //添加网络拦截器
      interceptors += client.networkInterceptors
    }
    //添加请求拦截器
    interceptors += CallServerInterceptor(forWebSocket)

    //创建责任链
    val chain = RealInterceptorChain(interceptors, transmitter, null, 0, originalRequest, this,
        client.connectTimeoutMillis, client.readTimeoutMillis, client.writeTimeoutMillis)
    ...
    try {
      //启动责任链
      val response = chain.proceed(originalRequest)
      ...
      return response
    } catch (e: IOException) {
      ...
    }
  }


一级一级的向下调用,最终会执行到CallServerInterceptor的intercept方法,此方法会将网络响应的结果封装成一个Response对象并return。之后沿着责任链一级一级的回溯,最终就回到getResponseWithInterceptorChain方法的返回。

拦截器分类

1.应用拦截器  

拿到的是原始请求,可以添加一些自定义header、通用参数、参数加密、网关接入等等。

2.RetryAndFollowUpInterceptor  

处理错误重试和重定向。 内部是个死循环,抛出IO或Route异常时就会重新执行,执行重试操作。 获取到Response后会根据响应码处理30X重定向(拿到响应头中的Location 的url,然后重新创建Request,发起请求)。

3.BridgeInterceptor

主要工作是为请求添加cookie、添加固定的header,比如Host、Connection、Content-Length、Content-Type、User-Agent等等,然后保存响应结果的cookie,如果响应使用gzip压缩过,则还需要进行解压。

4.CacheInterceptor

缓存拦截器,如果命中缓存则不会发起网络请求。
OKHttp默认只支持get请求的缓存,使用request URL作为缓存的key.
实现原理是Http的Etag & If-None-Match:服务器生成Etag返回给客户端,客户端发起第二次请求时发送一个If-None-Match,而它的值就是Etag的值。然后,服务器会比对这个客服端发送过来的Etag是否与服务器的相同,如果相同,就将If-None-Match的值设为false,返回状态为304,客户端继续使用本地缓存,不解析服务器返回的数据;如果不相同,就将If-None-Match的值设为true,返回状态为200,客户端重新解析服务器返回的数据。

5.ConnectInterceptor

连接拦截器,内部会维护一个连接池,负责连接复用、创建连接(三次握手等等)、释放连接以及创建连接上的socket流。 默认最多五个闲置连接,闲置连接最大存活时间5min。
第一次向连接池中put socket时,便会开启一个清理任务,该任务会将存活时间超过5min的闲置连接remove,或者当前闲置连接数超过5个时,会remove闲置最久的那个连接。

6.networkInterceptors(网络拦截器)

用户自定义拦截器,通常用于监控网络层的数据传输。

7.CallServerInterceptor

请求拦截器,在前置准备工作完成后,真正发起了网络请求。

addInterceptor与addNetworkInterceptor的区别

首先,应用拦截器在RetryAndFollowUpInterceptor和CacheInterceptor之前,所以一旦发生错误重试或者网络重定向,网络拦截器可能执行多次,因为相当于进行了二次请求,但是应用拦截器永远只会触发一次。另外如果在CacheInterceptor中命中了缓存就不需要走网络请求了,因此会存在短路网络拦截器的情况。

最后,从使用场景看,应用拦截器因为只会调用一次,通常用于统计客户端的网络请求发起情况;而网络拦截器一次调用代表了一定会发起一次网络通信,因此通常可用于统计网络链路上传输的数据。

HTTP2

二进制

HTTP/1 和 HTTP/2 的主要区别之一,就是 HTTP/2 是一个二进制、基于数据报的协议,而 HTTP/1 是完全基于文本的,基于文本的协议方便人类阅读,但是机器解析起来比较困难。

多路复用

HTTP/1 是一种同步的、独占的请求—响应协议,客户端发送 HTTP/1 消息,然后服务器返回 HTTP/1 响应,为了能更快地收发更多数据,HTTP/1 的解决办法就是打开多个连接,并且使用资源合并,以减少请求数,但是这种解决办法会引入其他问题和带来性能开销。

而 HTTP/2 允许在单个连接上同时执行多个请求,每个 HTTP 请求或响应使用不同的流,以流的方式多路复用。