OKhttp源碼分析

OkHttp是一個高效的HTTP客戶端,它有以下默認特性:

  • 支持HTTP/2,允許所有同一個主機地址的請求共享同一個socket連接
  • 連接池減少請求延時
  • 透明的GZIP壓縮減少響應(yīng)數(shù)據(jù)的大小
  • 緩存響應(yīng)內(nèi)容,避免一些完全重復(fù)的請求

OKhttp的一般使用方法如下:

//異步執(zhí)行
    private void asyncGetRequests() {
        String url = "https://www.alibaba.com";
        OkHttpClient okHttpClient = new OkHttpClient();
        final Request request = new Request.Builder()
                .url(url)
                .get()//默認就是GET請求
                .build();
        Call call = okHttpClient.newCall(request);
        call.enqueue(new Callback() { //執(zhí)行異步請求
            @Override
            public void onFailure(Call call, IOException e) {
                do.....
            }
            @Override
            public void onResponse(Call call, Response response) throws IOException {
                    do.....
            }
        });
    }

//同步執(zhí)行
    private void syncGetRequests() {
        String url = "https://wwww.alibaba.com";
        OkHttpClient okHttpClient = new OkHttpClient();
        final Request request = new Request.Builder()
                .url(url)
                .get()//默認就是GET請求
                .build();
        final Call call = okHttpClient.newCall(request);
        new Thread(() -> {
            try {
                Response response = call.execute(); //執(zhí)行同步請求
                String result = response.body().string();
                runOnUiThread(() -> tvContent.setText("synGetRequests run: " + result));
            } catch (IOException e) {
                e.printStackTrace();
            }
        }).start();
    }

主要步驟

  1. 創(chuàng)建OkHttpClient和Request實例
  2. 通過OkHttpClient和Request的實例創(chuàng)建Call對象
  3. 通過調(diào)用call對象的enqueue或execute方法執(zhí)行異步或同步調(diào)用

整體流程

下圖展示了同步/異步請求的整體流程


image.png

類說明

  • OkHttpClient:主要配置了網(wǎng)絡(luò)請求的很多參數(shù)催烘,最好是單例的形式存在,因為里面可以復(fù)用線程池和連接池。
  • Call:一個接口類萧福,具體去執(zhí)行一個請求,只能被執(zhí)行一次辈赋,它由RealCall實現(xiàn)鲫忍。
  • RealCall:定義了一個請求執(zhí)行的具體過程,包括同步/異步钥屈,并且定義了攔截器的排列順序悟民。
  • AsyncCall:是RealCall的內(nèi)部類,它集成自NamedRunnable實現(xiàn)了Runnable篷就,所以它是在子線程運行射亏,里面具體處理了異步請求網(wǎng)絡(luò)執(zhí)行過程。
  • Transmitter:意為發(fā)射器竭业,是應(yīng)用層和網(wǎng)絡(luò)層的橋梁智润,在進行連接、真正發(fā)出請求和讀取響應(yīng)中起到很重要的作用未辆,它里面保存了OkHttpClient窟绷、連接池、call咐柜、事件監(jiān)聽器兼蜈、call超時時間等信息。
  • Dispatcher:調(diào)度器炕桨,主要是異步請求的并發(fā)控制饭尝、把異步請求放入線程池執(zhí)行,實現(xiàn)方法是promoteAndExecute()献宫。 promoteAndExecute()有兩處調(diào)用:添加異步請求時钥平、同步/異步請求 結(jié)束時,里面使用了幾個數(shù)組維護當前包含了那些Call,包括readyAsyncCalls涉瘾、runningAsyncCalls(最大值為64知态,這里限制了線程池產(chǎn)生線程的個數(shù))和runningSyncCalls。
  • RealInterceptorChain:實現(xiàn)自Interceptor.Chain立叛,它是一個攔截器鏈负敏,它整合了所有攔截器,它具體實現(xiàn)了proceed方法秘蛇,在這個方法里面依次獲取了所有攔截器并執(zhí)行了攔截器的intercept方法其做,并傳入了下一個攔截器的索引。

流程中主要通過getResponseWithInterceptorChain方法獲取Response返回結(jié)果赁还,并且在這個方法里面整合了所有攔截器妖泄,我們具體看看整個過程。

攔截器組裝過程

  Response getResponseWithInterceptorChain() throws IOException {
    // Build a full stack of interceptors.
    List<Interceptor> interceptors = new ArrayList<>();
    interceptors.addAll(client.interceptors());//OkHttpClient.Build#addInterceptor添加自定義攔截器
    interceptors.add(new RetryAndFollowUpInterceptor(client));//重試和重定向攔截器
    interceptors.add(new BridgeInterceptor(client.cookieJar()));//應(yīng)用層和網(wǎng)絡(luò)層的橋接攔截器
    interceptors.add(new CacheInterceptor(client.internalCache()));//緩存處理相關(guān)的攔截器
    interceptors.add(new ConnectInterceptor(client));//連接服務(wù)的攔截器艘策,真正的網(wǎng)絡(luò)請求在這里實現(xiàn)
    if (!forWebSocket) {
      interceptors.addAll(client.networkInterceptors());//OkHttpClient.Build#addNetworkInterceptor添加自定義網(wǎng)絡(luò)網(wǎng)絡(luò)攔截器
    }
    interceptors.add(new CallServerInterceptor(forWebSocket));//負責寫請求和讀響應(yīng)的攔截器蹈胡,Okio主場
    //攔截器鏈,這里index為0朋蔫,所以默認是先執(zhí)行interceptors中的第一個攔截器
    Interceptor.Chain chain = new RealInterceptorChain(interceptors, transmitter, null, 0,
        originalRequest, this, client.connectTimeoutMillis(),
        client.readTimeoutMillis(), client.writeTimeoutMillis());

    boolean calledNoMoreExchanges = false;
    try {
      //攔截器鏈 開始執(zhí)行第一個index = 0 的攔截器
      Response response = chain.proceed(originalRequest);
      if (transmitter.isCanceled()) {
        closeQuietly(response);
        throw new IOException("Canceled");
      }
      return response;
    } catch (IOException e) {
      calledNoMoreExchanges = true;
      throw transmitter.noMoreExchanges(e);
    } finally {
      if (!calledNoMoreExchanges) {
        transmitter.noMoreExchanges(null);
      }
    }
  }

通過上面的代碼我們可以知道罚渐,我們是通過一下順序加入的攔截器:

  1. 通過addInterceptor 方式加入的自定義攔截器
  2. RetryAndFollowUpInterceptor 重試和重定向攔截器
  3. BridgeInterceptor 橋攔截器
  4. CacheInterceptor 緩存攔截器
  5. ConnectInterceptor 連接攔截器
  6. 通過addNetworkInterceptor 方式加入的自定義攔截器
  7. CallServerInterceptor 請求服務(wù)攔截器

所有攔截器都實現(xiàn)Interceptor接口

Interceptor {

  //把攔截器鏈傳遞下去,實現(xiàn)請求的處理過程驯妄,返回response
  Response intercept(Chain chain) throws IOException;

  //攔截器鏈荷并,負責把所有攔截器串聯(lián)起來
  interface Chain {
    Request request();

    //Chain的核心方法,進行攔截器的層次調(diào)用
    Response proceed(Request request) throws IOException;

    //返回請求執(zhí)行的 連接. 僅網(wǎng)絡(luò)攔截器可用; 應(yīng)用攔截器就是null.
    @Nullable Connection connection();

    Call call();
    int connectTimeoutMillis();
    Chain withConnectTimeout(int timeout, TimeUnit unit);
    int readTimeoutMillis();
    Chain withReadTimeout(int timeout, TimeUnit unit);
    int writeTimeoutMillis();
    Chain withWriteTimeout(int timeout, TimeUnit unit);
  }
}

Interceptor是個接口類富玷,只有一個intercept方法璧坟,參數(shù)是Chain對象。內(nèi)部接口類Chain攔截器鏈赎懦,有個proceed方法,參數(shù)是Request對象幻工,返回值是Response励两,這個方法的實現(xiàn)就是請求的處理過程了。

  public Response proceed(Request request, Transmitter transmitter, @Nullable Exchange exchange)
      throws IOException {
   //省略部分代碼囊颅。当悔。。踢代。
    // Call the next interceptor in the chain.
    //獲取下一個攔截器的RealInterceptorChain對象盲憎,并執(zhí)行當前攔截器的intercept方法,并將chain作為參數(shù)傳入
    RealInterceptorChain next = new RealInterceptorChain(interceptors, transmitter, exchange,
        index + 1, request, call, connectTimeout, readTimeout, writeTimeout);
    Interceptor interceptor = interceptors.get(index);
    Response response = interceptor.intercept(next);
  //省略部分代碼胳挎。饼疙。。慕爬。
    return response;
  }

綜上所述窑眯,攔截器通過chain的執(zhí)行過程類似于洋蔥屏积,從最外層,層層傳入磅甩,并重最內(nèi)層炊林,層層傳出。所有攔截器都可以處理當前獲取的request卷要,并將結(jié)果傳入內(nèi)層渣聚,也可以處理當前獲取的response,并將結(jié)果傳入最外層僧叉,除了最后一個攔截器饵逐,重這里我們也可以看出通過addInterceptor和addNetworkInterceptor的區(qū)別,前者更外層所以它只能處理最開始的request和最后的response彪标,整個請求過程調(diào)用一次倍权,而后者更內(nèi)層,所以它可以被調(diào)用多次捞烟,比如重試或者重定向后都會調(diào)用它薄声,并且它可以拿到很多connection和net相關(guān)的數(shù)據(jù)。


image.png
image.png

下面我們就依次分析每個攔截器题画。

重試/重定向攔截器

RetryAndFollowUpInterceptor默辨,意為“重試和重定向攔截器”,作用是連接失敗后進行重試苍息、對請求結(jié)果跟進后進行重定向缩幸,我們先看看它整個執(zhí)行過程,如下圖:


image.png
@Override public Response intercept(Chain chain) throws IOException {
    Request request = chain.request();
    RealInterceptorChain realChain = (RealInterceptorChain) chain;
    Transmitter transmitter = realChain.transmitter();

    int followUpCount = 0;
    Response priorResponse = null;
    while (true) {
      // 準備連接請求
      // 主機竞思、端口表谊、協(xié)議都相同時,連接可復(fù)用
      transmitter.prepareToConnect(request);

      if (transmitter.isCanceled()) {
        throw new IOException("Canceled");
      }

      Response response;
      boolean success = false;
      try {
        // 執(zhí)行其他(下一個->下一個->...)攔截器的功能盖喷,獲取Response爆办;
        response = realChain.proceed(request, transmitter, null);
        success = true;
      } catch (RouteException e) {
        // 連接路由異常,此時請求還未發(fā)送课梳。嘗試恢復(fù)
        // 返回true距辆,continue,繼續(xù)下一個while循環(huán)
        // The attempt to connect via a route failed. The request will not have been sent.
        if (!recover(e.getLastConnectException(), transmitter, false, request)) {
          throw e.getFirstConnectException();
        }
        continue;
      } catch (IOException e) {
        // IO異常暮刃,請求可能已經(jīng)發(fā)出跨算。嘗試恢復(fù)
        // An attempt to communicate with a server failed. The request may have been sent.
        boolean requestSendStarted = !(e instanceof ConnectionShutdownException);
        if (!recover(e, transmitter, requestSendStarted, request)) throw e;
        continue;
      } finally {
        // 請求沒成功,釋放資源
        // The network call threw an exception. Release any resources.
        if (!success) {
          transmitter.exchangeDoneDueToException();
        }
      }

      // Attach the prior response if it exists. Such responses never have a body.
      if (priorResponse != null) {
        response = response.newBuilder()
            .priorResponse(priorResponse.newBuilder()
                    .body(null)
                    .build())
            .build();
      }

      Exchange exchange = Internal.instance.exchange(response);
      Route route = exchange != null ? exchange.connection().route() : null;
      // 后面的攔截器執(zhí)行完了椭懊,拿到Response
      // 解析看下是否需要重試或重定向诸蚕,需要則返回新的Request
      Request followUp = followUpRequest(response, route);

      if (followUp == null) {
        if (exchange != null && exchange.isDuplex()) {
          transmitter.timeoutEarlyExit();
        }
        // 如果followUpRequest返回的Request為空,那邊就表示不需要執(zhí)行重試或者重定向,直接返回數(shù)據(jù)
        return response;
      }

      RequestBody followUpBody = followUp.body();
      if (followUpBody != null && followUpBody.isOneShot()) {
        // 如果followUp不為null挫望,請求體不為空立润,但只需要請求一次時,那么就返回response媳板;
        return response;
      }

      closeQuietly(response.body());
      if (transmitter.hasExchange()) {
        exchange.detachWithViolence();
      }

      // 判斷重試或者重定向的次數(shù)是否超過最大的次數(shù)20桑腮,是的話則拋出異常
      if (++followUpCount > MAX_FOLLOW_UPS) {
        throw new ProtocolException("Too many follow-up requests: " + followUpCount);
      }
      // 將需要重試或者重定向的請求賦值給新的請求
      // 繼續(xù)執(zhí)行新請求
      request = followUp;
      priorResponse = response;
    }//...while (true)
  }

總結(jié)

  1. 通過realChain.proceed發(fā)起請求獲取response
  2. 如果發(fā)生異常,則執(zhí)行recover方法判斷是否可以重試
  3. 如果請求成功蛉幸,則通過followUpRequest判斷response是否需要重定向
  4. 如果不需要重定向破讨,則請求成功返回

橋攔截器

BridgeInterceptor ,意為 橋攔截器奕纫,相當于 在 請求發(fā)起端 和 網(wǎng)絡(luò)執(zhí)行端 架起一座橋提陶,把應(yīng)用層發(fā)出的請求 變?yōu)?網(wǎng)絡(luò)層認識的請求,把網(wǎng)絡(luò)層執(zhí)行后的響應(yīng) 變?yōu)?應(yīng)用層便于應(yīng)用層使用的結(jié)果匹层。


image.png
public final class BridgeInterceptor implements Interceptor {

  //Cookie管理器隙笆,初始化OkhttpClient時創(chuàng)建的
  //默認是CookieJar.NO_COOKIES
  private final CookieJar cookieJar;

  public BridgeInterceptor(CookieJar cookieJar) {
    this.cookieJar = cookieJar;
  }

  @Override public Response intercept(Chain chain) throws IOException {
    Request userRequest = chain.request();
    Request.Builder requestBuilder = userRequest.newBuilder();

    RequestBody body = userRequest.body();
    // 處理封裝"Content-Type", "Content-Length","Transfer-Encoding","Host","Connection",
    // "Accept-Encoding","Cookie","User-Agent"等請求頭
    if (body != null) {
      MediaType contentType = body.contentType();
      if (contentType != null) {
        requestBuilder.header("Content-Type", contentType.toString());
      }

      long contentLength = body.contentLength();
      if (contentLength != -1) {
        requestBuilder.header("Content-Length", Long.toString(contentLength));
        requestBuilder.removeHeader("Transfer-Encoding");
      } else {
        requestBuilder.header("Transfer-Encoding", "chunked");
        requestBuilder.removeHeader("Content-Length");
      }
    }

    if (userRequest.header("Host") == null) {
      requestBuilder.header("Host", hostHeader(userRequest.url(), false));
    }

    if (userRequest.header("Connection") == null) {
      requestBuilder.header("Connection", "Keep-Alive");
    }
    // 在服務(wù)器支持gzip壓縮的前提下,客戶端不設(shè)置Accept-Encoding=gzip的話升筏,
    // okhttp會自動幫我們開啟gzip和解壓數(shù)據(jù)撑柔,如果客戶端自己開啟了gzip,就需要自己解壓服務(wù)器返回的數(shù)據(jù)了您访。
    // If we add an "Accept-Encoding: gzip" header field we're responsible for also decompressing
    // the transfer stream.
    boolean transparentGzip = false;
    if (userRequest.header("Accept-Encoding") == null && userRequest.header("Range") == null) {
      transparentGzip = true;
      requestBuilder.header("Accept-Encoding", "gzip");
    }

    //從cookieJar中獲取cookie铅忿,添加到header
    List<Cookie> cookies = cookieJar.loadForRequest(userRequest.url());
    if (!cookies.isEmpty()) {
      requestBuilder.header("Cookie", cookieHeader(cookies));
    }

    if (userRequest.header("User-Agent") == null) {
      requestBuilder.header("User-Agent", Version.userAgent());
    }
    // 把處理好的新請求往下傳遞,執(zhí)行后續(xù)的攔截器的邏輯
    Response networkResponse = chain.proceed(requestBuilder.build());
    //從networkResponse中獲取 header "Set-Cookie" 存入cookieJar
    HttpHeaders.receiveHeaders(cookieJar, userRequest.url(), networkResponse.headers());
    // 獲取返回體的Builder
    Response.Builder responseBuilder = networkResponse.newBuilder()
        .request(userRequest);
    // 處理返回的Response的"Content-Encoding"灵汪、"Content-Length"檀训、"Content-Type"等返回頭
    // 如果我們沒有手動添加"Accept-Encoding: gzip",這里會創(chuàng)建 能自動解壓的responseBody--GzipSource
    if (transparentGzip
        && "gzip".equalsIgnoreCase(networkResponse.header("Content-Encoding"))
        && HttpHeaders.hasBody(networkResponse)) {
      GzipSource responseBody = new GzipSource(networkResponse.body().source());
      Headers strippedHeaders = networkResponse.headers().newBuilder()
          .removeAll("Content-Encoding")
          .removeAll("Content-Length")
          .build();
      responseBuilder.headers(strippedHeaders);
      String contentType = networkResponse.header("Content-Type");
      responseBuilder.body(new RealResponseBody(contentType, -1L, Okio.buffer(responseBody)));
    }
    //然后新構(gòu)建的response返回出去
    return responseBuilder.build();
  }
}

總結(jié)

主要就是添加了一些header享言、如果有cookie就添加峻凫、如果需要gzip就做壓縮和解壓縮。

緩存攔截器

CacheInterceptor担锤,緩存攔截器蔚晨,提供網(wǎng)絡(luò)請求緩存的存取。合理使用本地緩存肛循,有效地減少網(wǎng)絡(luò)開銷、減少響應(yīng)延遲银择。


image.png
public final class CacheInterceptor implements Interceptor {
  final @Nullable InternalCache cache;

  public CacheInterceptor(@Nullable InternalCache cache) {
    this.cache = cache;
  }

  @Override public Response intercept(Chain chain) throws IOException {
    // 先獲取候選緩存多糠,前提是有配置緩存,也就是cache不為空浩考;
    Response cacheCandidate = cache != null
        ? cache.get(chain.request())
        : null;

    long now = System.currentTimeMillis();
    // 執(zhí)行獲取緩存策略的邏輯
    // 緩存策略決定是否使用緩存:
    // strategy.networkRequest為null,不使用網(wǎng)絡(luò)
    // strategy.cacheResponse為null,不使用緩存只怎。
    CacheStrategy strategy = new CacheStrategy.Factory(now, chain.request(), cacheCandidate).get();
    // 網(wǎng)絡(luò)請求
    Request networkRequest = strategy.networkRequest;
    // 本地的緩存保存的請求
    Response cacheResponse = strategy.cacheResponse;

    //根據(jù)緩存策略更新統(tǒng)計指標:請求次數(shù)怜俐、網(wǎng)絡(luò)請求次數(shù)、使用緩存次數(shù)
    if (cache != null) {
      cache.trackResponse(strategy);
    }

    //有緩存 但不能用拍鲤,關(guān)掉
    if (cacheCandidate != null && cacheResponse == null) {
      closeQuietly(cacheCandidate.body()); // The cache candidate wasn't applicable. Close it.
    }
    // networkRequest == null 不能用網(wǎng)絡(luò)
    // 如果不使用網(wǎng)絡(luò)數(shù)據(jù)且緩存數(shù)據(jù)為空,那么返回一個504的Response擅这,并且body為空
    // If we're forbidden from using the network and the cache is insufficient, fail.
    if (networkRequest == null && cacheResponse == null) {
      return new Response.Builder()
          .request(chain.request())
          .protocol(Protocol.HTTP_1_1)
          .code(504)
          .message("Unsatisfiable Request (only-if-cached)")
          .body(Util.EMPTY_RESPONSE)
          .sentRequestAtMillis(-1L)
          .receivedResponseAtMillis(System.currentTimeMillis())
          .build();
    }

    // 如果不需要使用網(wǎng)絡(luò)數(shù)據(jù)景鼠,那么就直接返回緩存的數(shù)據(jù)
    // If we don't need the network, we're done.
    if (networkRequest == null) {
      return cacheResponse.newBuilder()
          .cacheResponse(stripBody(cacheResponse))
          .build();
    }

    /*
     * 到這里仲翎,networkRequest != null (cacheResponse可能null,可能!null)
     * 沒有命中強緩存的情況下铛漓,進行網(wǎng)絡(luò)請求溯香,獲取response
     * 先判斷是否是協(xié)商緩存(304)命中,命中則更新緩存返回response
     * 未命中使用網(wǎng)絡(luò)請求的response返回并添加緩存
     */
    Response networkResponse = null;
    try {
      // 執(zhí)行后續(xù)的攔截器邏輯
      networkResponse = chain.proceed(networkRequest);
    } finally {
      // If we're crashing on I/O or otherwise, don't leak the cache body.
      if (networkResponse == null && cacheCandidate != null) {
        closeQuietly(cacheCandidate.body());
      }
    }

    // If we have a cache response too, then we're doing a conditional get.
    if (cacheResponse != null) {
      // 如果緩存數(shù)據(jù)不為空并且code為304票渠,表示數(shù)據(jù)沒有變化逐哈,繼續(xù)使用緩存數(shù)據(jù);
      if (networkResponse.code() == HTTP_NOT_MODIFIED) {
        Response response = cacheResponse.newBuilder()
            .headers(combine(cacheResponse.headers(), networkResponse.headers()))
            .sentRequestAtMillis(networkResponse.sentRequestAtMillis())
            .receivedResponseAtMillis(networkResponse.receivedResponseAtMillis())
            .cacheResponse(stripBody(cacheResponse))
            .networkResponse(stripBody(networkResponse))
            .build();
        networkResponse.body().close();

        // Update the cache after combining headers but before stripping the
        // Content-Encoding header (as performed by initContentStream()).
        cache.trackConditionalCacheHit();
        // 更新緩存數(shù)據(jù)
        cache.update(cacheResponse, response);
        return response;
      } else {
        //如果是非304问顷,說明服務(wù)端資源有更新昂秃,就關(guān)閉緩存body
        closeQuietly(cacheResponse.body());
      }
    }

    // 協(xié)商緩存也未命中,獲取網(wǎng)絡(luò)返回的response
    Response response = networkResponse.newBuilder()
        .cacheResponse(stripBody(cacheResponse))
        .networkResponse(stripBody(networkResponse))
        .build();

    if (cache != null) {
      //網(wǎng)絡(luò)響應(yīng)可緩存(請求和響應(yīng)的 頭 Cache-Control都不是'no-store')
      if (HttpHeaders.hasBody(response) && CacheStrategy.isCacheable(response, networkRequest)) {
        // 將網(wǎng)絡(luò)數(shù)據(jù)保存到緩存中
        // InternalCache接口杜窄,實現(xiàn)在Cache類中
        // Offer this request to the cache.
        CacheRequest cacheRequest = cache.put(response);
        return cacheWritingResponse(cacheRequest, response);
      }

      //OkHttp默認只會對get請求進行緩存
      //不是get請求就移除緩存
      if (HttpMethod.invalidatesCache(networkRequest.method())) {
        try {
          // 緩存失效肠骆,那么就移除緩存
          cache.remove(networkRequest);
        } catch (IOException ignored) {
          // The cache cannot be written.
        }
      }
    }
    return response;
  }
  ...
}

總結(jié)

  • 通過緩存策略決定是使用緩存數(shù)據(jù)還是網(wǎng)絡(luò)數(shù)據(jù)
  • 默認只緩存GET請求
  • 緩存保存在用戶指定的目錄,內(nèi)部通過DiskLruCache實現(xiàn)塞耕,使用URL的md5做為key

連接攔截器

ConnectInterceptor蚀腿,連接攔截器,主要負責連接的創(chuàng)建和復(fù)用莉钙。

image.png
@Override public Response intercept(Chain chain) throws IOException {
    RealInterceptorChain realChain = (RealInterceptorChain) chain;
    Request request = realChain.request();
    Transmitter transmitter = realChain.transmitter();

    // We need the network to satisfy this request. Possibly for validating a conditional GET.
    boolean doExtensiveHealthChecks = !request.method().equals("GET");

    // 實現(xiàn)了復(fù)雜的網(wǎng)絡(luò)請求,Exchange用于下一個攔截器CallServerInterceptor進行網(wǎng)絡(luò)請求使用筛谚;
    /*
        1:調(diào)用transmitter.newExchange方法蚊伞;
        2:通過Transmitter的ExchangeFinder調(diào)用了find方法癞揉;
        3:ExchangeFinder里調(diào)用了findHealthyConnection方法;
        4:ExchangeFinder里findHealthyConnection調(diào)用了findConnection方法創(chuàng)建了RealConnection胳泉;
        5:調(diào)用了RealConnection的connect方法控汉,實現(xiàn)了TCP + TLS 握手测僵,底層通過socket來實現(xiàn)的颜武;
        6: 通過RealConnection的newCodec方法創(chuàng)建了兩個協(xié)議類鬼贱,一個是Http1ExchangeCodec蹋岩,對應(yīng)著HTTP1.1,
        一個是Http2ExchangeCodec棋嘲,對應(yīng)著HTTP2.0;
    */
    //創(chuàng)建一個交換器Exchange
    Exchange exchange = transmitter.newExchange(chain, doExtensiveHealthChecks);
    // 執(zhí)行后續(xù)的攔截器邏輯,注意是三個參數(shù)
    return realChain.proceed(request, transmitter, exchange);
  }

類說明

  • Transmitter:意為發(fā)射器,是把請求從應(yīng)用端發(fā)射到網(wǎng)絡(luò)層,它持有請求的連接甩骏、請求论熙、響應(yīng)和流,一個請求對應(yīng)一個Transmitter實例交惯,一個數(shù)據(jù)流。
  • Exchange:request、responseIO管理笤昨,對ExchangeCodec的包裝睡互,增加了事件回調(diào)妻怎,一個請求對應(yīng)一個Exchange實例。
  • ExchangeCodec:接口類,負責真正的IO寫請求、讀響應(yīng)掖鱼,實現(xiàn)類有Http1ExchangeCodec褐墅、Http2ExchangeCodec码泛,對應(yīng)HTTP1.1齐莲、HTTP2.0岳枷。
  • ExchangeFinder:從連接池中尋找可用TCP連接,然后通過連接得到ExchangeCodec。
  • Route:一個具體連接服務(wù)器的路由拴竹,包括address(IP地址通過DNS可能獲取多個)和proxy(通過OKhttpclient配置沒有則使用默認的)
  • RouteSelector:配置代理服務(wù)器以及通過DNS獲取IP地址resetNextInetSocketAddress。
  • ConnectionPool:連接池,用于管理http1.1/http2.0連接重用赏陵,以減少網(wǎng)絡(luò)延遲。
    相同Address的http請求可以共享一個連接,ConnectionPool就是實現(xiàn)了連接的復(fù)用。
    1.默認最多保存5個空閑連接,并且空閑5分鐘后移除癌淮。
    2.通過put方法給連接池添加連接虚倒,首先通過cleanupRunning線程移除空閑并過期的連
    接易猫,這個線程在整個線程池里面只會存在一個准颓。
    3.通過transmitterAcquirePooledConnection取一個可以復(fù)用的連接炮赦。
  • RealConnection:一個具體的連接吠勘,通過connect闖將一個連接通道剧防,供后續(xù)IO操作使用。

總結(jié)

  1. 主要是獲取一個Exchange對象并通過realChain.proceed方法傳遞給后續(xù)攔截器做具體的IO操作使用棚唆。
  2. 通過ExchangeFinder去獲取一個ExchangeCodec鞋囊,即具體操作request和response的協(xié)議溜腐。
  3. 通過RealConnectionPool連接池去復(fù)用連接挺益,連接池復(fù)用邏輯如下圖所示:
image.png

請求服務(wù)連接器

CallServerInterceptor伞辛,請求服務(wù)攔截器蚤氏,也就是真正地去進行網(wǎng)絡(luò)IO讀寫了——寫入http請求的header和body數(shù)據(jù)竿滨、讀取響應(yīng)的header和body毁葱。


image.png
public final class CallServerInterceptor implements Interceptor {
  private final boolean forWebSocket;

  public CallServerInterceptor(boolean forWebSocket) {
    this.forWebSocket = forWebSocket;
  }

  @Override public Response intercept(Chain chain) throws IOException {
    RealInterceptorChain realChain = (RealInterceptorChain) chain;
    //ConnectInterceptor攔截器傳入的exchange
    Exchange exchange = realChain.exchange();
    Request request = realChain.request();

    long sentRequestMillis = System.currentTimeMillis();

    // 將請求頭寫入到socket中,底層通過ExchangeCodec協(xié)議類
    // (對應(yīng)Http1ExchangeCodec和Http2ExchangeCodec)
    // 最終是通過Okio來實現(xiàn)的鸠澈,具體實現(xiàn)在RealBufferedSink這個類里面
    exchange.writeRequestHeaders(request);

    // 如果有body的話际度,通過Okio將body寫入到socket中,用于發(fā)送給服務(wù)器
    boolean responseHeadersStarted = false;
    Response.Builder responseBuilder = null;
    //含body的請求
    if (HttpMethod.permitsRequestBody(request.method()) && request.body() != null) {
      // If there's a "Expect: 100-continue" header on the request, wait for a "HTTP/1.1 100
      // Continue" response before transmitting the request body. If we don't get that, return
      // what we did get (such as a 4xx response) without ever transmitting the request body.
      // 若請求頭包含 "Expect: 100-continue" ,
      // 就會等服務(wù)端返回含有 "HTTP/1.1 100 Continue"的響應(yīng)鹉勒,然后再發(fā)送請求body.
      // 如果沒有收到這個響應(yīng)(例如收到的響應(yīng)是4xx),那就不發(fā)送body了脯倒。
      if ("100-continue".equalsIgnoreCase(request.header("Expect"))) {
        exchange.flushRequest();
        responseHeadersStarted = true;
        exchange.responseHeadersStart();
        //讀取響應(yīng)頭
        responseBuilder = exchange.readResponseHeaders(true);
      }
      // responseBuilder為null說明服務(wù)端返回了100藻丢,也就是可以繼續(xù)發(fā)送body了
      // 底層通過ExchangeCodec協(xié)議類(對應(yīng)Http1ExchangeCodec和Http2ExchangeCodec)來讀取返回頭header的數(shù)據(jù)
      if (responseBuilder == null) {
        if (request.body().isDuplex()) {//默認是false不會進入
          // Prepare a duplex body so that the application can send a request body later.
          exchange.flushRequest();
          BufferedSink bufferedRequestBody = Okio.buffer(
              exchange.createRequestBody(request, true));
          request.body().writeTo(bufferedRequestBody);
        } else {
          // Write the request body if the "Expect: 100-continue" expectation was met.
          // 滿足了 "Expect: 100-continue" ,寫請求body
          BufferedSink bufferedRequestBody = Okio.buffer(
              exchange.createRequestBody(request, false));
          request.body().writeTo(bufferedRequestBody);
          bufferedRequestBody.close();
        }
      } else {
        //沒有滿足 "Expect: 100-continue" ,請求發(fā)送結(jié)束
        exchange.noRequestBody();
        if (!exchange.connection().isMultiplexed()) {
          // If the "Expect: 100-continue" expectation wasn't met, prevent the HTTP/1 connection
          // from being reused. Otherwise we're still obligated to transmit the request body to
          // leave the connection in a consistent state.
          exchange.noNewExchangesOnConnection();
        }
      }
    } else {
      //沒有body,請求發(fā)送結(jié)束
      exchange.noRequestBody();
    }

    //請求發(fā)送結(jié)束
    if (request.body() == null || !request.body().isDuplex()) {
      //真正將寫到socket輸出流的http請求數(shù)據(jù)發(fā)送冰木。
      exchange.finishRequest();
    }
    //回調(diào) 讀響應(yīng)頭開始事件(如果上面沒有)
    if (!responseHeadersStarted) {
      exchange.responseHeadersStart();
    }
    //讀響應(yīng)頭(如果上面沒有)
    if (responseBuilder == null) {
      responseBuilder = exchange.readResponseHeaders(false);
    }

    // 創(chuàng)建返回體Response
    Response response = responseBuilder
        .request(request)
        .handshake(exchange.connection().handshake())
        .sentRequestAtMillis(sentRequestMillis)
        .receivedResponseAtMillis(System.currentTimeMillis())
        .build();

    int code = response.code();
    if (code == 100) {
      // server sent a 100-continue even though we did not request one.
      // try again to read the actual response
      // 服務(wù)端又返回了個100穷劈,就再嘗試獲取真正的響應(yīng)
      response = exchange.readResponseHeaders(false)
          .request(request)
          .handshake(exchange.connection().handshake())
          .sentRequestAtMillis(sentRequestMillis)
          .receivedResponseAtMillis(System.currentTimeMillis())
          .build();

      code = response.code();
    }
    //回調(diào)讀響應(yīng)頭結(jié)束
    exchange.responseHeadersEnd(response);
    //這里就是獲取響應(yīng)body了
    if (forWebSocket && code == 101) {
      // Connection is upgrading, but we need to ensure interceptors see a non-null response body.
      response = response.newBuilder()
          .body(Util.EMPTY_RESPONSE)
          .build();
    } else {
      // 讀取返回體body的數(shù)據(jù)
      // 底層通過ExchangeCodec協(xié)議類(對應(yīng)Http1ExchangeCodec和Http2ExchangeCodec)
      response = response.newBuilder()
          .body(exchange.openResponseBody(response))
          .build();
    }
    //請求頭中Connection是close,表示請求完成后要關(guān)閉連接
    if ("close".equalsIgnoreCase(response.request().header("Connection"))
        || "close".equalsIgnoreCase(response.header("Connection"))) {
      exchange.noNewExchangesOnConnection();
    }
    //204(無內(nèi)容)踊沸、205(重置內(nèi)容)歇终,body應(yīng)該是空
    if ((code == 204 || code == 205) && response.body().contentLength() > 0) {
      throw new ProtocolException(
          "HTTP " + code + " had non-zero Content-Length: " + response.body().contentLength());
    }

    return response;
  }
}

類說明

  • Exchange:對外提供了writeXX、openXX逼龟、flushXX等方法通過ExchangeCodec操作request和response的傳遞评凝。
  • ExchangeCodec:它有兩個實現(xiàn)類Http1ExchangeCodec(HTTP/1.1)和Http2ExchangeCodec(HTTP/2),里面根據(jù)不同的協(xié)議規(guī)范制定了具體的格式和IO過程腺律。
  • Okio:具體執(zhí)行IO操作的框架

總結(jié)

  1. 通過上面的connection通道日杈,具體執(zhí)行發(fā)送request和接受response過程涨冀。
  2. 這個攔截器最終返回了response恋拍,并且沒有執(zhí)行chain的proceed方法僵娃,所以它是最里層節(jié)點,然后一層層把response返回出去。
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末唁桩,一起剝皮案震驚了整個濱河市捏肢,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌一睁,老刑警劉巖,帶你破解...
    沈念sama閱讀 216,496評論 6 501
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件髓棋,死亡現(xiàn)場離奇詭異镣奋,居然都是意外死亡妻柒,警方通過查閱死者的電腦和手機计盒,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,407評論 3 392
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人踩窖,你說我怎么就攤上這事〈海” “怎么了?”我有些...
    開封第一講書人閱讀 162,632評論 0 353
  • 文/不壞的土叔 我叫張陵榴嗅,是天一觀的道長。 經(jīng)常有香客問我,道長,這世上最難降的妖魔是什么钠右? 我笑而不...
    開封第一講書人閱讀 58,180評論 1 292
  • 正文 為了忘掉前任,我火速辦了婚禮,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘反浓。我一直安慰自己萌丈,他們只是感情好,可當我...
    茶點故事閱讀 67,198評論 6 388
  • 文/花漫 我一把揭開白布雷则。 她就那樣靜靜地躺著辆雾,像睡著了一般。 火紅的嫁衣襯著肌膚如雪月劈。 梳的紋絲不亂的頭發(fā)上度迂,一...
    開封第一講書人閱讀 51,165評論 1 299
  • 那天,我揣著相機與錄音猜揪,去河邊找鬼惭墓。 笑死,一個胖子當著我的面吹牛湿右,可吹牛的內(nèi)容都是我干的诅妹。 我是一名探鬼主播,決...
    沈念sama閱讀 40,052評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼毅人,長吁一口氣:“原來是場噩夢啊……” “哼吭狡!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起丈莺,我...
    開封第一講書人閱讀 38,910評論 0 274
  • 序言:老撾萬榮一對情侶失蹤划煮,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后缔俄,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體弛秋,經(jīng)...
    沈念sama閱讀 45,324評論 1 310
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,542評論 2 332
  • 正文 我和宋清朗相戀三年俐载,在試婚紗的時候發(fā)現(xiàn)自己被綠了蟹略。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 39,711評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡遏佣,死狀恐怖挖炬,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情状婶,我是刑警寧澤意敛,帶...
    沈念sama閱讀 35,424評論 5 343
  • 正文 年R本政府宣布馅巷,位于F島的核電站,受9級特大地震影響草姻,放射性物質(zhì)發(fā)生泄漏钓猬。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 41,017評論 3 326
  • 文/蒙蒙 一撩独、第九天 我趴在偏房一處隱蔽的房頂上張望敞曹。 院中可真熱鬧,春花似錦跌榔、人聲如沸异雁。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,668評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽纲刀。三九已至,卻和暖如春担平,著一層夾襖步出監(jiān)牢的瞬間示绊,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 32,823評論 1 269
  • 我被黑心中介騙來泰國打工暂论, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留面褐,地道東北人。 一個月前我還...
    沈念sama閱讀 47,722評論 2 368
  • 正文 我出身青樓取胎,卻偏偏與公主長得像展哭,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子闻蛀,可洞房花燭夜當晚...
    茶點故事閱讀 44,611評論 2 353

推薦閱讀更多精彩內(nèi)容