Okhttp 的源碼閱讀

首先先看一下用法:

val client = OkHttpClient.Builder().build()
val request = Request.Builder()
	.url("https://www.baidu.com")
	.build()
val call = client.newCall(request)
call.enqueue(object : okhttp3.Callback {
    override fun onFailure(call: okhttp3.Call, e: IOException) {

    }

    override fun onResponse(call: okhttp3.Call, response: okhttp3.Response) {
        Log.e("TAG", "onResponse: ${response.body?.string()}")
    }
})

首先通過 builder 模式創建了一個 client。然後通過 newCall 方法創建了一個 Call ,最後通過這個 call 進行網絡請求。

newCall 方法中傳了一個參數,是 Request,這個 Request 是自己拼出來的。這個方法就是通過 request 創建一個 待用的網絡請求。

override fun newCall(request: Request): Call {
  return RealCall.newRealCall(this, request, forWebSocket = false)
}

// RealCall 
companion object {
    fun newRealCall(
      client: OkHttpClient,  originalRequest: Request,   forWebSocket: Boolean
    ): RealCall {
        //創建一個 RealCall 
        //client ,requet,webSocket一般情況是用不到						
      return RealCall(client, originalRequest, forWebSocket).apply {
          //Transmitter
        transmitter = Transmitter(client, this)
      }
    }
  }

在 newRealCall 方法中返回了一個 RealCall 對象,由此可知,RealCall 是 Call 的實現類。

enqueue 方法

call.enqueue(object : okhttp3.Callback {
    override fun onFailure(call: okhttp3.Call, e: IOException) {
    }
    override fun onResponse(call: okhttp3.Call, response: okhttp3.Response) {
        Log.e("TAG", "onResponse: ${response.body?.string()}")
    }
})

//RealCall 
  override fun enqueue(responseCallback: Callback) {
    synchronized(this) {
      check(!executed) { "Already Executed" }
      executed = true
    }
    transmitter.callStart()
    client.dispatcher.enqueue(AsyncCall(responseCallback))
  }

在 enqueue 中 將 callback 轉交給了 dispather,並創建了一個異步的Call 。下面分別看一下 dispatcher,enqueue 和 AsyncCall 分別是什麼東西

  • dispatcher

    val dispatcher: Dispatcher = builder.dispatcher
    
    //異步請求何時執行的策略。每個調度程序使用一個[ExecutorService]在內部運行調用。
    //如果您提供自己的執行器,它應該能夠同時運行[配置的最大][maxRequests]調用數。
    class Dispatcher constructor() {
        
      // 最大請求數,超過後就會等一等
      @get:Synchronized var maxRequests = 64
      //...... 
        
      //主機的最大請求數,防止給服務器太大壓力
      @get:Synchronized var maxRequestsPerHost = 5
        
      @get:Synchronized
      @get:JvmName("executorService") val executorService: ExecutorService
        get() {
          if (executorServiceOrNull == null) {
          executorServiceOrNull = ThreadPoolExecutor(0, Int.MAX_VALUE, 60, TimeUnit.SECONDS,
                SynchronousQueue(), threadFactory("OkHttp Dispatcher", false))
        }
          return executorServiceOrNull!!
        }
    }
    

    Dispatcher 主要是用來管理線程的,每一個新的請求的過程是需要一個單獨的線程,這樣不同的請求之間不會被擋着。

    他的內部實現用的是 ExecutorService ,

  • dispatcher 的 enqueue

    internal fun enqueue(call: AsyncCall) {
      synchronized(this) {
        readyAsyncCalls.add(call)
    	//.....
      }
      promoteAndExecute()
    }
    

    調用 readyAsyncCalls ,這個是一個待命的隊列,隨時準備執行。

    private fun promoteAndExecute(): Boolean {
      assert(!Thread.holdsLock(this))
    
      val executableCalls = mutableListOf<AsyncCall>()
      val isRunning: Boolean
      synchronized(this) {
        val i = readyAsyncCalls.iterator()
         // 遍歷待命隊列
        while (i.hasNext()) {
          val asyncCall = i.next()
    	  //判斷 最大連接數 和 host 數量,不滿足直接 break	
          if (runningAsyncCalls.size >= this.maxRequests) break // Max capacity.
          if (asyncCall.callsPerHost().get() >= this.maxRequestsPerHost) continue // Host max capacity.
    	 // 從待命中移除	
          i.remove()
          asyncCall.callsPerHost().incrementAndGet()
           //將 asynCall 添加到 list 和 運行隊列中
          executableCalls.add(asyncCall)
          runningAsyncCalls.add(asyncCall)
        }
        isRunning = runningCallsCount() > 0
      }
     
      for (i in 0 until executableCalls.size) {
        val asyncCall = executableCalls[i]
        // 調用 AsyncCall 的 executeOn 開始執行
        asyncCall.executeOn(executorService)
      }
      return isRunning
    }
    

    dispatcher 的 enqueue 主要就是進行了最大連接數和host的判斷,然後將 asyncCall 添加到 一個 list 和 運行隊列中,最後遍歷 list 調用了 AsyncCall 的 executeOn 方法,

    下面看一下 AsyncCall

  • AsyncCall

    internal inner class AsyncCall( private val responseCallback: Callback) : Runnable {
    	
        fun executeOn(executorService: ExecutorService) {
          assert(!Thread.holdsLock(client.dispatcher))
          var success = false
          try {
            //執行  
            executorService.execute(this)
            success = true
          } catch (e: RejectedExecutionException) {
            val ioException = InterruptedIOException("executor rejected")
            ioException.initCause(e)
            transmitter.noMoreExchanges(ioException)
            responseCallback.onFailure(this@RealCall, ioException)
          } finally {
            if (!success) {
              client.dispatcher.finished(this) // This call is no longer running!
            }
          }
        }
    
        override fun run() {
          threadName("OkHttp ${redactedUrl()}") {
            var signalledCallback = false
            transmitter.timeoutEnter()
            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(INFO, "Callback failure for ${toLoggableString()}", e)
              } else {
                //
                responseCallback.onFailure(this@RealCall, e)
              }
            } finally {
              client.dispatcher.finished(this)
            }
          }
        }
      }
    }
    

    注意 executeOn 方法,這個方法就是 在 dispatcher 的 enqueue 中調用的。在這裏執行了 executorService 後,對應的 run 方法也就會執行。

    run 方法中,通過 getResponseWithInterceptorChain() 進行請求,並拿到響應 response,最後調用 callback,這個 callback 就是我們在請求的時候傳入的 callback

    在 run 中有一些比較重要的方法,我們到後面說

    到現在使用 enqueue 進行請求的大致邏輯已經非常清楚了,其中最關鍵的代碼就是:

    client.dispatcher.enqueue(AsyncCall(responseCallback))
    

    大致流程如下:

    1,創建一個 RealCall ,然後調用 enqueue

    2,在 enqueue 中調用 dispatcher 的 enqueue

    3,在 dispatcher 的 enqueue 方法中觸發了 AsyncCall 的 run 方法,

    4,在 run 方法中進行請求並響應


execute 方法

val response = call.execute()
override fun execute(): Response {
  synchronized(this) {
    check(!executed) { "Already Executed" }
    executed = true
  }
  transmitter.timeoutEnter()
  transmitter.callStart()
  try {
    client.dispatcher.executed(this)
     // 非常直接,直接調用了。
    return getResponseWithInterceptorChain()
  } finally {
    client.dispatcher.finished(this)
  }
}

execute 中不需要切線程,所以就直接調用了。


OkhttpClient 中的配置項

  • dispatcher

    用來調度線程的,對性能進行優化等,例如 maxRequest = 64 等

  • proxy

    代理

  • protocols

    所支持 http 協議的版本

    enum class Protocol(private val protocol: String) {
    
      HTTP_1_0("http/1.0"),
    
      HTTP_1_1("http/1.1"),
        
      HTTP_2("h2"),
      //......
     }
    
  • connectionSpecs

    ConnectionSpecs 是用來指定 http 傳輸時的 socket 連接;對於 https 連接,ConnectionSpecs 是在構建 TLS 連接時向服務端說明客戶端支持的 TLS 版本,密碼套件的一類,

    在通過 https 連接時,需要向服務器附加上 客戶端所支持的 TLS 版本,Cipher suite(可以接受的 對稱,非對稱和 hash 算法) 等數據。這些都在 ConnectionSpecs 這個類中。

    // 支持密碼套件的列表
    private val RESTRICTED_CIPHER_SUITES = arrayOf(
        // TLSv1.3.
        CipherSuite.TLS_AES_128_GCM_SHA256,
        CipherSuite.TLS_AES_256_GCM_SHA384,
        CipherSuite.TLS_CHACHA20_POLY1305_SHA256,
    
        // TLSv1.0, TLSv1.1, TLSv1.2.
        CipherSuite.TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256,
        CipherSuite.TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256,
        CipherSuite.TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384,
        CipherSuite.TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384,
        CipherSuite.TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256,
        CipherSuite.TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256)
    
    //支持密碼套件的列表
    private val APPROVED_CIPHER_SUITES = arrayOf(
        // TLSv1.3.
        CipherSuite.TLS_AES_128_GCM_SHA256,
        CipherSuite.TLS_AES_256_GCM_SHA384,
        CipherSuite.TLS_CHACHA20_POLY1305_SHA256,
    
        // TLSv1.0, TLSv1.1, TLSv1.2.
        CipherSuite.TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256,
        CipherSuite.TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256,
        CipherSuite.TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384,
        CipherSuite.TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384,
        CipherSuite.TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256,
        CipherSuite.TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256,
    
        // Note that the following cipher suites are all on HTTP/2's bad cipher suites list. We'll
        // continue to include them until better suites are commonly available.
        CipherSuite.TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA,
        CipherSuite.TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA,
        CipherSuite.TLS_RSA_WITH_AES_128_GCM_SHA256,
        CipherSuite.TLS_RSA_WITH_AES_256_GCM_SHA384,
        CipherSuite.TLS_RSA_WITH_AES_128_CBC_SHA,
        CipherSuite.TLS_RSA_WITH_AES_256_CBC_SHA,
        CipherSuite.TLS_RSA_WITH_3DES_EDE_CBC_SHA)
        
         /** 安全的TLS連接需要最近的客戶端和最近的服務器 */
        @JvmField
        val RESTRICTED_TLS = Builder(true)
            .cipherSuites(*RESTRICTED_CIPHER_SUITES)
            .tlsVersions(TlsVersion.TLS_1_3, TlsVersion.TLS_1_2)
            .supportsTlsExtensions(true)
            .build()
    
        /**
         *現代的TLS的配置,適用於大多數客戶端和可以連接到的服務器,是OkHttp的默認配置
         */
        @JvmField
        val MODERN_TLS = Builder(true)
            .cipherSuites(*APPROVED_CIPHER_SUITES)
            .tlsVersions(TlsVersion.TLS_1_3, TlsVersion.TLS_1_2)
            .supportsTlsExtensions(true)
            .build()
    
        /**
         *向後兼容的配置,相比於MODERN_TLS,支持的TLS的版本變少了,只支持TLS_1_0版本
         */
        @JvmField
        val COMPATIBLE_TLS = Builder(true)
            .cipherSuites(*APPROVED_CIPHER_SUITES)
            .tlsVersions(TlsVersion.TLS_1_3, TlsVersion.TLS_1_2, TlsVersion.TLS_1_1, TlsVersion.TLS_1_0)
            .supportsTlsExtensions(true)
            .build()
    
    
        /** 未加密未認證的連接,就是HTTP連接*/
        @JvmField
        val CLEARTEXT = Builder(false).build()
    

    上面的 xxxx_TLS 配置的就是你要使用 http 還是使用 https 。

    如果你要使用 https ,那麼你是用的 tls 版本是多少

  • interceptors,networkInterceptors

    攔截器

  • eventListenerFactory

    用來做統計的。

  • cookieJar

    cookie 的存儲器。okhttp 默認沒有實現 cookie 的存儲,需要自己去存儲。

  • cache

    緩存

  • socketFactory

    用來創建 TCP 端口的 ,http 本身是沒有端口的。

  • certificateChainCleaner

    從服務器拿下來的證書,有時候會拿到很多個證書,certificateChainCleaner 是用來整理的,整理完後就是一個鏈或者說是一個序列,最後一個證書就是本地的根證書。

  • hostnameVerifier

    給 https 做主機名驗證的,用來驗證對方的 host 是不是你需要訪問的host

  • certificatePinner

    證書固定器,用來驗證自簽名證書的!

  • connectionPool

    連接池

  • followRedirects

    遇到 301 的時候是否需要重定向

  • followSslRedirects

    當你訪問的是 http ,但是重定向到 https,或者是 訪問的是 https ,但是重定向到 http 的時候 是否要重定向。

  • retryOnConnectionFailure

    請求失敗的時候是否需要重連

  • connectTimeout

    tcp 連接的時間,超過這個事件則報錯

  • readTimeout

    下載響應的時候等待時間

  • writeTimeout

    寫入請求的等待時間

  • pingInterval

    針對 webSocket 的,可以雙向交互的通道,這就需要長連接了。pingInterval 表示多少時間 ping 一次。


getResponseWithInterceptorChain

在 enqueue 和 execute 方法中,最後都調用的是 getResponseWithInterceptorChain 方法進行請求,在這個方法中會將你的請求進行網絡請求,並獲得 response。下面看一哈源碼

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)

  //.....
    val response = chain.proceed(originalRequest)
  //......
}

​ 上面首先是獲取了自己添加的攔截器,然後又添加了許多內置的攔截器。

​ 接着就是創建了一個 chain ,這個 chain 就是一個鏈,這個鏈裏面放的的就是攔截器。

​ 在請求的時候,就會從鏈的開始往後執行,請求完成之後又會返回來。

​ chain 的作用:例子,我是店老闆,收到了一份快餐的訂單,然後我將快餐做好之後交給店裏騎手,接着騎手把快餐送到客戶家,客戶收到後將錢交給騎手,騎手會從錢中拿出一部分當做路飯,剩下的就給老闆。

​ 這就是一個鏈,老闆是起始端,負責製作快餐,並交給騎手;中間的節點就是騎手,負責將快餐送給用戶;終點就是客戶,接收快餐後,進行付錢。

​ 鏈中的每一個節點都會對 request 做一些處理,並轉交給下一個節點,一直到最後,並且會返回到最開始的地方。

​ 而網絡請求的時候會創建 chain,其中的節點就是攔截器,他會對 request 做一些或多或少的處理,然後交給下一個節點處理。

override fun proceed(request: Request): Response {
  return proceed(request, transmitter, exchange)
}

@Throws(IOException::class)
fun proceed(request: Request, transmitter: Transmitter, exchange: Exchange?): Response {
  if (index >= interceptors.size) throw AssertionError()

  calls++

  //.....

  // Call the next interceptor in the chain.
  val next = RealInterceptorChain(interceptors, transmitter, exchange,
      index + 1, request, call, connectTimeout, readTimeout, writeTimeout)
  val interceptor = interceptors[index]

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

 //......
  return response
}

​ 在 proced 中,獲取下一個攔截器,並調用他的 intercept 方法,在 intercept 中,又會調用 proced,獲取下一個攔截器。。。。;這裏就會形成一個循環,直到最後一個節點返回 response ,最終數據就會返回的開始調用的地方。

​ 需要注意的是,在調用下一個攔截器的時候,當前的 intercept 是沒有執行完的。

接着看一下一些內置的攔截器

 // 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)

  • RetryAndFollowUpInterceptor

    重試,並在必要時跟隨重定向。它可能會拋出IOException

  • BridgeInterceptor(client.cookieJar)

    連接應用程序代碼和網絡代碼。首先,它根據用戶請求構建網絡請求。然後它繼續調用網絡。最後,它從網絡響應構建一個用戶響應。

    override fun intercept(chain: Interceptor.Chain): Response {
      val userRequest = chain.request()
      val requestBuilder = userRequest.newBuilder()
    
      val body = userRequest.body
      if (body != null) {
        val contentType = body.contentType()
        if (contentType != null) {
          requestBuilder.header("Content-Type", contentType.toString())
        }
    
        val contentLength = body.contentLength()
        if (contentLength != -1L) {
          requestBuilder.header("Content-Length", contentLength.toString())
          requestBuilder.removeHeader("Transfer-Encoding")
        } else {
          requestBuilder.header("Transfer-Encoding", "chunked")
          requestBuilder.removeHeader("Content-Length")
        }
      }
    
      if (userRequest.header("Host") == null) {
        requestBuilder.header("Host", userRequest.url.toHostHeader())
      }
    
      if (userRequest.header("Connection") == null) {
        requestBuilder.header("Connection", "Keep-Alive")
      }
    
    
      var transparentGzip = false
      if (userRequest.header("Accept-Encoding") == null && userRequest.header("Range") == null) {
        transparentGzip = true
        //支持 gzip 數據類型
        requestBuilder.header("Accept-Encoding", "gzip")
      }
    
      val cookies = cookieJar.loadForRequest(userRequest.url)
      if (cookies.isNotEmpty()) {
        requestBuilder.header("Cookie", cookieHeader(cookies))
      }
    
      if (userRequest.header("User-Agent") == null) {
        requestBuilder.header("User-Agent", userAgent)
      }
    	
      // proced 之前,請求前  
      val networkResponse = chain.proceed(requestBuilder.build())
      // proced 之後,請求後	
      cookieJar.receiveHeaders(userRequest.url, networkResponse.headers)
    
      val responseBuilder = networkResponse.newBuilder()
          .request(userRequest)
      //解 gzip	
      if (transparentGzip &&
          "gzip".equals(networkResponse.header("Content-Encoding"), ignoreCase = true) &&
          networkResponse.promisesBody()) {
        val responseBody = networkResponse.body
        if (responseBody != null) {
          val gzipSource = GzipSource(responseBody.source())
          val strippedHeaders = networkResponse.headers.newBuilder()
              .removeAll("Content-Encoding")
              .removeAll("Content-Length")
              .build()
          responseBuilder.headers(strippedHeaders)
          val contentType = networkResponse.header("Content-Type")
          responseBuilder.body(RealResponseBody(contentType, -1L, gzipSource.buffer()))
        }
      }
    
      return responseBuilder.build()
    }
    

    在 proced 之前,設置一寫通用的設置,這些設置是在 header 中的,如 contentType,長度,host 等一系列東西。

    在 proced 之後如果服務器發送的是 gzip 數據,則進行解壓,將數據添加到 body 中,最後返回 response

  • CacheInterceptor

    做一些緩存的處理,如 請求之前判斷是否有緩存,請求成功後緩存的寫入等

  • ConnectInterceptor

    處理 http ,https ,tcp 連接的問題,和網絡進行交互,並返回 response

    override fun intercept(chain: Interceptor.Chain): Response {
      val realChain = chain as RealInterceptorChain
      val request = realChain.request()
      val transmitter = realChain.transmitter()
    
      // We need the network to satisfy this request. Possibly for validating a conditional GET.
      val doExtensiveHealthChecks = request.method != "GET"
      val exchange = transmitter.newExchange(chain, doExtensiveHealthChecks)
    
      return realChain.proceed(request, transmitter, exchange)
    }
    
  • networkInterceptors

    NetWorkIntercept 是放在最後面的,這種攔截器是用來做網絡相關的操作的。

如有問題,還請指出

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章