1.RealInterceptorChain初始化
Response getResponseWithInterceptorChain() throws IOException {
// Build a full stack of interceptors.
List<Interceptor> interceptors = new ArrayList<>();
interceptors.addAll(client.interceptors());
// 1.重试机制拦截器放在请求责任链中的第一位
interceptors.add(retryAndFollowUpInterceptor);
interceptors.add(new BridgeInterceptor(client.cookieJar()));
interceptors.add(new CacheInterceptor(client.internalCache()));
interceptors.add(new ConnectInterceptor(client));
if (!forWebSocket) {
interceptors.addAll(client.networkInterceptors());
}
interceptors.add(new CallServerInterceptor(forWebSocket));
Interceptor.Chain chain = new RealInterceptorChain(interceptors, null, null, null, 0,
originalRequest, this, eventListener, client.connectTimeoutMillis(),
client.readTimeoutMillis(), client.writeTimeoutMillis());
// 2.开始进行请求
return chain.proceed(originalRequest);
}
1、重试机制拦截器处于责任链中的第一位, 负责请求结束后的收尾工作, 包括根据请求中出现的异常采取是否启动重试机制
2.RealInterceptorChain.proceed
public Response proceed(Request request, StreamAllocation streamAllocation, HttpCodec httpCodec,
RealConnection connection) throws IOException {
if (index >= interceptors.size()) throw new AssertionError();
calls++;
// Call the next interceptor in the chain.
RealInterceptorChain next = new RealInterceptorChain(interceptors, streamAllocation, httpCodec,
connection, index + 1, request, call, eventListener, connectTimeout, readTimeout,
writeTimeout);
Interceptor interceptor = interceptors.get(index);
// 从责任链中遍历获取拦截器对request进行处理
Response response = interceptor.intercept(next);
return response;
}
3.RetryAndFollowUpInterceptor.intercept
@Override
public Response intercept(Chain chain) throws IOException {
Request request = chain.request();
RealInterceptorChain realChain = (RealInterceptorChain) chain;
Call call = realChain.call();
EventListener eventListener = realChain.eventListener();
StreamAllocation streamAllocation = new StreamAllocation(client.connectionPool(),
createAddress(request.url()), call, eventListener, callStackTrace);
this.streamAllocation = streamAllocation;
int followUpCount = 0;
Response priorResponse = null;
// 1.进入while循环
while (true) {
if (canceled) {
// 2.如果请求已经被取消, 释放连接池的资源
streamAllocation.release();
throw new IOException("Canceled");
}
Response response;
boolean releaseConnection = true;
try {
// 3.将request交给责任链上的下一个节点处理
response = realChain.proceed(request, streamAllocation, null, null);
// 4.是否需要释放连接池的标识, 正常情况下, 先不释放连接池
releaseConnection = false;
// 5.路由异常, 连接地址失败的异常
} catch (RouteException e) {
// 6.判断是否需要重试
if (!recover(e.getLastConnectException(), streamAllocation, false, request)) {
throw e.getFirstConnectException();
}
releaseConnection = false;
// 7.需要重试
continue;
// 8.IO异常
} catch (IOException e) {
// 9.除去连接异常的其他的IO异常
boolean requestSendStarted = !(e instanceof ConnectionShutdownException);
if (!recover(e, streamAllocation, requestSendStarted, request)) throw e;
releaseConnection = false;
// 10.需要重试
continue;
} finally {
// We're throwing an unchecked exception. Release any resources.
// 11.释放连接
if (releaseConnection) {
streamAllocation.streamFailed(null);
streamAllocation.release();
}
}
// 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();
}
Request followUp;
try {
followUp = followUpRequest(response, streamAllocation.route());
} catch (IOException e) {
streamAllocation.release();
throw e;
}
if (followUp == null) {
streamAllocation.release();
return response;
}
closeQuietly(response.body());
if (++followUpCount > MAX_FOLLOW_UPS) {
streamAllocation.release();
throw new ProtocolException("Too many follow-up requests: " + followUpCount);
}
if (followUp.body() instanceof UnrepeatableRequestBody) {
streamAllocation.release();
throw new HttpRetryException("Cannot retry streamed HTTP body", response.code());
}
if (!sameConnection(response, followUp.url())) {
streamAllocation.release();
streamAllocation = new StreamAllocation(client.connectionPool(),
createAddress(followUp.url()), call, eventListener, callStackTrace);
this.streamAllocation = streamAllocation;
} else if (streamAllocation.codec() != null) {
throw new IllegalStateException("Closing the body of " + response
+ " didn't close its backing stream. Bad interceptor?");
}
request = followUp;
priorResponse = response;
}
}
1、责任链上的其他节点在处理request时, 如果抛出异常, 进入**
catch(RouteException)或者catch(IOException)**中, 然后根据这两个异常分别进行处理. 然后判断是否需要进行重试机制
4.RouteException
这个异常发生在Request请求还没有发出去前, 就是打开Socket连接失败. 这个异常是okhttp自定义的异常, 是一个包裹类, 包裹住了建立连接失败中发生的各种Exception, 主要发生在ConnectInterceptor建立连接环节, 比如连接超时抛出的SocketTimeoutException, 包裹在RouteException中.
RouteException在RealConnection和StreamAllocation中被调用, 对应到具体的方法是RealConnection.connect方法和StreamAllocation.newStream
RealConnection.connect和StreamAllocation.newStream的调用链如下图:
异常在RealConnection.connect中抛出
4.1 RealConnection.connect
public void connect(int connectTimeout, int readTimeout, int writeTimeout,
int pingIntervalMillis, boolean connectionRetryEnabled, Call call,
EventListener eventListener) {
// 1.IllegalStateException
if (protocol != null) throw new IllegalStateException("already connected");
RouteException routeException = null;
List<ConnectionSpec> connectionSpecs = route.address().connectionSpecs();
ConnectionSpecSelector connectionSpecSelector = new ConnectionSpecSelector(connectionSpecs);
// 2.UnknownServiceException
throw new RouteException(new UnknownServiceException("..."));
while (true) {
try {
if (route.requiresTunnel()) {
connectTunnel(connectTimeout, readTimeout, writeTimeout, call, eventListener);
} else {
connectSocket(connectTimeout, readTimeout, call, eventListener);
}
establishProtocol(connectionSpecSelector, pingIntervalMillis, call, eventListener);
break;
} catch (IOException e) {
// 3.IOException
if (routeException == null) {
routeException = new RouteException(e);
} else {
routeException.addConnectException(e);
}
if (!connectionRetryEnabled || !connectionSpecSelector.connectionFailed(e)) {
throw routeException;
}
}
}
if (route.requiresTunnel() && rawSocket == null) {
ProtocolException exception = new ProtocolException("Too many tunnel connections attempted: "
+ MAX_TUNNEL_ATTEMPTS);
// 4.ProtocolException
throw new RouteException(exception);
}
}
会抛出四种异常:
1、IllegalStateException
2、UnknownServiceException 3、IOException 4、ProtocolException
RouteException由RealConnection.connect和StreamAllocation.newStream两个方法抛出, 而newStream方法又是由ConnectInterceptor的intercept方法调用, RealConnection.connect方法是与服务器建立连接, newStream是获取流, 所以是在连接拦截器中抛出.
**注意: **这个拦截器的作用是建立TCP连接, 抛出异常, 也就说明了真正的网络请求还没有发出去, 也就是打开Socket失败了, 比如连接超时抛出的SocketTimeoutException, 包裹在RouteException中
4.2 再次分析recover中的RouteException
@Override
public Response intercept(Chain chain) throws IOException {
try {
response = realChain.proceed(request, streamAllocation, null, null);
releaseConnection = false;
} 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.getFirstConnectException();
}
releaseConnection = false;
continue;
}
}
如果是RouteException, 调用recover方法时, 传入的第三个参数**请求是否已经发出的标识**为false, 表示如果发生RouteException时, 真正的请求并没有发送出去, 结合RouteException也可以看出, RouteException发生在RealConnection.connect和StreamAllocation.newStream中, 而这两个方法的调用源头都是在ConnectInterceptor中, 即TCP连接的阶段, 在这个阶段如果发生了异常, 将异常包装进RouteException中, 然后抛出, 接着看recover对RouteException的处理
4.3 recover
/**
* @param e: 当前发生的异常
* @param requestSendStarted: true:请求已经发送出去, false: 请求还未发送出去.
* 如果是RouteException, 则该值为false, 即请求还未发送出去
* @return true: 允许重试, false: 不允许重试
*/
private boolean recover(IOException e, StreamAllocation streamAllocation,
boolean requestSendStarted, Request userRequest) {
streamAllocation.streamFailed(e);
// 1.如果设置了不需要重试, 直接返回false
if (!client.retryOnConnectionFailure()) return false;
// 2.请求已经发出并且body内容只可以发送一次
// 如果当前异常是RouteException, 则requestSendStarted = false
if (requestSendStarted && userRequest.body() instanceof UnrepeatableRequestBody) return false;
// 3.根据异常类型判断是否可以重试
if (!isRecoverable(e, requestSendStarted)) return false;
// 4.判断是否还有可用路由, 如果没有, 即路由遍历完也没有连接成功
if (!streamAllocation.hasMoreRoutes()) return false;
// 5.可以重试
return true;
}
这里只考虑请求过程中的异常情况, 即第三个判断条件, 根据请求过程中出现的异常来判断是否可重试.
4.4 isRecoverable
/**
* 根据异常类型判断是否重试
* @param requestSendStarted: true: 请求已经发送, false: 请求未发送
* @return true: 允许重试, false: 不允许重试
*/
private boolean isRecoverable(IOException e, boolean requestSendStarted) {
// 1.如果是协议问题, 不允许重试
if (e instanceof ProtocolException) {
return false;
}
// 2.对于Socket超时异常, 如果请求未发送, 即当前异常是在建立连接阶段, 则允许重试
if (e instanceof InterruptedIOException) {
return e instanceof SocketTimeoutException && !requestSendStarted;
}
// 3.如果该异常是SSL握手异常
if (e instanceof SSLHandshakeException) {
// 4.如果证书出现问题, 则不能进行重试
if (e.getCause() instanceof CertificateException) {
return false;
}
}
// 4.如果是SSL握手未授权异常, 不能进行恢复
if (e instanceof SSLPeerUnverifiedException) {
return false;
}
return true;
}
1、如果发生协议、证书校验的异常, 则不允许重试
2、对于SocketTimeoutException异常 2.1 如果该异常是发生在TCP连接阶段, 即建立TCP连接超时, 此时允许重试, 返回true。 2.2 如果连接已经建立, 读取响应发生该异常, 此时requestSendStarted为true, 返回false, 即不允许重试