HttpClient 有 3 个超时时间设置,通过配置 RequestConfig 即可配置请求的超时时间,各个参数的作用如下:
connectTimeout:请求连接超时时间,超时会抛出 org.apache.http.conn.ConnectTimeoutException: Connect to 127.0.0.1:8083 [/127.0.0.1] failed: connect timed out
异常。例如请求本地不存在的一个服务:http://127.0.0.1:8083
socketTimeout:接收包的超时时间。服务端与客户端传输数据包之间的时间间隔,超过这个间隔将抛出 java.net.SocketTimeoutException: Read timed out
connectionRequestTimeout:在使用线程池场景下,从连接池获取到连接的超时时间,时间超出将抛出 org.apache.http.conn.ConnectionPoolTimeoutException: Timeout waiting for connection from pool
,线程池默认最大的连接数是 20。
PoolingHttpClientConnectionManager 作为连接池
maxTotal:最大连接数
defaultMaxPerRoute:每个路由最大连接数
<dependency>
<groupId>org.apache.httpcomponentsgroupId>
<artifactId>httpclientartifactId>
<version>4.5.10version>
dependency>
static {
LayeredConnectionSocketFactory sslsf = null;
try {
sslsf = new SSLConnectionSocketFactory(SSLContext.getDefault());
} catch (NoSuchAlgorithmException e) {
e.printStackTrace();
}
Registry<ConnectionSocketFactory> socketFactoryRegistry = RegistryBuilder.<ConnectionSocketFactory>create()
.register("https", sslsf)
.register("http", new PlainConnectionSocketFactory())
.build();
connectionManager = new PoolingHttpClientConnectionManager(socketFactoryRegistry);
connectionManager.setMaxTotal(2);
connectionManager.setDefaultMaxPerRoute(150);
}
public static void requestNotExistURL(String url,int connectTimeOut,int connectionRequestTimeout,int socketTimeout){
try {
RequestConfig requestConfig = RequestConfig.custom()
.setConnectTimeout(connectTimeOut)
.setConnectionRequestTimeout(connectionRequestTimeout)
.setSocketTimeout(socketTimeout)
.build();
CloseableHttpClient httpClient = HttpClients.custom().setConnectionManager(connectionManager)
.setDefaultRequestConfig(requestConfig).build();
CloseableHttpResponse response = null;
HttpGet httpget = new HttpGet(url);
response = httpClient.execute(httpget);
}catch (Exception e){
e.printStackTrace();
}
}
private static PoolingHttpClientConnectionManager connectionManager = null;
static {
LayeredConnectionSocketFactory sslsf = null;
try {
sslsf = new SSLConnectionSocketFactory(SSLContext.getDefault());
} catch (NoSuchAlgorithmException e) {
e.printStackTrace();
}
Registry<ConnectionSocketFactory> socketFactoryRegistry = RegistryBuilder.<ConnectionSocketFactory>create()
.register("https", sslsf)
.register("http", new PlainConnectionSocketFactory())
.build();
connectionManager = new PoolingHttpClientConnectionManager(socketFactoryRegistry);
connectionManager.setMaxTotal(2);
connectionManager.setDefaultMaxPerRoute(150);
}
public static void requestGoogle(){
long startTime = System.currentTimeMillis();
try {
RequestConfig requestConfig = RequestConfig.custom()
.setConnectTimeout(1000)
.setConnectionRequestTimeout(2000)
.setSocketTimeout(3000)
.build();
CloseableHttpClient httpClient = HttpClients.custom().setConnectionManager(connectionManager)
.setDefaultRequestConfig(requestConfig).build();
CloseableHttpResponse response = null;
HttpGet httpget = new HttpGet("http://www.google.com");
response = httpClient.execute(httpget);
}catch (Exception e){
e.printStackTrace();
}finally {
System.out.println(System.currentTimeMillis() - startTime);
}
}
18:18:51.446 [main] DEBUG org.apache.http.client.protocol.RequestAddCookies - CookieSpec selected: default
18:18:51.461 [main] DEBUG org.apache.http.client.protocol.RequestAuthCache - Auth cache not set in the context
18:18:51.463 [main] DEBUG org.apache.http.impl.conn.PoolingHttpClientConnectionManager - Connection request: [route: {}->http://www.google.com:80][total kept alive: 0; route allocated: 0 of 150; total allocated: 0 of 2]
18:18:51.480 [main] DEBUG org.apache.http.impl.conn.PoolingHttpClientConnectionManager - Connection leased: [id: 0][route: {}->http://www.google.com:80][total kept alive: 0; route allocated: 1 of 150; total allocated: 1 of 2]
18:18:51.484 [main] DEBUG org.apache.http.impl.execchain.MainClientExec - Opening connection {}->http://www.google.com:80
18:19:02.547 [main] DEBUG org.apache.http.impl.conn.DefaultManagedHttpClientConnection - http-outgoing-0: Shutdown connection
18:19:02.547 [main] DEBUG org.apache.http.impl.execchain.MainClientExec - Connection discarded
18:19:02.547 [main] DEBUG org.apache.http.impl.conn.PoolingHttpClientConnectionManager - Connection released: [id: 0][route: {}->http://www.google.com:80][total kept alive: 0; route allocated: 0 of 150; total allocated: 0 of 2]
11208
java.net.UnknownHostException: www.google.com
at java.net.Inet6AddressImpl.lookupAllHostAddr(Native Method)
at java.net.InetAddress$2.lookupAllHostAddr(InetAddress.java:929)
at java.net.InetAddress.getAddressesFromNameService(InetAddress.java:1324)
at java.net.InetAddress.getAllByName0(InetAddress.java:1277)
at java.net.InetAddress.getAllByName(InetAddress.java:1193)
at java.net.InetAddress.getAllByName(InetAddress.java:1127)
at org.apache.http.impl.conn.SystemDefaultDnsResolver.resolve(SystemDefaultDnsResolver.java:45)
at org.apache.http.impl.conn.DefaultHttpClientConnectionOperator.connect(DefaultHttpClientConnectionOperator.java:112)
at org.apache.http.impl.conn.PoolingHttpClientConnectionManager.connect(PoolingHttpClientConnectionManager.java:374)
at org.apache.http.impl.execchain.MainClientExec.establishRoute(MainClientExec.java:393)
at org.apache.http.impl.execchain.MainClientExec.execute(MainClientExec.java:236)
at org.apache.http.impl.execchain.ProtocolExec.execute(ProtocolExec.java:186)
at org.apache.http.impl.execchain.RetryExec.execute(RetryExec.java:89)
at org.apache.http.impl.execchain.RedirectExec.execute(RedirectExec.java:110)
at org.apache.http.impl.client.InternalHttpClient.doExecute(InternalHttpClient.java:185)
at org.apache.http.impl.client.CloseableHttpClient.execute(CloseableHttpClient.java:83)
at org.apache.http.impl.client.CloseableHttpClient.execute(CloseableHttpClient.java:108)
at com.company.Main.requestGoogle(Main.java:58)
at com.company.Main.main(Main.java:91)
当请求被防火墙屏蔽的网站 Google 时,发现方法执行了 10s 才结束,上面 3 个超时时间都失去了作用。
因为请求连接之前,有域名到 IP 的 DNS 解析过程占据了大部分时间。
将域名替换成 IP+端口 之后,访问 Google 才会抛出 ConnectTimeoutException,以及 connectTimeout 参数才会起作用。
C:\Users\LENOVO>nslookup -d www.google.com
------------
Got answer:
HEADER:
opcode = QUERY, id = 1, rcode = NOERROR
header flags: response, auth. answer, want recursion, recursion avail.
questions = 1, answers = 1, authority records = 0, additional = 0
QUESTIONS:
1.2.168.192.in-addr.arpa, type = PTR, class = IN
ANSWERS:
-> 1.2.168.192.in-addr.arpa
name = R7000-2F35
ttl = 0 (0 secs)
------------
服务器: R7000-2F35
Address: 192.168.2.1
DNS request timed out.
timeout was 2 seconds.
timeout (2 secs)
DNS request timed out.
timeout was 2 seconds.
timeout (2 secs)
DNS request timed out.
timeout was 2 seconds.
timeout (2 secs)
DNS request timed out.
timeout was 2 seconds.
timeout (2 secs)
*** 请求 R7000-2F35 超时