连接管理
两个主机之间建立的过程是很复杂的,包括了两个终端之间许多数据包的交换,会消耗掉大量的时间。对于很小的HTTP报文传输,TCP/IP的握手环节也是必不可少的。如果已有的链接能够重复使用,来执行多个请求,将会加大程序的数据吞吐量。HttpClient完全地支持连接持久化。
连接路由
HttpClient能够直接建立连接到目标主机,或者通过路由,但这会涉及多个中间连接–也被称为“一跳”。HttpClient 区分连接路由plain,tunneled和laryed。连接到目标主机的隧道使用多个中间代理,被称为代理链。
- 直接连接到目标主机或仅仅有一个代理的是plain路由
- 通过一个代理或者代理链连到目标主机是tunneled路由,没有代理的路由不是tunneled路由
- 通过已有连接加上协议分层是 layered
连接管理器
HTTP连接是复杂的,有状态的,非县城安全的对象。 HTTP连接 一次仅只能有一个执行线程来使用,HttpClient 采用一个特殊实体来管理访问HTTP连接,这被称为HTTP连接管理器,ClientConnectionManager 接口就是代表。HTTP连接管理器的目的是作为工厂创建新的HTTP连接,管理持久连接的生命周期和同步访问持久连接。内部的HTTP连接管理器使用OperatedClientConnection实例,这个实例为真正的连接扮演了一个代理,来管理连接状态和控制I/O操作执行。如果一个管理的连接被释放或者被使用者明确地关闭,潜在的连接就会从它的代理分离,退回到管理中。
- BasicHttpClientConnectionManager 是一个简单的连接管理器,它保持一次只有一个连接,尽管这个方法是线程安全的,它也应该一次使用一个县城。如果连接已经被分配,将会抛出java.lang.IllegalStateException异常,连接管理器应该在EJB容器中实现。
连接池管理器 Pool Connection Manager
PoolingHttpClientConnectionManager 是一个管理客户端连接更复杂的实现。它为执行多线程的连接请求提供服务。对于每个基本的路由,连接都是池管理的。对于路由的请求,连接器在池中有可用的持久性连接,将被从池中取出连接服务,而不是创建一个新的连接。对每个基本路由,PoolingHttpClientConnectionManager保持着一个最大限制的连接数。使用HttpClient.close() 释放连接。 当使用PoolingClientConnectionManager 时可以使用多线程来同时执行多个请求。如果池中没有可用的连接,请求将会被阻塞,直到有可用的连接。如果在一定的时间内不能被响应,将会抛出ConnectionPoolTimeoutException异常。
Socket 工厂
HTTP 连接内部使用java.net.Socket 来传输数据,依靠ConnectionSocketFactory接口来创建、初始化和连接socket。PlainConnectionSocketFactory是创建、初始化普通socket的工厂。扩展ConnectionSocketFactory 的类还有LayeredConnectionSocketFactory、SSLConnectionSocketFactory (安全套接字创建工厂)。
代理配置
尽管HTTPClient 了解复杂的路由模式和路由链,但它指支持简单的重定向或者一跳代理连接。你也可以实现一个自定义的RoutePlanner,来实现一个完整控制HTTP路由计算的处理器。
Cookie
Cookie 是HTTP代理和目标服务器可以交流保持会话的状态信息的令牌或小的数据包。创建方式:
CookieStore cookieStore = new BasicCookieStore() ;
可以将cookie设置在上下文中,这样服务器就可以识辨多次请求来自同一个客户端。BasicCookieStore 使用java.util.TreeSet 的简单实现。
- Lookup 实例代表了真实的cookie详细记录,这个属性的值设置在本地上下文中,优先于默认的
- CookieSpec 实例代表了真实的cookie规范
- CookieOrigins 实例代表了当前的源服务器的详情
- CookieStore 实例代表了当前cookie store ,这个属性的值设置在本地上下文中而优先于默认
通过下面的代码设置cookie :
CookieStore cookie = new BasicCookieStore() ;
HttpClientContext httpClientContext = HttpClientContext.create();
httpClientContext.setCookieStore(cookie);
没有什么使用写一遍代码解决不了的,一遍不行那就写两边,上面说了这么多,肯定蒙蔽了,好了我们直接上代码,我们看一下用上面的内容如何来组装程序,组装成要给一个可以多次请求的demo。
public class HttpClientUtils {
private final HttpClientContext context = new HttpClientContext() ; //初始化上下文实例
private final HttpClientConnectionManager manager = builderPoolConnectionManager() ; //定义连接池管理变量
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 HttpClient 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 static void main(String[] args) throws IOException {
HttpClientUtils utils = new HttpClientUtils() ;
HttpClient httpClient = utils.buildHttpClient();
HttpGet get = new HttpGet("http://www.qq.com");
HttpResponse response = httpClient.execute(get, utils.context);
System.out.println(EntityUtils.toString(response.getEntity() , Consts.UTF_8));
}
}
参考: http://blog.csdn.net/u011179993/article/details/47123727