Contents

Apache HttpClient 超时

1. 概述

本教程将展示如何使用 Apache HttpClient 4 配置超时

2. HttpClient 4.3之前配置超时

2.1. 原始String参数

在 4.3 版本出来之前,HttpClient带有很多配置参数,所有这些都可以通过通用的、类似Map的方式进行设置。

有3 个超时参数需要配置

DefaultHttpClient httpClient = new DefaultHttpClient();
int timeout = 5; // seconds
HttpParams httpParams = httpClient.getParams();
httpParams.setParameter(
  CoreConnectionPNames.CONNECTION_TIMEOUT, timeout * 1000);
httpParams.setParameter(
  CoreConnectionPNames.SO_TIMEOUT, timeout * 1000);
httpParams.setParameter(
  ClientPNames.CONN_MANAGER_TIMEOUT, new Long(timeout * 1000));

2.2. API

这些参数中更重要的——即前两个——也可以通过更安全的 API 来设置:

DefaultHttpClient httpClient = new DefaultHttpClient();
int timeout = 5; // seconds
HttpParams httpParams = httpClient.getParams();
HttpConnectionParams.setConnectionTimeout(
  httpParams, timeout * 1000); // http.connection.timeout
HttpConnectionParams.setSoTimeout(
  httpParams, timeout * 1000); // http.socket.timeout

第三个参数在HttpConnectionParams中没有自定义设置器,仍然需要通过setParameter方法手动设置。

3. 使用新的 4.3 配置超时.建造者

4.3 中引入的流畅的构建器 API 提供了在高级别设置超时的正确方法

int timeout = 5;
RequestConfig config = RequestConfig.custom()
  .setConnectTimeout(timeout * 1000)
  .setConnectionRequestTimeout(timeout * 1000)
  .setSocketTimeout(timeout * 1000).build();
CloseableHttpClient client = 
  HttpClientBuilder.create().setDefaultRequestConfig(config).build();

这是以类型安全和可读的方式配置所有三个超时的推荐方法。

4. 超时属性解释

现在,让我们解释一下这些不同类型的超时意味着什么:

  • Connection Timeout ( http.connection.timeout ) – 与远程主机建立连接的时间
  • Socket Timeout ( http.socket.timeout ) – 建立连接后等待数据的时间;两个数据包之间不活动的最长时间
  • Connection Manager Timeout ( http.connection-manager.timeout ) – 等待来自连接管理器/池的连接的时间

前两个参数——连接和套接字超时——是最重要的。但是,在高负载情况下,设置获取连接的超时时间肯定很重要,这就是为什么不应该忽略第三个参数的原因。

5. 使用HttpClient

配置完成后,我们现在可以使用客户端执行 HTTP 请求了:

HttpGet getMethod = new HttpGet("http://host:8080/path");
HttpResponse response = httpClient.execute(getMethod);
System.out.println(
  "HTTP Status of response: " + response.getStatusLine().getStatusCode());

使用之前定义的客户端,与主机的连接将在 5 秒后超时。此外,如果建立了连接但没有收到数据,超时时间也会增加5 秒

请注意,连接超时将导致org.apache.http.conn.ConnectTimeoutException被抛出,而套接字超时将导致java.net.SocketTimeoutException

6. 硬超时

虽然在建立 HTTP 连接和不接收数据时设置超时非常有用,但有时我们需要为整个请求设置硬超时

例如,下载一个可能很大的文件就属于这一类。在这种情况下,连接可能会成功建立,数据可能会一直通过,但我们仍然需要确保操作不会超过某个特定的时间阈值。

HttpClient没有任何配置允许我们为请求设置整体超时;但是,它确实为请求提供了中止功能,因此我们可以利用该机制来实现一个简单的超时机制:

HttpGet getMethod = new HttpGet(
  "http://localhost:8080/httpclient-simple/api/bars/1");
int hardTimeout = 5; // seconds
TimerTask task = new TimerTask() {
    @Override
    public void run() {
        if (getMethod != null) {
            getMethod.abort();
        }
    }
};
new Timer(true).schedule(task, hardTimeout * 1000);
HttpResponse response = httpClient.execute(getMethod);
System.out.println(
  "HTTP Status of response: " + response.getStatusLine().getStatusCode());

我们使用java.util.Timerjava.util.TimerTask来设置一个简单的延迟任务,该任务在 5 秒硬超时后中止 HTTP GET 请求。

7. 超时和 DNS 循环——需要注意的事情

一些较大的域将使用 DNS 循环配置是很常见的——本质上是将相同的域映射到多个 IP 地址。这为针对此类域的超时引入了新挑战,仅仅是因为 HttpClient 将尝试连接到该超时的域的方式:

  • HttpClient 获取到该域的 IP 路由列表
  • 它尝试第一个- 超时(使用我们配置的超时)
  • 它会尝试第二个- 这也超时
  • 等等 …

因此,如您所见——整体操作不会在我们预期的时候超时。相反——当所有可能的路由都超时时,它会超时。更重要的是——这将对客户端完全透明地发生(除非您在 DEBUG 级别配置了日志)。

这是一个简单的示例,您可以运行并复制此问题:

int timeout = 3;
RequestConfig config = RequestConfig.custom().
  setConnectTimeout(timeout * 1000).
  setConnectionRequestTimeout(timeout * 1000).
  setSocketTimeout(timeout * 1000).build();
CloseableHttpClient client = HttpClientBuilder.create()
  .setDefaultRequestConfig(config).build();
HttpGet request = new HttpGet("http://www.google.com:81");
response = client.execute(request);

您会注意到带有 DEBUG 日志级别的重试逻辑:

DEBUG o.a.h.i.c.HttpClientConnectionOperator - Connecting to www.google.com/173.194.34.212:81
DEBUG o.a.h.i.c.HttpClientConnectionOperator - 
 Connect to www.google.com/173.194.34.212:81 timed out. Connection will be retried using another IP address
DEBUG o.a.h.i.c.HttpClientConnectionOperator - Connecting to www.google.com/173.194.34.208:81
DEBUG o.a.h.i.c.HttpClientConnectionOperator - 
 Connect to www.google.com/173.194.34.208:81 timed out. Connection will be retried using another IP address
DEBUG o.a.h.i.c.HttpClientConnectionOperator - Connecting to www.google.com/173.194.34.209:81
DEBUG o.a.h.i.c.HttpClientConnectionOperator - 
 Connect to www.google.com/173.194.34.209:81 timed out. Connection will be retried using another IP address
//...