两种超时
在设置 HTTP 超时时,通常需要区分两种主要的超时类型:

-
连接超时
- 含义:客户端与服务器建立 TCP 连接(三次握手)所允许的最长时间。
- 场景:如果服务器不在线、网络不通或防火墙阻止,连接可能会失败,连接超时可以快速失败,而不是无限等待。
- 单位:毫秒。
-
读取/请求超时
- 含义:在连接建立成功后,客户端从服务器读取响应(包括响应头和响应体)所允许的最长时间,它也被称为
socketTimeout或requestTimeout。 - 场景:连接已经建立,但服务器处理请求很慢,或者在数据传输过程中网络变得很慢,读取超时可以防止客户端线程长时间阻塞。
- 单位:毫秒。
- 含义:在连接建立成功后,客户端从服务器读取响应(包括响应头和响应体)所允许的最长时间,它也被称为
使用 java.net.HttpURLConnection (JDK 内置)
HttpURLConnection 是 Java 标准库自带的 HTTP 客户端,从 Java 1.5 开始支持设置超时。
设置方法
通过 setConnectTimeout() 和 setReadTimeout() 方法来设置。

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.net.HttpURLConnection;
import java.net.URL;
public class HttpURLConnectionTimeoutExample {
public static void main(String[] args) {
String urlString = "https://www.example.com"; // 替换为你要请求的URL
try {
URL url = new URL(urlString);
HttpURLConnection connection = (HttpURLConnection) url.openConnection();
// 1. 设置连接超时时间 (单位: 毫秒)
// 5秒
connection.setConnectTimeout(5000);
// 2. 设置读取超时时间 (单位: 毫秒)
// 10秒
connection.setReadTimeout(10000);
// 设置请求方法
connection.setRequestMethod("GET");
// 获取响应码
int responseCode = connection.getResponseCode();
System.out.println("Response Code: " + responseCode);
// 读取响应内容
if (responseCode == HttpURLConnection.HTTP_OK) { // 200 OK
BufferedReader in = new BufferedReader(
new InputStreamReader(connection.getInputStream()));
String inputLine;
StringBuilder response = new StringBuilder();
while ((inputLine = in.readLine()) != null) {
response.append(inputLine);
}
in.close();
// 打印响应
System.out.println("Response: " + response.toString());
} else {
System.out.println("GET request failed");
}
} catch (java.net.SocketTimeoutException e) {
// 捕获超时异常
System.err.println("Request timed out!");
e.printStackTrace();
} catch (IOException e) {
System.err.println("An I/O error occurred: " + e.getMessage());
e.printStackTrace();
}
}
}
注意:HttpURLConnection 在 Java 9 之后有了一些改进(比如响应体可以更方便地流式读取),但其基本的超时设置方式保持不变。
使用 Apache HttpClient (推荐)
Apache HttpClient 是一个功能强大、灵活且被广泛使用的 HTTP 客户端库,它提供了更精细和强大的超时控制。
Maven 依赖
<dependency>
<groupId>org.apache.httpcomponents.client5</groupId>
<artifactId>httpclient5</artifactId>
<version>5.3.1</version> <!-- 使用最新版本 -->
</dependency>
<dependency>
<groupId>org.apache.httpcomponents.core5</groupId>
<artifactId>httpcore5</artifactId>
<version>5.2</version> <!-- 使用最新版本 -->
</dependency>
设置方法
在 RequestConfig 中设置超时,然后将这个配置应用到 HttpGet 或 HttpPost 请求上。
import org.apache.hc.client5.http.classic.methods.HttpGet;
import org.apache.hc.client5.http.config.RequestConfig;
import org.apache.hc.client5.http.impl.classic.CloseableHttpClient;
import org.apache.hc.client5.http.impl.classic.CloseableHttpResponse;
import org.apache.hc.client5.http.impl.classic.HttpClients;
import org.apache.hc.core5.http.HttpEntity;
import org.apache.hc.core5.http.io.entity.EntityUtils;
import org.apache.hc.core5.util.Timeout;
public class ApacheHttpClientTimeoutExample {
public static void main(String[] args) {
String url = "https://www.example.com";
// 1. 创建 RequestConfig 对象来配置超时
RequestConfig requestConfig = RequestConfig.custom()
// 连接超时: 5秒
.setConnectTimeout(Timeout.ofMilliseconds(5000))
// 建立连接后,从服务器获取响应的超时: 10秒
.setResponseTimeout(Timeout.ofMilliseconds(10000))
// Socket 超时 (与读取超时类似): 10秒
.setSocketTimeout(Timeout.ofMilliseconds(10000))
.build();
// 2. 创建 HttpClient 实例,并应用 RequestConfig
try (CloseableHttpClient httpClient = HttpClients.custom()
.setDefaultRequestConfig(requestConfig)
.build()) {
// 3. 创建请求对象
HttpGet httpGet = new HttpGet(url);
// 4. 执行请求
try (CloseableHttpResponse response = httpClient.execute(httpGet)) {
System.out.println("Response Code: " + response.getCode());
HttpEntity entity = response.getEntity();
if (entity != null) {
// 将响应体转换为字符串
String result = EntityUtils.toString(entity);
System.out.println("Response: " + result);
// 确保实体内容被完全消耗,以便连接可以重用
EntityUtils.consume(entity);
}
} catch (org.apache.hc.client5.http.socketTimeoutException e) {
System.err.println("Request timed out!");
e.printStackTrace();
}
} catch (Exception e) {
System.err.println("An error occurred: " + e.getMessage());
e.printStackTrace();
}
}
}
说明:

setConnectTimeout: 连接超时。setResponseTimeout: 从连接建立到收到第一个响应字节的总超时时间。setSocketTimeout: 数据传输过程中的超时时间,与setReadTimeout类似。
使用 OkHttp (现代流行选择)
OkHttp 是另一个非常流行、高效且易于使用的 HTTP 客户端,特别适合在 Android 和现代 Java 应用中使用。
Maven 依赖
<dependency>
<groupId>com.squareup.okhttp3</groupId>
<artifactId>okhttp</artifactId>
<version>4.12.0</version> <!-- 使用最新版本 -->
</dependency>
设置方法
在 OkHttpClient.Builder 中设置超时。
import okhttp3.OkHttpClient;
import okhttp3.Request;
import okhttp3.Response;
import java.io.IOException;
import java.util.concurrent.TimeUnit;
public class OkHttpTimeoutExample {
public static void main(String[] args) {
String url = "https://www.example.com";
// 1. 创建 OkHttpClient.Builder
OkHttpClient.Builder builder = new OkHttpClient.Builder();
// 2. 设置超时时间
// 连接超时: 5秒
builder.connectTimeout(5, TimeUnit.SECONDS);
// 读取超时: 10秒
builder.readTimeout(10, TimeUnit.SECONDS);
// 写入超时: 10秒 (适用于 POST 请求)
builder.writeTimeout(10, TimeUnit.SECONDS);
// 3. 构建 OkHttpClient
OkHttpClient client = builder.build();
// 4. 创建请求
Request request = new Request.Builder()
.url(url)
.build();
// 5. 执行请求
try (Response response = client.newCall(request).execute()) {
if (!response.isSuccessful()) {
throw new IOException("Unexpected code " + response);
}
// 打印响应
System.out.println("Response Code: " + response.code());
System.out.println("Response Body: " + response.body().string());
} catch (java.net.SocketTimeoutException e) {
System.err.println("Request timed out!");
e.printStackTrace();
} catch (IOException e) {
System.err.println("An I/O error occurred: " + e.getMessage());
e.printStackTrace();
}
}
}
说明:
connectTimeout: 连接超时。readTimeout: 读取响应的超时。writeTimeout: 发送请求体(如 POST 的 JSON 数据)的超时。
总结与对比
| 特性 | java.net.HttpURLConnection |
Apache HttpClient | OkHttp |
|---|---|---|---|
| 依赖 | JDK 内置,无需额外依赖 | 需要添加 httpclient5 依赖 |
需要添加 okhttp 依赖 |
| 易用性 | 较低,API 过时,手动管理资源 | 中等,需要构建 RequestConfig |
高,Builder 模式,非常直观 |
| 功能 | 基础功能 | 强大,高度可定制,支持连接池等 | 强大,支持 HTTP/2,连接池,WebSocket |
| 超时控制 | setConnectTimeout(), setReadTimeout() |
RequestConfig 中精细设置 |
OkHttpClient.Builder 中链式调用 |
| 推荐场景 | 简单脚本、不想引入第三方库的项目 | 企业级应用、需要复杂控制和历史兼容性的项目 | 现代应用、Android 开发、追求高性能和新特性的项目 |
最佳实践
- 总是设置超时:为了避免你的程序在网络不佳时卡住,为所有生产环境的 HTTP 请求设置合理的超时时间。
- 区分两种超时:根据你的应用场景设置不同的连接超时和读取超时,读取超时应该比连接超时更长。
- 处理异常:务必捕获
SocketTimeoutException或其等效异常,并进行适当的处理(如重试、记录日志或向用户提示)。 - 选择合适的库:
- 如果项目简单且不想引入依赖,
HttpURLConnection足够用。 - 如果是大型企业项目或需要非常精细的控制,Apache HttpClient 是一个稳健的选择。
- 对于新项目,尤其是需要高性能和现代特性的,OkHttp 通常是首选。
- 如果项目简单且不想引入依赖,
