okhttp拦截器中RetryAndFollowUpInterceptor重试重定向拦截器中的重试判定
okhttp的拦截器有5个,其中RetryAndFollowUpInterceptor重试重定向拦截器是五个拦截器中第一个执行的拦截器,它的intercept开始执行重试重定向判定,本文我们只分析一下重试判定
@Override public Response intercept(Chain chain) throws IOException {
// 代码略...
while (true) {
// 代码略...
try {
response = realChain.proceed(request, streamAllocation, null, null);
releaseConnection = false;
} catch (RouteException e) {// 发生了路由异常
// 判断是否能重试 返回true可以重试
if (!recover(e.getLastConnectException(), streamAllocation, false, request)) {
throw e.getLastConnectException();
}
// 释放
releaseConnection = false;
// 跳出catch继续执行while循环 重新执行一遍后面的拦截器
continue;
} catch (IOException e) {// 发生了IO异常
// An attempt to communicate with a server failed. The request may have been sent.
boolean requestSendStarted = !(e instanceof ConnectionShutdownException);
// 判断是否能重试 返回true可以重试
if (!recover(e, streamAllocation, requestSendStarted, request)) throw e;
// 释放
releaseConnection = false;
// 跳出catch继续执行while循环 重新执行一遍后面的拦截器
continue;
} finally {
// We're throwing an unchecked exception. Release any resources.
if (releaseConnection) {
streamAllocation.streamFailed(null);
streamAllocation.release();
}
}
// 代码略...
}
}
首先得是请求的过程中发生了RouteException路由异常或者IOException才会执行recover方法来继续下面的重试判定
private boolean recover(IOException e, StreamAllocation streamAllocation,
boolean requestSendStarted, Request userRequest) {
streamAllocation.streamFailed(e);
// okhttpclient设置了允许重试 放下执行 否正返回false
if (!client.retryOnConnectionFailure()) return false;
// 这里是适配HTTP2.0有关的 先不管 Http1.x requestSendStarted这个是false
if (requestSendStarted && userRequest.body() instanceof UnrepeatableRequestBody) return false;
// 判断是不是可以重试的异常 如果是往下执行 否正返回false
if (!isRecoverable(e, requestSendStarted)) return false;
// HOSt有没有更多的ip 如果有 往下执行 否正返回false
if (!streamAllocation.hasMoreRoutes()) return false;
// 执行到这里证明这个异常可以被重试
return true;
}
// 判断是不是可以重试的异常
private boolean isRecoverable(IOException e, boolean requestSendStarted) {
// 如果是一个协议异常 返回false 不要重试
if (e instanceof ProtocolException) {
return false;
}
// 如果是SocketTimeoutException就直接返回true,有其他路由就可以重试
// 不是SocketTimeoutException就返回false 不重试
if (e instanceof InterruptedIOException) {
return e instanceof SocketTimeoutException && !requestSendStarted;
}
// 如果是SSL证书格式 校验的异常 就返回false 不会重试,不是的话继续执行
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;
}
// 返回true 这是可以进行重试 前提是得有多个路由
return true;
}
给出一张自己总结的比较详细的重试代码执行流程图