Retrofit大请求未按writeTimeout中断:如何15秒内停止发送
解决Retrofit+OkHttp大请求超时未按配置触发的问题
看起来你遇到的问题是OkHttp的超时配置没有按预期限制大请求的总时长——这其实是因为你当前设置的几个超时参数和你想要的「整个请求总时长限制」不是一回事。我来帮你拆解清楚并给出解决方案:
先搞懂你当前配置的超时参数到底管什么
你设置的connectTimeout、readTimeout、writeTimeout各自有明确的作用范围,并不是限制整个请求的总时长:
connectTimeout:仅限制建立TCP连接的时间,连接成功后这个超时就不再生效readTimeout:限制等待服务器返回数据的间隔时间(比如服务器发了一部分数据后停顿,超过15秒没发下一部分才会超时),不是总读取时间writeTimeout:限制每次写入请求体数据块的间隔时间,不是整个大请求体写入的总时间
所以当你发送大请求时,只要每次写入数据块的操作都在15秒内完成,整个写入过程总时长超过15秒也不会触发超时,这就是你看到2分钟后才返回超时提示的原因。
解决方案一:升级OkHttp版本,使用callTimeout(推荐)
OkHttp从3.12版本开始新增了callTimeout参数,这个参数会直接限制从请求发起到响应结束的总时长,完全符合你的需求。
步骤1:升级依赖版本
把OkHttp相关依赖升级到3.12+(和你当前用的Retrofit 2.5.0兼容,推荐用3.14.9这个稳定版本):
implementation 'com.squareup.retrofit2:retrofit:2.5.0' implementation 'com.squareup.retrofit2:converter-jackson:2.5.0' implementation 'com.squareup.okhttp3:okhttp:3.14.9' implementation 'com.squareup.okhttp3:logging-interceptor:3.14.9'
步骤2:修改OkHttp配置
在OkHttpClient.Builder中添加callTimeout配置:
public OkHttpClient getOkHttp() { HttpLoggingInterceptor interceptor = new HttpLoggingInterceptor(); interceptor.setLevel(HttpLoggingInterceptor.Level.BODY); okClient = new OkHttpClient.Builder() .connectTimeout(15, TimeUnit.SECONDS) .readTimeout(15, TimeUnit.SECONDS) .writeTimeout(15, TimeUnit.SECONDS) .callTimeout(15, TimeUnit.SECONDS) // 添加这个总超时配置 .addInterceptor(new GzipRequestInterceptor()) .addInterceptor(interceptor) .build(); return okClient; }
这样不管是连接、写入还是读取环节,整个请求的总时长超过15秒就会自动中断并返回超时错误。
解决方案二:手动定时器取消请求(适合无法升级依赖的场景)
如果你暂时不能升级OkHttp版本,可以在发起同步请求时,用定时器在15秒后手动调用Call.cancel()来中断请求:
Call<DoReadResult> x = RetrofitFactory.getInstance().getlimit().DoReading(data, "Something"); // 启动定时器,15秒后取消请求 ScheduledExecutorService executor = Executors.newSingleThreadScheduledExecutor(); Future<?> future = executor.schedule(() -> { if (!x.isExecuted() || !x.isCanceled()) { x.cancel(true); } }, 15, TimeUnit.SECONDS); try { response = x.execute(); // 请求成功完成,及时取消定时器避免资源浪费 future.cancel(true); } catch (IOException e) { // 区分是取消导致的异常还是其他IO异常 if (x.isCanceled()) { System.out.println("请求已超时并被取消"); } else { e.printStackTrace(); } } finally { executor.shutdown(); }
这种方式需要手动管理定时器生命周期,注意在请求完成后及时取消定时器。
内容的提问来源于stack exchange,提问作者Amir133
相关产品推荐
相关产品推荐

