OkHttp源码阅读记录

394 阅读6分钟

OkHttp的历史

  1. OKHttp最初是基于HttpClient的封装,因为HttpClient或是谷歌的提供的方案过于繁琐
  2. 后面Square完全移除了HttpClient等底层网络请求原生支持,完全从头开始进行了实现
  3. 目前成为了公认的业界网络请求方案

OkHttp的简单使用和调用链

val client = OkHttpClient()

val req = Request.Builder().url("https://www.baidu.com").build()

client.newCall(req).enqueue(object : Callback {
    override fun onFailure(call: okhttp3.Call, e: IOException) {
     
    }

    override fun onResponse(call: okhttp3.Call, response: okhttp3.Response) {
       
    }
})

OkHttpClient如他的名字一样,是一个请求中心负责请求的管理, Request是请求条件,可以传入请求体,header等信息;

我们从newCall(req)这边跟踪进去,会发现我们newCall创建的是RealCall对象

先看RealCall的构造函数,有三个参数

constructor(client: okhttp3.OkHttpClient, originalRequest: okhttp3.Request, forWebSocket: kotlin.Boolean)

client就是我们创建的OkHttpClient

originalRequest即是我们创建的request

forWebSocket这个参数主要是用于WebSocket双向通信的,OkHttp早期版本均一直为flase,最新版本开始支持WebSocket后开始有实际作用

我们来看看这个realCall在调用enqueue()阶段做了些什么

override fun enqueue(responseCallback: Callback) {
  check(executed.compareAndSet(false, true)) { "Already Executed" }

  callStart()
  client.dispatcher.enqueue(AsyncCall(responseCallback))
}

这个callStart()和请求本身无关,是添加了一个eventListener,用于跟踪程序的错误,连接断开响应,TCP状态变化等事件

我们继续查看client.dispatcher.enqueue(AsyncCall(responseCallback))

我们先看Dispatcher,Dispatcher是OKHttpClient内的一个对象,用于多线程的管理的工具, 有几个属性

maxRequests:最大请求数量

maxRequestsPerHost:对同一主机最大请求数量

我们接着看其enqueue()阶段做了些什么

private val readyAsyncCalls = ArrayDeque<AsyncCall>()
internal fun enqueue(call: AsyncCall) {
  synchronized(this) {
    readyAsyncCalls.add(call)

    if (!call.call.forWebSocket) {
      val existingCall = findExistingCallWithHost(call.host)
      if (existingCall != null) call.reuseCallsPerHostFrom(existingCall)
    }
  }
  promoteAndExecute()
}

可见我们的请求,被放入了一个readyAsyncCalls的双向队列当中; 之后对AsyncCall中访问同一主机数量的变量进行更新 再然后调用promoteAndExecute()

promoteAndExecute()这个方法名可以推举出他的行为:

1.推举出可执行的call

2.将这些call进行执行

private fun promoteAndExecute(): Boolean {
  this.assertThreadDoesntHoldLock()

  val executableCalls = mutableListOf<AsyncCall>()
  val isRunning: Boolean
  synchronized(this) {
      // 遍历这些Call
    val i = readyAsyncCalls.iterator()
    while (i.hasNext()) {
      val asyncCall = i.next()

        // 检查是到达最大请求数量和同一主机最大请求数
      if (runningAsyncCalls.size >= this.maxRequests) break // Max capacity.
      if (asyncCall.callsPerHost.get() >= this.maxRequestsPerHost) continue // Host max capacity.

      i.remove()
      asyncCall.callsPerHost.incrementAndGet()
      // 放入被选中的calls队列
      executableCalls.add(asyncCall)
      // 放入正在被执行的calls队列
      runningAsyncCalls.add(asyncCall)
    }
    isRunning = runningCallsCount() > 0
  }
    if (executorService.isShutdown) {
      for (i in 0 until executableCalls.size) {
        val asyncCall = executableCalls[i]
        asyncCall.callsPerHost.decrementAndGet()

        synchronized(this) {
          runningAsyncCalls.remove(asyncCall)
        }

        asyncCall.failRejected()
      }
      idleCallback?.run()
    } else {
      for (i in 0 until executableCalls.size) {
        val asyncCall = executableCalls[i]
        asyncCall.executeOn(executorService)
      }
    }

    return isRunning

可见最终执行请求走的是asyncCall.executeOn方法

fun executeOn(executorService: ExecutorService) {
  client.dispatcher.assertThreadDoesntHoldLock()

  var success = false
  try {
    executorService.execute(this)
    success = true
  } catch (e: RejectedExecutionException) {
    failRejected(e)
  } finally {
    if (!success) {
      client.dispatcher.finished(this) // This call is no longer running!
    }
  }
}

executeOn()是在线程池中执行自己的runnable

override fun run() {
  threadName("OkHttp ${redactedUrl()}") {
    var signalledCallback = false
    timeout.enter()
    try {
      val response = getResponseWithInterceptorChain()
      signalledCallback = true
      responseCallback.onResponse(this@RealCall, response)
    } catch (e: IOException) {
      if (signalledCallback) {
        // Do not signal the callback twice!
        Platform.get().log("Callback failure for ${toLoggableString()}", Platform.INFO, e)
      } else {
        responseCallback.onFailure(this@RealCall, e)
      }
    } catch (t: Throwable) {
      cancel()
      if (!signalledCallback) {
        val canceledException = IOException("canceled due to $t")
        canceledException.addSuppressed(t)
        responseCallback.onFailure(this@RealCall, canceledException)
      }
      throw t
    } finally {
      client.dispatcher.finished(this)
    }
  }
}

自此请求结束了,getResponseWithInterceptorChain()获取到了请求结果后,对我们传入的responseCallback进行响应

总结一下请求的调用链

  1. client.newCall(req) 这时候创建了一个RealCall
  2. 调用enqueue(),既是调用RealCallenqueue()
  3. enqueue中调用了OkHttpClient的dispatcher的enqueue
  4. dispatcher的enqueue将calls传入readyAsyncCalls队列中,并执行promoteAndExecute()
  5. promoteAndExecute()将符合执行条件的calls进行执行请求
  6. 执行请求本质上是调用call的executeOn(executorService: ExecutorService),executeOn(executorService: ExecutorService)是用传入的线程池执行内部的runnable接口
  7. runnable内执行请求,获取返回后响应我们传入的回调responseCallback

参数介绍

OKHttp将连接,请求获取返回全部业务都集成在了方法getResponseWithInterceptorChain()中,为了更好的了解其中都做了什么,我们先看下OkHttpClient的初始化参数

  • dispatcher 【线程调度工具】
  • interceptors 【拦截器】
  • networkInterceptors 【网络拦截器】
  • connectionPool 连接池【负责连接的批量管理,如果要进行连接,会先查看其中有没有可复用的连接,进行复用,没有才进行创建,超过一定时间可以自动进行销毁,可以免去握手过程从而加快请求速度,和节约资源】
  • eventListenerFactory【事件监听器】
  • retryOnConnectionFailure【自动重试】
  • authenticator【认证工具】
  • followRedirects【是否根据服务器返回重定向】
  • followSslRedirects【当重定向后的地址协议发生了变化,是否依旧重定向】
  • cookieJar【cookie罐,提供接口支持给我们存储cookie和加载cookie】
  • dns【DNS解析,默认实现是调用Java原生InetAddress.getAllByName()方法根据域名获取ip地址数组,可以自实现】
  • cache【缓存】
  • proxy【代理,默认使用proxySelector获取到的配置,可以配置代理服务器地址】
  • sslSocketFactory【用于创建加密Socket连接】
  • socketFactory【用于创建Socket连接】
  • x509TrustManager【原生类,证书验证管家】
  • connectionSpecs【连接规范,tcp建立连接要向服务器发送支持的协议和加密算法等信息,可以在此自定义】
  • protocols【支持的HTTP协议版本号】
  • hostnameVerifier【主机名验证器】
  • certificatePinner【验证图钉,在验证证书合法逻辑之外可以加Hash验证】
  • certificateChainCleaner【清理证书】
  • pingInterval【心跳间隔,http2/websocket用】 authenticator使用方式示例:
OkHttpClient().newBuilder()
    .authenticator(object : Authenticator {
        override fun authenticate(route: Route?, response: Response): Request? {
            // 执行获取最新token业务
            ...
            // 更新请求信息后返回
            return response.request.newBuilder()
                .header("Authorization", "Bearer ${getToken()}")
                .build()
        }
    })

现在我们回到getResponseWithInterceptorChain()

@Throws(IOException::class)
internal fun getResponseWithInterceptorChain(): Response {
  // Build a full stack of interceptors.
  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(
      call = this,
      interceptors = interceptors,
      index = 0,
      exchange = null,
      request = originalRequest,
      connectTimeoutMillis = client.connectTimeoutMillis,
      readTimeoutMillis = client.readTimeoutMillis,
      writeTimeoutMillis = client.writeTimeoutMillis,
    )

  var calledNoMoreExchanges = false
  try {
    val response = chain.proceed(originalRequest)
    if (isCanceled()) {
      response.closeQuietly()
      throw IOException("Canceled")
    }
    return response
  } catch (e: IOException) {
    calledNoMoreExchanges = true
    throw noMoreExchanges(e) as Throwable
  } finally {
    if (!calledNoMoreExchanges) {
      noMoreExchanges(null)
    }
  }
}

工作链的实现

先引入一个概念,链式工作, A->B->C,按照从前往后执行过去,再A<-B<-C执行回来,中间的每个环节有前置工作,有后置工作

getResponseWithInterceptorChain()中创建了interceptors这个链条,并且传入了RealInterceptorChain,这里我们可以把RealInterceptorChain抽象为这个链条本身,interceptors是链条的队列,其他参数为这个链条的整体参数

我们直接从入口进行查看

val response = chain.proceed(originalRequest)

点进去看proceed的实现

@Throws(IOException::class)
override fun proceed(request: Request): Response {
  check(index < interceptors.size)

  calls++

  if (exchange != null) {
    check(exchange.finder.routePlanner.sameHostAndPort(request.url)) {
      "network interceptor ${interceptors[index - 1]} must retain the same host and port"
    }
    check(calls == 1) {
      "network interceptor ${interceptors[index - 1]} must call proceed() exactly once"
    }
  }

  // Call the next interceptor in the chain.
  val next = copy(index = index + 1, request = request)
  val interceptor = interceptors[index]

  @Suppress("USELESS_ELVIS")
  val response =
    interceptor.intercept(next) ?: throw NullPointerException(
      "interceptor $interceptor returned null",
    )

  if (exchange != null) {
    check(index + 1 >= interceptors.size || next.calls == 1) {
      "network interceptor $interceptor must call proceed() exactly once"
    }
  }

  return response
}

这里调用了链条中的第一环也就是RetryAndFollowUpInterceptorintercept方法,并且传入了index+1后的参数next 我们点进去看下RetryAndFollowUpInterceptorintercept方法的具体实现如下

@Throws(IOException::class)
override fun intercept(chain: Interceptor.Chain): Response {
  val realChain = chain as RealInterceptorChain
  var request = chain.request
  val call = realChain.call
  var followUpCount = 0
  var priorResponse: Response? = null
  var newRoutePlanner = true
  var recoveredFailures = listOf<IOException>()
  while (true) {
    call.enterNetworkInterceptorExchange(request, newRoutePlanner, chain)

    var response: Response
    var closeActiveExchange = true
    try {
      if (call.isCanceled()) {
        throw IOException("Canceled")
      }

      try {
        response = realChain.proceed(request)
        newRoutePlanner = true
      } catch (e: IOException) {
        // An attempt to communicate with a server failed. The request may have been sent.
        if (!recover(e, call, request, requestSendStarted = e !is ConnectionShutdownException)) {
          throw e.withSuppressed(recoveredFailures)
        } else {
          recoveredFailures += e
        }
        newRoutePlanner = false
        continue
      }

      // Clear out downstream interceptor's additional request headers, cookies, etc.
      response =
        response.newBuilder()
          .request(request)
          .priorResponse(priorResponse?.stripBody())
          .build()

        //  检查是否需要重定向
      val exchange = call.interceptorScopedExchange
      val followUp = followUpRequest(response, exchange)



      if (followUp == null) {
        if (exchange != null && exchange.isDuplex) {
          call.timeoutEarlyExit()
        }
        closeActiveExchange = false
        // 不需要重定向就返回结果
        return response
      }

      val followUpBody = followUp.body
      if (followUpBody != null && followUpBody.isOneShot()) {
        closeActiveExchange = false
        return response
      }

      response.body.closeQuietly()

      if (++followUpCount > MAX_FOLLOW_UPS) {
        throw ProtocolException("Too many follow-up requests: $followUpCount")
      }

      // 需要重定向 就更新req信息后从头开始
      request = followUp
      priorResponse = response
    } finally {
      call.exitNetworkInterceptorExchange(closeActiveExchange)
    }
  }
}

我们看见了

response = realChain.proceed(request)

这时候又调用了realChain.proceed(request), 但是这时候index已经加了1,所以这次执行等于执行了下一个环的intercept

所以可以把Interceptor的工作流程抽象如下

override fun intercept(chain: Interceptor.Chain): Response {
    // 执行前置工作
    ....
    // 执行中置工作 让下一环节执行前置工作,并向后传递让下下环境执行前置工作和向后传递..
    realChain.proceed(request)
    // 等待中置工作结束 执行后置工作

}

至此我们可以理解了这条工作链的实现方式

现在我们看看每个Interceptor都做了什么

RetryAndFollowUpInterceptor

RetryAndFollowUpInterceptor主要做两个工作

  1. Retry 重试
  2. FollowUp 重定向

实现重试的机制也简单

我们简化下结构

  while (true) {
  ... 
      try {
        // 执行请求
      } catch (e: IOException) {
      // 检查是否符合重试条件,是就 continue,
      // 不符合(比如配置不允许重试,404等)就抛出异常
        continue
      }
  }

就是循环中,trycatch捕获异常,如果有异常就执行continue,从头开始,这其中加了些额外环节,比如recover环节就是发生异常后是直接抛出异常,还是继续重试

BridgeInterceptor

这个环节比较简单 主要是给我们添加请求头信息,帮我们添加编码信息,contentLength数据长度等信息

CacheInterceptor

OkHttp的缓存实现

ConnectInterceptor

这个要展开说明下,先看下基础的结构

object ConnectInterceptor : Interceptor {
  @Throws(IOException::class)
  override fun intercept(chain: Interceptor.Chain): Response {
    val realChain = chain as RealInterceptorChain
    val exchange = realChain.call.initExchange(realChain)
    val connectedChain = realChain.copy(exchange = exchange)
    return connectedChain.proceed(realChain.request)
  }
}

这个结构比较特殊,没有了后置工作,因为这是请求环节的最后一环。

先看initExchange

internal fun initExchange(chain: RealInterceptorChain): Exchange {
  synchronized(this) {
    check(expectMoreExchanges) { "released" }
    check(!responseBodyOpen)
    check(!requestBodyOpen)
  }

  val exchangeFinder = this.exchangeFinder!!
  val connection = exchangeFinder.find()
  val codec = connection.newCodec(client, chain)
  val result = Exchange(this, eventListener, exchangeFinder, codec)
  this.interceptorScopedExchange = result
  this.exchange = result
  synchronized(this) {
    this.requestBodyOpen = true
    this.responseBodyOpen = true
  }

  if (canceled) throw IOException("Canceled")
  return result
}

这边将RealInterceptorChain转成了Exchange

codec意思是coder和decoder,编码解码器 这边根据我们请求的格式如HTTP1,HTTP2,给到不同的编码解码器,然后提供给Exchange

未完待续-