一、RequestConfig
Builder(org.apache.http.client.config.RequestConfig.Builder)
是RequestConfig(org.apache.http.client.config.RequestConfig)的内部类,专门用于创建RequestConfig。我经常这样创建RequestConfig:
RequestConfig requestConfig = RequestConfig.custom()
.setConnectionRequestTimeout(3000)
.setConnectTimeout(3000)
.setSocketTimeout(3000).build();
- 1
- 2
- 3
- 4
其中,RequestConfig.custom()方法返回一个Buider对象:
public static RequestConfig.Builder custom() {
return new Builder();
}
- 1
- 2
- 3
如果熟悉Builder设计模式,就可以看出,通过Builder构建RequestConfig的过程是通过遵循Builder设计模式的类创建对象的标准过程:
1.得到待创建对象的Builder对象
2.通过Builder对象一步一步构建待创建对象
3.通过build方法真正创建对象并返回
其中,第二步,“一步一步”构建对象(构建对象的步骤没有顺序要求)就是Builder设计模式的精髓。
(这不禁让我想起了TensorFlow的行为模式:先创建图,最后才执行;还有Spark构建有向无环图,只有遇到action才执行。似乎和Builder设计模式思想一致)
接下来看看Builder的源码:
public static class Builder {
private boolean expectContinueEnabled;
private HttpHost proxy;
private InetAddress localAddress;
private boolean staleConnectionCheckEnabled;
private String cookieSpec;
private boolean redirectsEnabled;
private boolean relativeRedirectsAllowed;
private boolean circularRedirectsAllowed;
private int maxRedirects;
private boolean authenticationEnabled;
private Collection<String> targetPreferredAuthSchemes;
private Collection<String> proxyPreferredAuthSchemes;
private int connectionRequestTimeout;
private int connectTimeout;
private int socketTimeout;
Builder() {
super();
this.staleConnectionCheckEnabled = true;
this.redirectsEnabled = true;
this.maxRedirects = 50;
this.relativeRedirectsAllowed = true;
this.authenticationEnabled = true;
this.connectionRequestTimeout = -1;
this.connectTimeout = -1;
this.socketTimeout = -1;
}
public Builder setExpectContinueEnabled(final boolean expectContinueEnabled) {
this.expectContinueEnabled = expectContinueEnabled;
return this;
}
public Builder setProxy(final HttpHost proxy) {
this.proxy = proxy;
return this;
}
public Builder setLocalAddress(final InetAddress localAddress) {
this.localAddress = localAddress;
return this;
}
public Builder setStaleConnectionCheckEnabled(final boolean staleConnectionCheckEnabled) {
this.staleConnectionCheckEnabled = staleConnectionCheckEnabled;
return this;
}
public Builder setCookieSpec(final String cookieSpec) {
this.cookieSpec = cookieSpec;
return this;
}
public Builder setRedirectsEnabled(final boolean redirectsEnabled) {
this.redirectsEnabled = redirectsEnabled;
return this;
}
public Builder setRelativeRedirectsAllowed(final boolean relativeRedirectsAllowed) {
this.relativeRedirectsAllowed = relativeRedirectsAllowed;
return this;
}
public Builder setCircularRedirectsAllowed(final boolean circularRedirectsAllowed) {
this.circularRedirectsAllowed = circularRedirectsAllowed;
return this;
}
public Builder setMaxRedirects(final int maxRedirects) {
this.maxRedirects = maxRedirects;
return this;
}
public Builder setAuthenticationEnabled(final boolean authenticationEnabled) {
this.authenticationEnabled = authenticationEnabled;
return this;
}
public Builder setTargetPreferredAuthSchemes(final Collection<String> targetPreferredAuthSchemes) {
this.targetPreferredAuthSchemes = targetPreferredAuthSchemes;
return this;
}
public Builder setProxyPreferredAuthSchemes(final Collection<String> proxyPreferredAuthSchemes) {
this.proxyPreferredAuthSchemes = proxyPreferredAuthSchemes;
return this;
}
public Builder setConnectionRequestTimeout(final int connectionRequestTimeout) {
this.connectionRequestTimeout = connectionRequestTimeout;
return this;
}
public Builder setConnectTimeout(final int connectTimeout) {
this.connectTimeout = connectTimeout;
return this;
}
public Builder setSocketTimeout(final int socketTimeout) {
this.socketTimeout = socketTimeout;
return this;
}
public RequestConfig build() {
return new RequestConfig(
expectContinueEnabled,
proxy,
localAddress,
staleConnectionCheckEnabled,
cookieSpec,
redirectsEnabled,
relativeRedirectsAllowed,
circularRedirectsAllowed,
maxRedirects,
authenticationEnabled,
targetPreferredAuthSchemes,
proxyPreferredAuthSchemes,
connectionRequestTimeout,
connectTimeout,
socketTimeout);
}
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 38
- 39
- 40
- 41
- 42
- 43
- 44
- 45
- 46
- 47
- 48
- 49
- 50
- 51
- 52
- 53
- 54
- 55
- 56
- 57
- 58
- 59
- 60
- 61
- 62
- 63
- 64
- 65
- 66
- 67
- 68
- 69
- 70
- 71
- 72
- 73
- 74
- 75
- 76
- 77
- 78
- 79
- 80
- 81
- 82
- 83
- 84
- 85
- 86
- 87
- 88
- 89
- 90
- 91
- 92
- 93
- 94
- 95
- 96
- 97
- 98
- 99
- 100
- 101
- 102
- 103
- 104
- 105
- 106
- 107
- 108
- 109
- 110
- 111
- 112
- 113
- 114
- 115
- 116
- 117
- 118
- 119
- 120
- 121
- 122
- 123
- 124
- 125
Builder里包含了RequestConfig的所有属性;它的构造器给所有属性提供了默认值;它给所有属性提供设置值的方法,且每一个设置值的方法都返回Buidler对象本身;在build方法中真正创建RequestConfig对象。
到此,我们明白了RequestConfig.Builder和Builder设计模式的工作方式。
二、httpClient
1.HttpClient内部三个超时时间的区别
HttpClient内部有三个超时时间设置:连接池获取可用连接超时,连接超时,读取数据超时
先看以下HttpClient的初始化代码:
- RequestConfig requestConfig = RequestConfig.custom()
- .setConnectionRequestTimeout(config.connReqTimeout) //从连接池中获取连接的超时时间
- //与服务器连接超时时间:httpclient会创建一个异步线程用以创建socket连接,此处设置该socket的连接超时时间
- .setConnectTimeout(config.connTimeout)
- .setSocketTimeout(config.socketTimeout) //socket读数据超时时间:从服务器获取响应数据的超时时间
- .build();
- httpClient = HttpClientBuilder.create()
- .setMaxConnTotal(config.maxConnTotal) //连接池中最大连接数
- /**
- * 分配给同一个route(路由)最大的并发连接数。
- * route:运行环境机器 到 目标机器的一条线路。
- * 举例来说,我们使用HttpClient的实现来分别请求 www.baidu.com 的资源和 www.bing.com 的资源那么他就会产生两个route。
- */
- .setMaxConnPerRoute(config.maxConnPerRoute)
- .setDefaultRequestConfig(requestConfig)
- .build();
三个超时时间详解:
1.从连接池中获取可用连接超时
2.连接目标超时connectionTimeout
如测试的时候,将url改为一个不存在的url:“http://test.com” ,超时时间3000ms过后,系统报出异常: org.apache.commons.httpclient.ConnectTimeoutException:The host did not accept the connection within timeout of 3000 ms
3.等待响应超时(读取数据超时)socketTimeout
连接上一个url后,获取response的返回等待时间 ,即在与目标url建立连接后,等待放回response的最大时间,在规定时间内没有返回响应的话就抛出SocketTimeout。
测试的时候的连接url为我本地开启的一个url,http://localhost:8080/firstTest.htm?method=test,在我这个测试url里,当访问到这个链接时,线程sleep一段时间,来模拟返回response超时。
1. @RequestMapping(params = "method=test")
2. public String testMethod(ModelMap model) {
3. try {
4. Thread.sleep(10000);
5. } catch (InterruptedException e) {
6. // TODO Auto-generated catch block
7. e.printStackTrace();
8. }
9. System.out.println("call testMethod method.");
10. model.addAttribute("name", "test method");
11. return "test";
12. }
将读取response返回超时时间设的时间比那个sleep时间短之后,运行程序给出异常:java.net.SocketTimeoutException:Read timed out
三、各版本HttpClient 区别
HttpClient 4.5版本设置连接超时时间-CloseableHttpClient设置Timeout(区别于4.3.2)
HttpClient升级到4.5版本后,API有很多变化,HttpClient 4之后,API一直没有太稳定,我感觉4.5版本抽象后,很多API应该快稳定了。
使用HttpClient,一般都需要设置连接超时时间和获取数据超时时间。这两个参数很重要,目的是为了防止访问其他http时,由于超时导致自己的应用受影响。
4.5版本中,这两个参数的设置都抽象到了RequestConfig中,由相应的Builder构建,具体的例子如下:
CloseableHttpClient httpclient = HttpClients.createDefault();
HttpGet httpGet = new HttpGet("http://stackoverflow.com/");
RequestConfig requestConfig = RequestConfig.custom()
.setConnectTimeout(5000).setConnectionRequestTimeout(1000)
.setSocketTimeout(5000).build();
httpGet.setConfig(requestConfig);
CloseableHttpResponse response = httpclient.execute(httpGet);
System.out.println("得到的结果:" + response.getStatusLine());//得到请求结果
HttpEntity entity = response.getEntity();//得到请求回来的数据
setConnectTimeout:设置连接超时时间,单位毫秒。
setConnectionRequestTimeout:设置从connect Manager获取Connection 超时时间,单位毫秒。这个属性是新加的属性,因为目前版本是可以共享连接池的。
setSocketTimeout:请求获取数据的超时时间,单位毫秒。 如果访问一个接口,多少时间内无法返回数据,就直接放弃此次调用。
===========================================
昨天遇到一个问题需要设置CloseableHttpClient的超时时间,查了官方文档如下。
新建一个RequestConfig:
RequestConfig defaultRequestConfig = RequestConfig.custom()
.setSocketTimeout(5000)
.setConnectTimeout(5000)
.setConnectionRequestTimeout(5000)
.setStaleConnectionCheckEnabled(true)
.build();
这个超时可以设置为客户端级别,作为所有请求的默认值:
CloseableHttpClient httpclient = HttpClients.custom()
.setDefaultRequestConfig(defaultRequestConfig)
.build();
Request不会继承客户端级别的请求配置,所以在自定义Request的时候,需要将客户端的默认配置拷贝过去:
HttpGet httpget = new HttpGet("http://www.apache.org/");
RequestConfig requestConfig = RequestConfig.copy(defaultRequestConfig)
.setProxy(new HttpHost("myotherproxy", 8080))
.build();
httpget.setConfig(requestConfig);
4.3版本的超时是这样的:
public static String httpPost(String url, String jsonString) { // 设置HTTP请求参数 String result = null; HttpClient httpClient = new DefaultHttpClient(); HttpPost httpPost = new HttpPost(url); try { httpClient.getParams().setParameter(CoreConnectionPNames.CONNECTION_TIMEOUT, 10000);//设置请求超时时间 10s StringEntity entity = new StringEntity(jsonString); entity.setContentEncoding("UTF-8"); entity.setContentType("application/json"); httpPost.setEntity(entity); HttpEntity resEntity = httpClient.execute(httpPost).getEntity(); result = EntityUtils.toString(resEntity, "UTF-8"); } catch (Exception e) { logger.error("http接口调用异常:url is::" + url, e); return null; } finally { httpClient.getConnectionManager().shutdown(); } return result; }
4.5.2版本是这样的:
public static String testTimeout(String url) {
// 设置HTTP请求参数
String result = null;
CloseableHttpClient client = HttpClients.createDefault();
HttpGet httpGet = new HttpGet(url);
RequestConfig requestConfig = RequestConfig.custom()
.setConnectTimeout(50000).setConnectionRequestTimeout(10000)
.setSocketTimeout(50000).build();
httpGet.setConfig(requestConfig);
try {
CloseableHttpResponse response = client.execute(httpGet);
result = EntityUtils.toString(response.getEntity(), "UTF-8");
} catch (ClientProtocolException e) {
logger.error("http接口调用异常:url is::" + url, e);
return null;
} catch (Exception e) {
logger.error("http接口调用异常:url is::" + url, e);
return null;
} finally {
try {
client.close();
} catch (IOException e) {
logger.error("http接口调用异常:url is::" + url, e);
}
}
return result;
}