Okhttp源碼解讀第一篇——整體架構

OkHttp概述

OkHttp 是適用於 Android 和 Java 應用程序的高效HTTP客戶端。最早的版本是基於 HttpURLConnection 和 HttpClient 的封裝,之後 Android 6.0 版移除了對 Apache HTTP 客戶端的支持,OkHttp 也移除了 HttpClient,然後移除 HttpURLConnection 的底層支持,連接底層到應用全部自己實現,成爲業界公認最佳方案,HttpURLConnection 的底層實現也改爲了 OkHttp 的實現方式。

優勢:

  • 支持HTTP/2, HTTP/2通過使用多路複用技術在一個單獨的TCP連接上支持併發, 通過在一個連接上一次性發送多個請求來發送或接收數據

  • 如果HTTP/2不可用, 連接池複用技術也可以極大減少延時

  • 支持GZIP, 可以壓縮下載體積

  • 響應緩存可以直接避免重複請求

  • 會從很多常用的連接問題中自動恢復

  • 如果您的服務器配置了多個IP地址, 當第一個IP連接失敗的時候, OkHttp會自動嘗試下一個IP

  • OkHttp還處理了代理服務器問題和SSL握手失敗問題

使用

使用 OkHttp 同步請求

   OkHttpClient client = new OkHttpClient();

   Request request = new Request.Builder()
      .url(BASE_URL + "/date")
      .build();

    Call call = client.newCall(request);
    Response response = call.execute();

使用 OkHttp 異步請求

  OkHttpClient client = new OkHttpClient();

  Request request = new Request.Builder()
      .url(BASE_URL + "/date")
      .build();

    Call call = client.newCall(request);
    call.enqueue(new Callback() {
        public void onResponse(Call call, Response response) 
          throws IOException {
            // ...
        }
        
        public void onFailure(Call call, IOException e) {
            fail();
        }
    });

源碼走讀

異步請求

call.enqueue開始,查看方法實現:

  fun enqueue(responseCallback: Callback)

這是一個抽象方法,所以需要回到Call call = client.newCall(request),查看newCall的實現,

  /** Prepares the [request] to be executed at some point in the future. */
  override fun newCall(request: Request): Call = RealCall(this, request, forWebSocket = false)

那麼真正調用enqueue的是RealCall,查看 RealCallenqueue的實現:

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

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

callStart()是一個監聽器,監聽請求的開始,重點在這裏:client.dispatcher.enqueue(AsyncCall(responseCallback))。點進dispatcher

/**
 * Policy on when async requests are executed.
 *
 * Each dispatcher uses an [ExecutorService] to run calls internally. If you supply your own
 * executor, it should be able to run [the configured maximum][maxRequests] number of calls
 * concurrently.
 */
class Dispatcher constructor() {}

通過註釋,可以知道這是一個使用 ExecutorService 的線程調度器。

  /**
   * The maximum number of requests to execute concurrently. Above this requests queue in memory,
   * waiting for the running calls to complete.
   *
   * If more than [maxRequests] requests are in flight when this is invoked, those requests will
   * remain in flight.
   */
  @get:Synchronized var maxRequests = 64
    set(maxRequests) {
      require(maxRequests >= 1) { "max < 1: $maxRequests" }
      synchronized(this) {
        field = maxRequests
      }
      promoteAndExecute()
    }

可以 Set 的同時執行的最大請求數,默認同時執行64個請求。

 /**
   * The maximum number of requests for each host to execute concurrently. This limits requests by
   * the URL's host name. Note that concurrent requests to a single IP address may still exceed this
   * limit: multiple hostnames may share an IP address or be routed through the same HTTP proxy.
   *
   * If more than [maxRequestsPerHost] requests are in flight when this is invoked, those requests
   * will remain in flight.
   *
   * WebSocket connections to hosts **do not** count against this limit.
   */
  @get:Synchronized var maxRequestsPerHost = 5
    set(maxRequestsPerHost) {
      require(maxRequestsPerHost >= 1) { "max < 1: $maxRequestsPerHost" }
      synchronized(this) {
        field = maxRequestsPerHost
      }
      promoteAndExecute()
    }

每個主機能同時執行的最大請求數,默認是5個。

  @get:Synchronized
  @get:JvmName("executorService") val executorService: ExecutorService
    get() {
      if (executorServiceOrNull == null) {
        executorServiceOrNull = ThreadPoolExecutor(0, Int.MAX_VALUE, 60, TimeUnit.SECONDS,
            SynchronousQueue(), threadFactory("$okHttpName Dispatcher", false))
      }
      return executorServiceOrNull!!
    }

內部使用一個核心線程數爲0、容量無限大、60秒超時的線程池。

查看enqueue

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

      // Mutate the AsyncCall so that it shares the AtomicInteger of an existing running call to
      // the same host.
      if (!call.call.forWebSocket) {
        val existingCall = findExistingCallWithHost(call.host)
        if (existingCall != null) call.reuseCallsPerHostFrom(existingCall)
      }
    }
    promoteAndExecute()
  }

readyAsyncCalls.add(call)把準備執行但未執行的 AsyncCall 放入隊列,然後記錄主機連接數,最後調用promoteAndExecute()

 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
  }

遍歷隊列,挑選符合條件可以被執行的 Call ,把他們放在正在執行的隊列,然後調用asyncCall.executeOn(executorService)執行:

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

      var success = false
      try {
        executorService.execute(this)
        success = true
      } catch (e: RejectedExecutionException) {
        val ioException = InterruptedIOException("executor rejected")
        ioException.initCause(e)
        noMoreExchanges(ioException)
        responseCallback.onFailure(this@RealCall, ioException)
      } finally {
        if (!success) {
          client.dispatcher.finished(this) // This call is no longer running!
        }
      }
    }

executorService.execute(this)這裏既然傳入了this,那麼這個類肯定實現了 Runnable 接口:

  inner class AsyncCall(
    private val responseCallback: Callback
  ) : Runnable 

我們查看 AsyncCall 的 run方法,找出怎麼執行的:

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

val response = getResponseWithInterceptorChain()這裏直接返回的是 Response,說明getResponseWithInterceptorChain裏會發起請求和返回,核心也就在這裏。

同步請求

下面看下同步請求的執行:

  override fun execute(): Response {
    check(executed.compareAndSet(false, true)) { "Already Executed" }

    timeout.enter()
    callStart()
    try {
      client.dispatcher.executed(this)
      return getResponseWithInterceptorChain()
    } finally {
      client.dispatcher.finished(this)
    }
  }

很簡單,直接調用getResponseWithInterceptorChain()

整體架構分析到此,下一篇詳細分析getResponseWithInterceptorChain()

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