okhttp read timed out 重试方案解疑

疑问

使用okhttp 抓取数据场景中,偶发Read timed out异常。正常的做法是增加重试机制。

在查看文档过程中,发现okhttp 默认会注册RetryAndFollowUpInterceptor ,字面上是支持重试的

那么,为什么timed out 异常不会重试,RetryAndFollowUpInterceptor 是干啥的?

RetryAndFollowUpInterceptor分析

能做什么

This interceptor recovers from failures and follows redirects as necessary.

  1. add authentication headers

    // 针对未授权的异常(HTTP Status-Code .401: Unauthorized),尝试调用authenticate(), 继续请求操作
    client.authenticator().authenticate(route, userResponse);
    
  2. follow redirects

    /**
     * 针对重定向的异常
     * HTTP Status-Code 301: Moved Permanently.
     * HTTP Status-Code 302: Temporary Redirect.
     * HTTP Status-Code 303: See Other.
     * 通过重新构造request情况,达到自动跳转的目的
     */
    String location = userResponse.header("Location");
    HttpUrl url = userResponse.request().url().resolve(location);
    
  3. handle a client request timeout(稀有场景)

    case HTTP_CLIENT_TIMEOUT:
    // 408's are rare in practice, but some servers like HAProxy use this response code. The
    // spec says that we may repeat the request without modifications. Modern browsers also
    // repeat the request (even non-idempotent ones.)
    // 注意:此处的Timeout 不是上述的SocketTimeout...
    

参考:okhttp3.internal.http.RetryAndFollowUpInterceptor#followUpRequest

不能做什么

遇到如下的异常:ProtocolException、InterruptedIOException、SSLHandshakeException、CertificateException

称之为:the failure is permanent

if (e instanceof InterruptedIOException) {
    return e instanceof SocketTimeoutException && !requestSendStarted;
}

参考:okhttp3.internal.http.RetryAndFollowUpInterceptor#recover

SocketTimeoutException 方案

  1. 根据情况,适当调整timeout设置

    new OkHttpClient.Builder()         
        .connectTimeout(10, TimeUnit.SECONDS)
        .writeTimeout(5, TimeUnit.SECONDS)
        .readTimeout(10, TimeUnit.SECONDS)
        .build();
    
    
  2. 增加重试机制,对网络的波动进行容错

    实现Interceptor接口,对SocketTimeoutException catch 重试。

总结

通过上述的分析,RetryAndFollowUpInterceptor 解决的是http 协议应用层重试问题,而read timed out 通讯协议层的问题。解决timeout 对于RetryAndFollowUpInterceptor 不是职责内的功能。

其他

okhttp3.Interceptor 注册

Response getResponseWithInterceptorChain() throws IOException {
	// Build a full stack of interceptors.
	List<Interceptor> interceptors = new ArrayList<>();
    // 自定义的拦截器优先执行
	interceptors.addAll(client.interceptors());
	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());
	// 调用链发起调用
	return chain.proceed(originalRequest);
}

参考

浅析 OkHttp 拦截器之 RetryAndFollowUpInterceptor

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
好的,针对你的问题,我可以给出以下的基于 OkHttp重试拦截器实现: ```java public class RetryInterceptor implements Interceptor { private final int maxRetryCount; private final long retryInterval; public RetryInterceptor(int maxRetryCount, long retryInterval) { this.maxRetryCount = maxRetryCount; this.retryInterval = retryInterval; } @Override public Response intercept(Chain chain) throws IOException { Request request = chain.request(); Response response = null; IOException exception = null; int tryCount = 0; while (tryCount <= maxRetryCount) { try { response = chain.proceed(request); if (response.isSuccessful()) { return response; } } catch (IOException e) { exception = e; } tryCount++; if (tryCount <= maxRetryCount) { try { Thread.sleep(retryInterval); } catch (InterruptedException e) { Thread.currentThread().interrupt(); throw new RuntimeException(e); } } } if (exception != null) { throw exception; } throw new IOException("Request failed after " + maxRetryCount + " attempts."); } } ``` 在这个拦截器中,我们可以通过传入最大重试次数和重试间隔时间来配置拦截器。当请求失败时,会在一定时间后重新尝试请求,直到达到最大重试次数或请求成功为止。如果最大重试次数用完后仍然失败,则抛出异常。 使用这个拦截器需要在 OkHttpClient 中添加: ```java OkHttpClient client = new OkHttpClient.Builder() .addInterceptor(new RetryInterceptor(3, 1000)) .build(); ``` 这样就可以在 OkHttp 的请求中添加重试功能了。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值