OKHTTP解析之RetryAndFollowUpInterceptor重试机制

本文涉及的产品
云解析DNS,个人版 1个月
全局流量管理 GTM,标准版 1个月
公共DNS(含HTTPDNS解析),每月1000万次HTTP解析
简介: OKHTTP解析之RetryAndFollowUpInterceptor重试机制
// Attach the prior response if it exists. Such responses never have a body.
//5. 根据上一个Response结果构建一个新的response对象,且这个对象的body为空
if (priorResponse != null) {
response = response.newBuilder()
.priorResponse(priorResponse.newBuilder()
.body(null)
.build())
.build();
}
//6. 根据请求码创建一个新的请求,以供下一次重试请求使用
Exchange exchange = Internal.instance.exchange(response);
Route route = exchange != null ? exchange.connection().route() : null;
Request followUp = followUpRequest(response, route);
//7. 如果第六步构建的出来的Request为空,则不再进行,直接返回Response
if (followUp == null) {
if (exchange != null && exchange.isDuplex()) {
transmitter.timeoutEarlyExit();
}
return response;
}
//8. 构建的Request对象出存在请求body且为一次性请求,则直接返回Response,也不进行重试。
RequestBody followUpBody = followUp.body();
if (followUpBody != null && followUpBody.isOneShot()) {
return response;
}
closeQuietly(response.body());
if (transmitter.hasExchange()) {
exchange.detachWithViolence();
}
// 9. 判断当前重试次数是否已经到达最大次数(默认20),如果到达,则直接抛出异常
if (++followUpCount > MAX_FOLLOW_UPS) {
throw new ProtocolException("Too many follow-up requests: " + followUpCount);
}
//10. 如果上述没有抛出异常或者中断循环,则进入while循环,开始下一次重试过程
request = followUp;
priorResponse = response;
}
}
1. 首先尝试去创建一个请求流,准备链接;
2. 开始执行,进入后续拦截器,真正进行网络请求;
3. 发生RouteException:路由链接出现异常,且多次重试后没有一次正常;然后判断当前路由异常是否可恢复,不可恢复的时候抛出FirstConnectException,可恢复则进行充实,具体判断逻辑如下:
/**
• 是否可恢复
*/
private boolean recover(IOException e, Transmitter transmitter,
boolean requestSendStarted, Request userRequest) {
// The application layer has forbidden retries.
if (!client.retryOnConnectionFailure()) return false;
// We can’t send the request body again.
if (requestSendStarted && requestIsOneShot(e, userRequest)) return false;
// This exception is fatal.
if (!isRecoverable(e, requestSendStarted)) return false;
// No more routes to attempt.
if (!transmitter.canRetry()) return false;
// For failure recovery, use the same route selector with a new connection.
return true;
}
private boolean requestIsOneShot(IOException e, Request userRequest) {
RequestBody requestBody = userRequest.body();
return (requestBody != null && requestBody.isOneShot())
|| e instanceof FileNotFoundException;
}
private boolean isRecoverable(IOException e, boolean requestSendStarted) {
// 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 && !requestSendStarted;
}
// 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;
}

上述代码逻辑如下:

  1. 首先判断是否允许重试,根据我们创建请求的时候配置的重试开关,则不允许重试;
  2. 第二层判断如果请求已经开始,且当前请求最多只能被发送一次的情况下,则不允许重试;
  3. 判断当前请求是否可恢复的,以下异常场景不可恢复:
    a. ProtocolException,协议异常
    b. SocketTimeoutException,Socket链接超时且请求没有开始
    c. SSLHandshakeException && CertificateException :
    表示和服务端约定的安全级别不匹配异常,引起基本为证书引起的,这种链接是不可用的。
    d. SSLPeerUnverifiedException
    对等实体认证异常,也就是说对等个体没有被验证,类似没有证书,
    或者在握手期间没有建立对等个体验证;
  4. 判断是否存在其他可重试的路由,如果不存在,不允许重试;
  5. 不属于上述情况判断可以重试;
4. 发生IOException异常,是否可恢复判断逻辑参照上述RouteException判断逻辑;
5. 根据上一个Response结果构建一个新的response对象,且这个对象的body为空;
6. 根据请求码创建一个新的请求,以供下一次重试请求使用:会根据上一次的请求结果添加认证头信息,跟踪重定向或处理客户端请求超时等。

代码如下:

private Request followUpRequest(Response userResponse, @Nullable Route route) throws IOException {
if (userResponse == null) throw new IllegalStateException();
int responseCode = userResponse.code();
final String method = userResponse.request().method();
switch (responseCode) {
case HTTP_PROXY_AUTH:
Proxy selectedProxy = route != null
? route.proxy()
: client.proxy();
if (selectedProxy.type() != Proxy.Type.HTTP) {
throw new ProtocolException(“Received HTTP_PROXY_AUTH (407) code while not using proxy”);
}
return client.proxyAuthenticator().authenticate(route, userResponse);
case HTTP_UNAUTHORIZED:
return client.authenticator().authenticate(route, userResponse);
case HTTP_PERM_REDIRECT:
case HTTP_TEMP_REDIRECT:
// “If the 307 or 308 status code is received in response to a request other than GET
// or HEAD, the user agent MUST NOT automatically redirect the request”
if (!method.equals(“GET”) && !method.equals(“HEAD”)) {
return null;
}
// fall-through
case HTTP_MULT_CHOICE:
case HTTP_MOVED_PERM:
case HTTP_MOVED_TEMP:
case HTTP_SEE_OTHER:
// Does the client allow redirects?
if (!client.followRedirects()) return null;
String location = userResponse.header(“Location”);
if (location == null) return null;
HttpUrl url = userResponse.request().url().resolve(location);
相关文章
|
Java Android开发 安全
Android八门神器(一):OkHttp框架源码解析
HTTP是我们交换数据和媒体流的现代应用网络,有效利用HTTP可以使我们节省带宽和更快地加载数据,Square公司开源的OkHttp网络请求是有效率的HTTP客户端。
1391 0
|
缓存 Java 调度
okhttp源码解析
okhttp介绍 OkHttp是一个非常优秀的网络请求框架,已被谷歌加入到Android的源码中。目前比较流行的Retrofit也是默认使用OkHttp的。
1682 0
|
JSON Java Android开发
OkHttp框架从入门到放弃,解析图片使用Picasso裁剪,二次封装OkHttpUtils,Post提交表单数据
OkHttp框架从入门到放弃,解析图片使用Picasso裁剪,二次封装OkHttpUtils,Post提交表单数据 我们这片博文就来聊聊这个反响很不错的OkHttp了,标题是我恶搞的,本篇将着重详细的分析,探索OkHttp这个框架的使用和封装 一.
2328 0
|
9天前
|
机器学习/深度学习 缓存 算法
netty源码解解析(4.0)-25 ByteBuf内存池:PoolArena-PoolChunk
netty源码解解析(4.0)-25 ByteBuf内存池:PoolArena-PoolChunk
|
11天前
|
XML Java 数据格式
深度解析 Spring 源码:从 BeanDefinition 源码探索 Bean 的本质
深度解析 Spring 源码:从 BeanDefinition 源码探索 Bean 的本质
23 3
|
3天前
|
Java 数据库连接 Spring
Spring 整合 MyBatis 底层源码解析
Spring 整合 MyBatis 底层源码解析
|
3天前
|
NoSQL Java Redis
【源码解析】自动配置的这些细节都不知道,别说你会 springboot
【源码解析】自动配置的这些细节都不知道,别说你会 springboot
|
10天前
|
存储 NoSQL 算法
Redis(四):del/unlink 命令源码解析
Redis(四):del/unlink 命令源码解析
|
11天前
|
XML Java 数据格式
深度解析 Spring 源码:揭秘 BeanFactory 之谜
深度解析 Spring 源码:揭秘 BeanFactory 之谜
16 1
|
21天前
|
SQL 缓存 算法
【源码解析】Pandas PandasObject类详解的学习与实践
【源码解析】Pandas PandasObject类详解的学习与实践

推荐镜像

更多