OkHttp机制梳理

629 阅读5分钟

使用方式

创建一个OkHttpClient对象,一个Request对象,然后利用他们创建一个Call对象,最后调用同步请求execute()方法或者异步请求enqueue()方法来拿到Response

OkHttpClient client = new OkHttpClient();
Request request = new Request.Builder()
  .url("https://github.com/")
  .build();
RealCall call = client.newCall(request)
//同步请求
Response response = client.newCall(request).execute();

//异步请求
client.newCall(request).enqueue();

各步骤解析

构造OkHttpClient

它使用Builder模式来配置各种参数,如添加拦截器、代理、SSL、超时时间等,然后创建RealCall实例来处理具体的请求。

OkHttpClient client = new OkHttpClient.Builder()  
        .dispatcher(dispatcher)  
        .addNetworkInterceptor(new SignInterceptor())  
        .addNetworkInterceptor(new NetworkInterceptor())  
        .eventListenerFactory(NetworkListener.get())  
        .build();

构造Request

只有四个参数,分别是请求URL请求方法请求头请求体

class Request internal constructor(
  @get:JvmName("url") val url: HttpUrl,
  @get:JvmName("method") val method: String,
  @get:JvmName("headers") val headers: Headers,
  @get:JvmName("body") val body: RequestBody?,
  internal val tags: Map<Class<*>, Any>
) 

构造RealCall

interface Call : Cloneable {
  /** 返回发起此调用的原始请求 */
  fun request(): Request
  /**
   * 同步请求,立即执行。
   * 
   * 抛出两种异常:
   * 1. 请求失败抛出IOException;
   * 2. 如果在执行过一回的前提下再次执行抛出IllegalStateException;*/
  @Throws(IOException::class)
  fun execute(): Response

  /**
   * 异步请求,将请求安排在将来的某个时间点执行。
   * 如果在执行过一回的前提下再次执行抛出IllegalStateException */
  fun enqueue(responseCallback: Callback)

  /** 取消请求。已经完成的请求不能被取消 */
  fun cancel()

  /** 是否已被执行  */
  fun isExecuted(): Boolean

  /** 是否被取消   */
  fun isCanceled(): Boolean

  /** 一个完整Call请求流程的超时时间配置,默认选自[OkHttpClient.Builder.callTimeout] */
  fun timeout(): Timeout

  /** 克隆这个call,创建一个新的相同的Call */
  public override fun clone(): Call

  /** 利用工厂模式来让 OkHttpClient 来创建 Call对象 */
  fun interface Factory {
    fun newCall(request: Request): Call
  }
}

Call接口的具体实现类,是应用与网络层之间的连接桥,包含OkHttpClientRequest信息。 在OkHttpClient中调用创建

override fun newCall(request: Request): Call = RealCall(this, request, forWebSocket = false)

同步execute或异步enqueue

构造RealCall之后就可以通过该类进行同步请求 execute() 或 异步请求 enqueue()

// 同步执行
override fun execute(): Response {  
  try {  
    client.dispatcher.executed(this)  
    return getResponseWithInterceptorChain()  
  } finally {  
    client.dispatcher.finished(this)  
  }  
}
// 异步执行
override fun enqueue(responseCallback: Callback) {  
  client.dispatcher.enqueue(AsyncCall(responseCallback))  
}

同步请求

realCall.execute()在当前线程直接执行,通过getResponseWithInterceptorChain()构造责任链获取Response

override fun execute(): Response {  
  try {  
    return getResponseWithInterceptorChain()  
  } 
}

异步请求

realCall.enqueue()时构造AsyncCall,实际上就是一个Runnable,有个executeOn方法,在Dispatcher中调度使用线程池中线程执行该Runnable,run中的逻辑与同步请求基本一致

fun executeOn(executorService: ExecutorService) {  
  try {  
    executorService.execute(this)  
  } 
}

override fun run() {  
  threadName("OkHttp ${redactedUrl()}") {  
    try {  
      val response = getResponseWithInterceptorChain()  
    } 
  }  
}
异步请求的执行策略

Dispatcher负责异步请求的执行策略

1、三个队列 readyAsyncCalls(准备中的异步调用)、runningAsyncCalls(运行中的异步调用)、runningSyncCalls(运行中的同步调用)。Dispatcher主要是负责异步请求的执行策略,所以最主要的还是readyAsyncCalls和runningAsyncCalls

private val readyAsyncCalls = ArrayDeque<AsyncCall>()  // 等待队列
private val runningAsyncCalls = ArrayDeque<AsyncCall>()  // 运行中的异步请求
private val runningSyncCalls = ArrayDeque<RealCall>()   // 运行中的同步请求

2、线程池配置 这种线程池配置适合处理大量短生命周期的异步任务,因为当任务提交时,如果有空闲线程则复用,否则创建新线程,空闲超过60秒则回收。

executorService = ThreadPoolExecutor(
    0,            // 核心线程数
    Int.MAX_VALUE, // 最大线程数(实际受maxRequests限制)
    60, SECONDS,   // 空闲线程存活时间
    SynchronousQueue() // 直接传递任务
)

3、promoteAndExecute promoteAndExecute方法是关键,它负责将准备队列中的任务提升到运行队列,并提交给线程池执行。

4、 执行流程 当一个异步请求被enqueue时,首先被加入readyAsyncCalls队列。

fun enqueue(call: AsyncCall) {
    synchronized {
        readyAsyncCalls.add(call)
    }
    promoteAndExecute() // 触发任务调度
}

然后,promoteAndExecute被调用,检查当前运行中的请求数是否未超过maxRequests,并且每个主机的请求数未超过maxRequestsPerHost。如果条件满足,任务被移动到runningAsyncCalls,并通过线程池执行。

private fun promoteAndExecute(): Boolean {  
  this.assertThreadDoesntHoldLock()  
  
  val executableCalls = mutableListOf<AsyncCall>()  
  val isRunning: Boolean  
  synchronized(this) {  
    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()  
      executableCalls.add(asyncCall)  
      runningAsyncCalls.add(asyncCall)  
    }  
    isRunning = runningCallsCount() > 0  
  }  
  
  for (i in 0 until executableCalls.size) {  
    val asyncCall = executableCalls[i]  
    asyncCall.executeOn(executorService)  
  }  
  
  return isRunning  
}

这部分即通过线程池执行AsyncCall逻辑

for (i in 0 until executableCalls.size) {  
    val asyncCall = executableCalls[i]  
    asyncCall.executeOn(executorService)  
  }  

请求前的拦截器

前面的execute或者enqueue最后都会到getResponseWithInterceptorChain中构造责任链,需要经过多个拦截器处理,最后一个CallServerInterceptor拦截器中会执行真正的网络请求

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)  // 最终网络IO
  
  val chain = RealInterceptorChain(  
      call = this,  
      interceptors = interceptors,  
      index = 0,  
      exchange = null,  
      request = originalRequest,  
      connectTimeoutMillis = client.connectTimeoutMillis,  
      readTimeoutMillis = client.readTimeoutMillis,  
      writeTimeoutMillis = client.writeTimeoutMillis  
  )  
  val response = chain.proceed(originalRequest)  
  return response  
}
链式调用机制
sequenceDiagram
    participant C as Client
    participant IC as InterceptorChain
    participant I1 as Interceptor1
    participant I2 as Interceptor2
    participant NC as NetworkCall

    C->>IC: 发起请求
    IC->>I1: proceed()
    I1->>IC: 修改请求
    IC->>I2: 创建新Chain传递请求
    I2->>NC: 最终执行网络请求
    NC-->>I2: 返回原始响应
    I2-->>IC: 返回处理后的响应
    IC-->>C: 最终响应结果

RealInterceptorChain中会创建了新的chain传给intercept,在每个interceptor#intercept中会调用chain的proceed传入新的request

// Chain的proceed会创建新的chain并传递给下一个interceptor
override fun proceed(request: Request): Response {
    // 创建下一个链节点
    val next = copy(index = index + 1, request = request)
    // 获取当前拦截器
    val interceptor = interceptors[index]
    // 执行拦截逻辑
    return interceptor.intercept(next)
}

LoggingInterceptor封装了新的request,将其传回给chain,在chain.proceed中会将request传递给下一个interceptor

class LoggingInterceptor : Interceptor {
    override fun intercept(chain: Chain): Response {
	    // 修改request
        val request = chain.request().newBuilder()
            .addHeader("X-Request-Time", System.currentTimeMillis().toString())
            .build()
        val response = chain.proceed(modifiedRequest)
        // 修改response
        return response
	        .newBuilder()
			.cacheControl(CacheControl.FORCE_CACHE)
			.build()
    }
}
class NetworkMonitorInterceptor : Interceptor {
    override fun intercept(chain: Chain): Response {
        val connection = chain.connection()!!
        monitor.trackConnection(connection)
        return chain.proceed(chain.request())
    }
}
设计要点
  1. 不可变设计:通过copy()方法创建新chain实例保持线程安全
  2. 状态隔离:每个chain实例维护独立的index/calls计数器
  3. 防御性校验:通过严格检查保证拦截器实现符合规范
  4. 上下文传递:通过exchange对象携带底层网络连接信息

总结

当用户构建OkHttpClient实例并调用newCall(request)时,会创建一个RealCall,然后通过Dispatcher来管理异步请求的执行。请求会经过配置的拦截器链,最终由CallServerInterceptor发送到服务器并获取响应,再逆向返回给调用方。