上篇简单描述了OkHttp的请求流程没了解的可以 点我 ,这篇主要解读下除自定义的interceptor之后的首个拦截器:RetryAndFollowUpInterceptor(容错的重连拦截器),话不多说,很简单,直接结合代码了解下!
首先我们是可以通过OkHttpClient控制是否启用重连机制的
而触发时机是在抛出RouteException异常时,那么什么时候会抛出该异常触发重连呢?
通过点开RouteException查看其调用的地方,我们了解到一共两个地方:RealConnection的connect方法中及StreamAllocation的newStream方法中。两处都是直接抛出异常,所以该异常其实最终都由RetryAndFollowUpInterceptor来处理了。那两个类以后会详细讲解,可以大概理解为链接服务器及流处理时用到的类。接下来看下其核心方法intercept
public Response intercept(Chain chain) throws IOException {
// 从责任链中拿到request对象并初始化StreamAllocation对象
Request request = chain.request();
this.streamAllocation = new StreamAllocation(this.client.connectionPool(), this.createAddress(request.url()), this.callStackTrace);
int followUpCount = 0; // 记录重连次数
Response priorResponse = null;
while(!this.canceled) {
Response response = null;
boolean releaseConnection = true;
try {
response = ((RealInterceptorChain)chain).proceed(request, this.streamAllocation, (HttpCodec)null, (RealConnection)null);
releaseConnection = false;
} catch (RouteException var13) {
// 满足一定条件,抛出异常包括我们设置的是否需要重连机制、断网等情况
if (!this.recover(var13.getLastConnectException(), false, request)) {
throw var13.getLastConnectException();
}
releaseConnection = false;
continue;
} catch (IOException var14) { // 当发生io异常时,以抛出io异常的方式结束循环
boolean requestSendStarted = !(var14 instanceof ConnectionShutdownException);
if (!this.recover(var14, requestSendStarted, request)) {
throw var14;
}
releaseConnection = false;
continue;
} finally { // 如果上述步骤即没执行也没捕获到异常则调用错误回调并关闭流释放资源
if (releaseConnection) {
this.streamAllocation.streamFailed((IOException)null);
this.streamAllocation.release();
}
}
if (priorResponse != null) { // 当前一次response不为空时,重新构造response
response = response.newBuilder().priorResponse(priorResponse.newBuilder().body((ResponseBody)null).build()).build();
}
Request followUp = this.followUpRequest(response);
if (followUp == null) { // 调用环境正常时会进入此步骤
if (!this.forWebSocket) {
this.streamAllocation.release();
}
return response;
}
Util.closeQuietly(response.body());
++followUpCount;
if (followUpCount > 20) { // 当重连请求大于二十次时以异常的方式结束本次重连
this.streamAllocation.release();
throw new ProtocolException("Too many follow-up requests: " + followUpCount);
}
if (followUp.body() instanceof UnrepeatableRequestBody) { // body被标记成UnrepeatableRequestBody不会再进行重连
this.streamAllocation.release();
throw new HttpRetryException("Cannot retry streamed HTTP body", response.code());
}
if (!this.sameConnection(response, followUp.url())) {
//判断要重连的接口与刚完成的请求的host、port、scheme是否一致
//不一致,则释放streamAllocation,新建重定向之后的StreamAllocation对象
this.streamAllocation.release();
this.streamAllocation = new StreamAllocation(this.client.connectionPool(), this.createAddress(followUp.url()), this.callStackTrace);
} else if (this.streamAllocation.codec() != null) { // 请求正常,但流没有被关闭,则抛出异常
throw new IllegalStateException("Closing the body of " + response + " didn't close its backing stream. Bad interceptor?");
}
// 赋值重新构建请求
request = followUp;
priorResponse = response;
// 进入下一此循环
}
this.streamAllocation.release();
throw new IOException("Canceled");
}
从上面可以看出真个流程是一个死循环,但当首次请求环境正常时或者抛出几个异常时就会退出本次重连进入下一步或中断请求。
其中首个异常的判断依据recover方法这里也贴出来:
private boolean recover(IOException e, boolean requestSendStarted, Request userRequest) {
this.streamAllocation.streamFailed(e);
if (!this.client.retryOnConnectionFailure()) { // 我们自己设置是否需要重连的标识
return false;
} else if (requestSendStarted && userRequest.body() instanceof UnrepeatableRequestBody) { // 请求开始了但是请求体被标识UnrepeatableRequestBody
return false;
} else if (!this.isRecoverable(e, requestSendStarted)) { // 根据异常判断是否可以恢复连接
return false;
} else { // 是否有多余可用线路、无网络时回从这里返回false
return this.streamAllocation.hasMoreRoutes();
}
}
此外,RetryAndFollowUpInterceptor中还存在cancel方法,如RealCall中的cannel就是调用了它。其最终调用到了RealConnection的cancel()方法,其中关闭并释放了socket使用资源。好了本篇就到这了!大家元宵节快乐!