1. 概述
本文将深入讲解 OkHttp 客户端中几种关键的超时(Timeout)配置方式。掌握这些超时机制,能有效避免请求堆积、资源耗尽等线上“踩坑”问题。
如果你还不熟悉 OkHttp 的基本使用,建议先阅读我们之前的 OkHttp 入门指南。
2. 连接超时(Connect Timeout)
连接超时指的是:客户端尝试与目标服务器建立 TCP 连接的最大等待时间。
✅ 默认值:OkHttpClient
的连接超时默认为 10 秒。
❌ 设置为 0 表示不设限,即永不超时,生产环境慎用。
可通过 OkHttpClient.Builder#connectTimeout()
方法自定义。
示例:触发连接超时
@Test
public void whenConnectTimeoutExceeded_thenSocketTimeoutException() {
OkHttpClient client = new OkHttpClient.Builder()
.connectTimeout(10, TimeUnit.MILLISECONDS)
.build();
Request request = new Request.Builder()
.url("http://203.0.113.1") // 无效地址,无法建立连接
.build();
Throwable thrown = catchThrowable(() -> client.newCall(request).execute());
assertThat(thrown).isInstanceOf(SocketTimeoutException.class);
}
⚠️ 注意:虽然异常是 SocketTimeoutException
,但它实际是由连接阶段超时引发的。这是 OkHttp 的设计行为,别被名字误导。
3. 读取超时(Read Timeout)
读取超时指的是:连接建立后,客户端等待服务器返回数据时,两次数据包之间的最大空闲时间。
简单说:服务器“卡住”不发数据,超过这个时间就断。
✅ 默认值:同样是 10 秒。
✅ 零值表示无限制。
使用 OkHttpClient.Builder#readTimeout()
配置。
示例:触发读取超时
@Test
public void whenReadTimeoutExceeded_thenSocketTimeoutException() {
OkHttpClient client = new OkHttpClient.Builder()
.readTimeout(10, TimeUnit.MILLISECONDS)
.build();
Request request = new Request.Builder()
.url("https://httpbin.org/delay/2") // 延迟 2 秒返回
.build();
Throwable thrown = catchThrowable(() -> client.newCall(request).execute());
assertThat(thrown).isInstanceOf(SocketTimeoutException.class);
}
服务器响应时间(2s) > 读取超时(10ms),触发超时。
4. 写入超时(Write Timeout)
写入超时指的是:客户端向服务器发送请求体(如 POST 数据)时,两次数据包之间的最大空闲时间。
适用于上传大文件或网络较差的场景。
✅ 默认值:10 秒。
✅ 零值表示无限制。
通过 OkHttpClient.Builder#writeTimeout()
设置。
示例:触发写入超时
@Test
public void whenWriteTimeoutExceeded_thenSocketTimeoutException() {
OkHttpClient client = new OkHttpClient.Builder()
.writeTimeout(10, TimeUnit.MILLISECONDS)
.build();
Request request = new Request.Builder()
.url("https://httpbin.org/delay/2")
.post(RequestBody.create(MediaType.parse("text/plain"), create1MBString()))
.build();
Throwable thrown = catchThrowable(() -> client.newCall(request).execute());
assertThat(thrown).isInstanceOf(SocketTimeoutException.class);
}
假设网络慢,10ms 内无法发完 1MB 数据,触发写入超时。
5. 调用超时(Call Timeout)
调用超时是对整个 HTTP 请求生命周期的总时限控制,涵盖:
- DNS 解析
- 建立连接(Connect)
- 发送请求体(Write)
- 服务器处理
- 接收响应体(Read)
✅ 默认值:0(无超时),需手动开启。
✅ 使用 OkHttpClient.Builder#callTimeout()
设置。
⚠️ 一旦触发,抛出的是 InterruptedIOException
,不是 SocketTimeoutException
,注意区分。
示例:触发调用超时
@Test
public void whenCallTimeoutExceeded_thenInterruptedIOException() {
OkHttpClient client = new OkHttpClient.Builder()
.callTimeout(1, TimeUnit.SECONDS)
.build();
Request request = new Request.Builder()
.url("https://httpbin.org/delay/2")
.build();
Throwable thrown = catchThrowable(() -> client.newCall(request).execute());
assertThat(thrown).isInstanceOf(InterruptedIOException.class);
}
总耗时超过 1 秒即中断,即使某个阶段(如 read)单独看未超时。
6. 按请求设置超时(Per-Request Timeout)
✅ 最佳实践:全局共用一个 OkHttpClient
实例,避免资源浪费。
但某些接口响应慢(如报表导出),不能因此拉高全局超时。此时应:
- 基于默认 client 创建新 builder
- 调整特定超时
- 构建临时 client 发起请求
示例:为特定请求延长超时
@Test
public void whenPerRequestTimeoutExtended_thenResponseSuccess() throws IOException {
OkHttpClient defaultClient = new OkHttpClient.Builder()
.readTimeout(1, TimeUnit.SECONDS)
.build();
Request request = new Request.Builder()
.url("https://httpbin.org/delay/2")
.build();
// 默认 client 超时失败
Throwable thrown = catchThrowable(() -> defaultClient.newCall(request).execute());
assertThat(thrown).isInstanceOf(InterruptedIOException.class);
// 为当前请求创建专用 client
OkHttpClient extendedTimeoutClient = defaultClient.newBuilder()
.readTimeout(5, TimeUnit.SECONDS)
.build();
Response response = extendedTimeoutClient.newCall(request).execute();
assertThat(response.code()).isEqualTo(200);
}
✅ newBuilder()
复用原配置,只修改所需项,简单粗暴有效。
7. 总结
本文梳理了 OkHttp 四类超时机制:
超时类型 | 作用阶段 | 默认值 | 触发异常 |
---|---|---|---|
Connect | 建立 TCP 连接 | 10 秒 | SocketTimeoutException |
Read | 接收响应数据时的空闲间隔 | 10 秒 | SocketTimeoutException |
Write | 发送请求数据时的空闲间隔 | 10 秒 | SocketTimeoutException |
Call | 整个请求生命周期 | 无 | InterruptedIOException |
✅ 核心要点:
- 生产环境务必显式设置超时,避免线程阻塞。
callTimeout
是总闸,建议对关键接口设置。- 利用
newBuilder()
实现 per-request 级别的灵活控制。
所有示例代码已上传至 GitHub:https://github.com/eugenp/tutorials/tree/master/libraries-http