(Android) OkHttp3.10 源碼學習筆記 6 RetryAndFollowUpInterceptor分析

RetryAndFollowUpInterceptor是重試重定向攔截器。它主要是負責失敗重連的。因爲在 OKHTTP 中的攔截器的執行過程是一個遞歸的過程,也就是它內部會通過 RealInterceptorChain 這個類去負責將所有的攔截器進行串起來。只有所有的攔截器執行完畢之後,一個網絡請求的響應 Response 纔會被返回。

但是,在執行這個過程中,難免會出現一些問題,例如連接中斷,握手失敗或者服務器檢測到未認證等,那麼這個 resposne 的返回碼就不是正常的 200 了,因此說這個 response 並不一定是可用的,或者說在請求過程就已經拋出異常了,例如超時異常等,那麼 RetryAndFollowUpInterceptor 需要依據這些問題進行判斷是否可以進行重新連接。

主要我們還是分析intercept方法

首先,它創建了一個StreamAllocation對象

StreamAllocation streamAllocation = new StreamAllocation(client.connectionPool(),
        createAddress(request.url()), call, eventListener, callStackTrace);
    this.streamAllocation = streamAllocation;

這個對象是用來建立http請求需要的網絡組件的,從這個類名我們可以看出,是來分配stream的。雖然它是在這裏創建的,但並未在這個攔截器中使用。

異常檢測

下面進入了一個while(true)循環,通過proceed方法,去獲取下一個攔截器的response。這邊會捕獲RouteException這個異常,下圖我們可以看到這個異常會在哪些地方拋出


對拋出的異常代碼裏用recover方法進行了檢測

catch (RouteException e) {
        // The attempt to connect via a route failed. The request will not have been sent.
        if (!recover(e.getLastConnectException(), streamAllocation, false, request)) {
          throw e.getLastConnectException();
        }
        releaseConnection = false;
        continue;
      }

看下面的recover方法,註釋裏已經做了解釋

private boolean recover(IOException e, boolean routeException, Request userRequest) {
  streamAllocation.streamFailed(e);
  //1.判斷 OkHttpClient 是否支持失敗重連的機制
  // The application layer has forbidden retries.
  if (!client.retryOnConnectionFailure()) return false;
  // 在該方法中傳入的 routeException值 爲 true
  // We can't send the request body again.
  if (!routeException && userRequest.body() instanceof UnrepeatableRequestBody) return false;
  //2.isRecoverable 檢測該異常是否是致命的。
  // This exception is fatal.
  if (!isRecoverable(e, routeException)) return false;
  // No more routes to attempt.
  //3.是否有更多的路線
  if (!streamAllocation.hasMoreRoutes()) return false;
  // For failure recovery, use the same route selector with a new connection.
  return true;
}
private boolean isRecoverable(IOException e, boolean routeException) {
  //ProtocolException 這種異常屬於嚴重異常,不能進行重新連接
  // If there was a protocol problem, don't recover.
  if (e instanceof ProtocolException) {
    return false;
  }
  //當異常爲中斷異常時
  // If there was an interruption don't recover, but if there was a timeout connecting to a route
  // we should try the next route (if there is one).
  if (e instanceof InterruptedIOException) {
    return e instanceof SocketTimeoutException && routeException;
  }
  // Look for known client-side or negotiation errors that are unlikely to be fixed by trying
  // again with a different route.
  //握手異常
  if (e instanceof SSLHandshakeException) {
    // If the problem was a CertificateException from the X509TrustManager,
    // do not retry.
    if (e.getCause() instanceof CertificateException) {
      return false;
    }
  }
  //驗證異常
  if (e instanceof SSLPeerUnverifiedException) {
    // e.g. a certificate pinning error.
    return false;
  }
  // An example of one we might want to retry with a different route is a problem connecting to a
  // proxy and would manifest as a standard IOException. Unless it is one we know we should not
  // retry, we return true and try a new route.
  return true;

響應碼檢測

158行,下面的代碼是對響應碼進行檢測,能走到這,說明請求是成功的,但服務器返回不是200的情況,具體代碼就不貼了,註釋很詳細

 Request followUp = followUpRequest(response, streamAllocation.route());

重試次數判斷

再看169行

if (++followUpCount > MAX_FOLLOW_UPS) {
        streamAllocation.release();
        throw new ProtocolException("Too many follow-up requests: " + followUpCount);
      }

上面的代碼對最大重試次數做了限制,通過閱讀代碼,我們知道這個數是20,如果超過了重試次數,便會釋放連接。



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