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,如果超過了重試次數,便會釋放連接。