`
想起要学好java
  • 浏览: 8150 次
  • 性别: Icon_minigender_1
  • 来自: 深圳
社区版块
存档分类
最新评论

HttpClient连接池

    博客分类:
  • http
 
阅读更多

连接管理:

两个主机之间建立的过程是很复杂的,包括了两个终端之间许多数据包的交换,会消耗掉大量的时间。对于很小的HTTP报文传输,TCP/IP的握手环节也是必不可少的。如果已有的链接能够重复使用,来执行多个请求,将会加大程序的数据吞吐量。HttpClient完全地支持连接持久化。

 

连接池管理器 Pool Connection Manager:

PoolingHttpClientConnectionManager 是一个管理客户端连接更复杂的实现。它为执行多线程的连接请求提供服务。对于每个基本的路由,连接都是池管理的。对于路由的请求,连接器在池中有可用的持久性连接,

将被从池中取出连接服务,而不是创建一个新的连接。对每个基本路由,PoolingHttpClientConnectionManager保持着一个最大限制的连接数。使用HttpClient.close() 释放连接。 

当使用PoolingClientConnectionManager 时可以使用多线程来同时执行多个请求。如果池中没有可用的连接,请求将会被阻塞,直到有可用的连接。如果在一定的时间内不能被响应,将会抛出ConnectionPoolTimeoutException异常。

 

代码示例:

import java.io.IOException;

import java.io.InputStream;

import java.util.ArrayList;

import java.util.List;

import java.util.Map;

 

import javax.net.ssl.HostnameVerifier;

import javax.net.ssl.SSLContext;

 

import org.apache.commons.io.IOUtils;

import org.apache.http.HttpResponse;

import org.apache.http.NameValuePair;

import org.apache.http.client.ClientProtocolException;

import org.apache.http.client.CookieStore;

import org.apache.http.client.config.RequestConfig;

import org.apache.http.client.entity.UrlEncodedFormEntity;

import org.apache.http.client.methods.HttpPost;

import org.apache.http.client.protocol.HttpClientContext;

import org.apache.http.config.Registry;

import org.apache.http.config.RegistryBuilder;

import org.apache.http.conn.HttpClientConnectionManager;

import org.apache.http.conn.socket.ConnectionSocketFactory;

import org.apache.http.conn.socket.PlainConnectionSocketFactory;

import org.apache.http.conn.ssl.DefaultHostnameVerifier;

import org.apache.http.conn.ssl.SSLConnectionSocketFactory;

import org.apache.http.impl.client.BasicCookieStore;

import org.apache.http.impl.client.CloseableHttpClient;

import org.apache.http.impl.client.DefaultHttpRequestRetryHandler;

import org.apache.http.impl.client.HttpClients;

import org.apache.http.impl.conn.PoolingHttpClientConnectionManager;

import org.apache.http.message.BasicNameValuePair;

import org.apache.http.ssl.SSLContexts;

import org.slf4j.Logger;

import org.slf4j.LoggerFactory;

 

public class HttpClientUtils {

private static Logger logger = LoggerFactory.getLogger(HttpClientUtils.class);

 

private final HttpClientContext context = new HttpClientContext(); // 初始化上下文实例

private final HttpClientConnectionManager manager = builderPoolConnectionManager(); // 定义连接池管理变量

 

private final String CHAR_SET = "UTF-8";

 

public HttpClientUtils() {

CookieStore cookieStore = new BasicCookieStore();

context.setCookieStore(cookieStore);

}

 

public HttpClientConnectionManager builderPoolConnectionManager() {

final SSLContext context = SSLContexts.createSystemDefault();

final HostnameVerifier verifier = new DefaultHostnameVerifier();

// 自定义注册器,既可以发送http请求,也可以发送https请求

final Registry<ConnectionSocketFactory> register = RegistryBuilder.<ConnectionSocketFactory>create()

.register("http", PlainConnectionSocketFactory.INSTANCE)

.register("https", new SSLConnectionSocketFactory(context, verifier)).build();

 

PoolingHttpClientConnectionManager poolingHttpClientConnectionManager = new PoolingHttpClientConnectionManager(

register);

poolingHttpClientConnectionManager.setMaxTotal(200); // 设置连接池的最大连接数

poolingHttpClientConnectionManager.setDefaultMaxPerRoute(poolingHttpClientConnectionManager.getMaxTotal()); // 一个路由的最大连接数

return poolingHttpClientConnectionManager;

}

 

public CloseableHttpClient buildHttpClient() {

RequestConfig config = RequestConfig.custom().setConnectionRequestTimeout(3000) // 从池中获取请求的时间

.setConnectTimeout(2000) // 连接到服务器的时间

.setSocketTimeout(5000).build(); // 读取信息时间

 

// 如果使用了代理,请打开注释

// HttpHost proxy = new HttpHost("127.0.0.1" , 2924) ;

// HttpRoutePlanner httpRoutePlanner = new HttpRoutePlanner() {

// @Override

// public HttpRoute determineRoute(HttpHost httpHost, HttpRequest httpRequest,

// HttpContext httpContext) throws HttpException {

// return new HttpRoute(httpHost ,proxy);

// }

// };

CloseableHttpClient build = HttpClients.custom().setRetryHandler(DefaultHttpRequestRetryHandler.INSTANCE)

.setDefaultRequestConfig(config).setConnectionManagerShared(true).setConnectionManager(manager)

// .setRoutePlanner(httpRoutePlanner) 设置路由

.build();

 

return build;

}

 

public String post(String url, Map<String, String> params) {

HttpPost post = new HttpPost(url);

String resp = null;

// 这里每次拿到的是池内的一个空闲连接,而不是新建 的连接

CloseableHttpClient httpClient = new HttpClientUtils().buildHttpClient();

try {

try {

if (params != null) {

List<NameValuePair> nvps = new ArrayList<NameValuePair>();

for (Map.Entry<String, String> param : params.entrySet()) {

nvps.add(new BasicNameValuePair(param.getKey(), param.getValue()));

}

post.setEntity(new UrlEncodedFormEntity(nvps, CHAR_SET));

}

 

HttpResponse response = httpClient.execute(post);

InputStream input = response.getEntity().getContent();

resp = IOUtils.toString(input);

input.close();// 释放连接,以便下个请求复用

} catch (ClientProtocolException e) {

logger.error(e.getMessage(), e);

} catch (IOException e) {

logger.error(e.getMessage(), e);

} catch (Exception e) {

logger.error(e.getMessage(), e);

}

} finally {

if (post != null) {

post.releaseConnection();

}

}

 

return resp;

}

}

 

连接池使用注意事项:

1. 连接池中连接都是在发起请求的时候建立,并且都是长连接

2. HttpResponse input.close();作用就是将用完的连接释放,下次请求可以复用,这里特别注意的是,如果不使用in.close();而仅仅使用httpClient.close();结果就是连接会被关闭,并且不能被复用,这样就失去了采用连接池的意义。

3. 连接池释放连接的时候,并不会直接对TCP连接的状态有任何改变,只是维护了两个Set,leased和avaliabled,leased代表被占用的连接集合,avaliabled代表可用的连接的集合,释放连接的时候仅仅是将连接从leased中remove掉了,并把连接放到avaliabled集合中。

 

对RequestConfig里timeout的解释:

RequestConfig config = RequestConfig.custom()

                                    .setConnectionRequestTimeout(3000)  //从池中获取请求的时间

                                    .setConnectTimeout(2000)   //连接到服务器的时间

                                    .setSocketTimeout(5000).build(); //读取信息时间

setConnectTimeout:设置连接超时时间,单位毫秒。ConnectTimeoutException

setConnectionRequestTimeout:设置从connect Manager获取Connection 超时时间,单位毫秒。这个属性是新加的属性,因为目前版本是可以共享连接池的。ConnectionPoolTimeout

setSocketTimeout:请求获取数据的超时时间,单位毫秒。 如果访问一个接口,多少时间内无法返回数据,就直接放弃此次调用。SocketTimeoutException

 

更详细的说明可以参考:

https://blog.csdn.net/ll641058431/article/details/79566277

分享到:
评论

相关推荐

Global site tag (gtag.js) - Google Analytics