OKHTTP解析之RetryAndFollowUpInterceptor重试机制

本文涉及的产品
公共DNS(含HTTPDNS解析),每月1000万次HTTP解析
云解析 DNS,旗舰版 1个月
全局流量管理 GTM,标准版 1个月
简介: 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);
相关文章
|
缓存 网络协议 API
Android 网络优化,使用 HTTPDNS 优化 DNS,从原理到 OkHttp 集成
Android 网络优化,使用 HTTPDNS 优化 DNS,从原理到 OkHttp 集成一、前言谈到优化,首先第一步,肯定是把一个大功能,拆分成一个个细小的环节,再单个拎出来找到可以优化的点,App 的网络优化也是如此。
3267 0
|
Java Android开发 安全
Android八门神器(一):OkHttp框架源码解析
HTTP是我们交换数据和媒体流的现代应用网络,有效利用HTTP可以使我们节省带宽和更快地加载数据,Square公司开源的OkHttp网络请求是有效率的HTTP客户端。
1439 0
|
缓存 Java 调度
okhttp源码解析
okhttp介绍 OkHttp是一个非常优秀的网络请求框架,已被谷歌加入到Android的源码中。目前比较流行的Retrofit也是默认使用OkHttp的。
1714 0
|
JSON Java Android开发
OkHttp框架从入门到放弃,解析图片使用Picasso裁剪,二次封装OkHttpUtils,Post提交表单数据
OkHttp框架从入门到放弃,解析图片使用Picasso裁剪,二次封装OkHttpUtils,Post提交表单数据 我们这片博文就来聊聊这个反响很不错的OkHttp了,标题是我恶搞的,本篇将着重详细的分析,探索OkHttp这个框架的使用和封装 一.
2348 0
|
22天前
|
缓存 Java 程序员
Map - LinkedHashSet&Map源码解析
Map - LinkedHashSet&Map源码解析
55 0
|
22天前
|
算法 Java 容器
Map - HashSet & HashMap 源码解析
Map - HashSet & HashMap 源码解析
47 0
|
22天前
|
存储 Java C++
Collection-PriorityQueue源码解析
Collection-PriorityQueue源码解析
53 0
|
22天前
|
安全 Java 程序员
Collection-Stack&Queue源码解析
Collection-Stack&Queue源码解析
64 0
|
3天前
|
消息中间件 缓存 安全
Future与FutureTask源码解析,接口阻塞问题及解决方案
【11月更文挑战第5天】在Java开发中,多线程编程是提高系统并发性能和资源利用率的重要手段。然而,多线程编程也带来了诸如线程安全、死锁、接口阻塞等一系列复杂问题。本文将深度剖析多线程优化技巧、Future与FutureTask的源码、接口阻塞问题及解决方案,并通过具体业务场景和Java代码示例进行实战演示。
19 3
|
20天前
|
存储
让星星⭐月亮告诉你,HashMap的put方法源码解析及其中两种会触发扩容的场景(足够详尽,有问题欢迎指正~)
`HashMap`的`put`方法通过调用`putVal`实现,主要涉及两个场景下的扩容操作:1. 初始化时,链表数组的初始容量设为16,阈值设为12;2. 当存储的元素个数超过阈值时,链表数组的容量和阈值均翻倍。`putVal`方法处理键值对的插入,包括链表和红黑树的转换,确保高效的数据存取。
47 5

推荐镜像

更多