httpclient 4.5.1---连接管理

package com.cycore.dbs.common.demo.hc;

import java.io.IOException;
import java.net.InetAddress;
import java.net.InetSocketAddress;
import java.net.ProxySelector;
import java.net.Socket;
import java.security.KeyStore;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.TimeUnit;

import javax.net.ssl.SSLContext;

import org.apache.http.HeaderElement;
import org.apache.http.HeaderElementIterator;
import org.apache.http.HttpClientConnection;
import org.apache.http.HttpException;
import org.apache.http.HttpHost;
import org.apache.http.HttpRequest;
import org.apache.http.HttpResponse;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.client.protocol.HttpClientContext;
import org.apache.http.config.Registry;
import org.apache.http.config.RegistryBuilder;
import org.apache.http.conn.ConnectionKeepAliveStrategy;
import org.apache.http.conn.ConnectionRequest;
import org.apache.http.conn.HttpClientConnectionManager;
import org.apache.http.conn.routing.HttpRoute;
import org.apache.http.conn.routing.HttpRoutePlanner;
import org.apache.http.conn.socket.ConnectionSocketFactory;
import org.apache.http.conn.socket.LayeredConnectionSocketFactory;
import org.apache.http.conn.socket.PlainConnectionSocketFactory;
import org.apache.http.conn.ssl.SSLConnectionSocketFactory;
import org.apache.http.conn.ssl.SSLContexts;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClients;
import org.apache.http.impl.conn.BasicHttpClientConnectionManager;
import org.apache.http.impl.conn.DefaultProxyRoutePlanner;
import org.apache.http.impl.conn.PoolingHttpClientConnectionManager;
import org.apache.http.impl.conn.SystemDefaultRoutePlanner;
import org.apache.http.message.BasicHeaderElementIterator;
import org.apache.http.protocol.HTTP;
import org.apache.http.protocol.HttpContext;

public class HC_ConnectionManagement {

	/**
	 * 管理HTTP连接和连接管理器
	 * @throws InterruptedException
	 * @throws ExecutionException
	 * @throws IOException
	 */
	public void m1() throws InterruptedException, ExecutionException, IOException{
		HttpClientContext context=HttpClientContext.create();

		//BasicHttpClientConnectionManager是单个线程安全的连接管理器,一次只有一个连接。
		HttpClientConnectionManager connMrg=new BasicHttpClientConnectionManager();
		HttpRoute route=new HttpRoute(new HttpHost("localhost", 80));//请求路由

		//请求新连接,这可能是个很长的过程
		ConnectionRequest connRequest=connMrg.requestConnection(route, null);
		//等待连接10秒
		HttpClientConnection conn=connRequest.get(10, TimeUnit.SECONDS);

		//必要时可使用 ConnectionRequest#cancel()提前终止连接请求,并会解开 ConnectionRequest#get()的阻塞线程

		try {
			//如果连接没有打开
			if(!conn.isOpen()){
				//基于它的路由信息在1000毫秒内创建连接
				connMrg.connect(conn, route, 1000, context);
				//当路由完成即所有中间跳完成,连接完全建立时时标记
				connMrg.routeComplete(conn, route, context);
			}
			//用连接做一些事。。。
		} finally {
			//释放连接到还回到管理器,连接存活1分钟,便于被其它消费者使用
			connMrg.releaseConnection(conn, null, 1, TimeUnit.MINUTES);
		}
	}

	/**
	 * PoolingHttpClientConnectionManager-管理客户端连接池,可以服务于来自多个执行进程的连接请求。
	 * 这些连接是基于每一个路由被池化的。管理器会租借一个持久可用的连接给一个路由的一个请求而不是新建一个连接。
	 * 连接管理的实现默认可为每一个路由创建不多于2个且总数不超过20个的并发连接。
	 * 然而在真实应用环境中这些限制对性能提高有限,特别是HTTP协议传输服务的时候。
	 * 不过下面的例子展示了如何调整连接池的参数:
	 */
	public void m2(){
		PoolingHttpClientConnectionManager cm=new PoolingHttpClientConnectionManager();

		//为单路由设置默认最大并发连接数为20
		cm.setDefaultMaxPerRoute(20);

		//为地址localhost:80的主机设置最大并发连接数为50
		HttpHost localhost=new HttpHost("localhost", 80);
		cm.setMaxPerRoute(new HttpRoute(localhost), 50);

		CloseableHttpClient client=HttpClients.custom()
				.setConnectionManager(cm)
				.build();

		/*连接管理器关闭:保证管理器中的连接得以关闭,且被这些连接分配的系统资源得以释放*/
		//ClosableHttpClient httpClient = <...>
		//httpClient.close();
	}

	/**
	 * 执行多线程请求---在执行多线程请求时httpClient是线程安全的且可共享。
	 * 这里推荐每个线程最好拥有专用的HttpContext实例。
	 * @throws InterruptedException
	 */
	public void m3() throws InterruptedException{
		PoolingHttpClientConnectionManager cm=new PoolingHttpClientConnectionManager();
		CloseableHttpClient client=HttpClients.custom().setConnectionManager(cm).build();

		//URIs to perform GETs on
		String[] urisToGet={
			"http://www.domain1.com",
			"http://www.domain2.com",
			"http://www.domain3.com",
			"http://www.domain4.com"
		};

		//create a thread for each URI
		GetThread[] threads=new GetThread[urisToGet.length];
		for (int i = 0; i < threads.length; i++) {
			HttpGet httpGet=new HttpGet(urisToGet[i]);
			threads[i]=new GetThread(client, httpGet);
		}

		//start the threads
		for (int i = 0; i < threads.length; i++) {
			threads[i].start();
		}

		//join the threads
		for (int i = 0; i < threads.length; i++) {
			threads[i].join();
		}
	}

	/**
	 * 连接保持存活策略与keep-alive头
	 */
	public void m4(){
		ConnectionKeepAliveStrategy myStrategy=new ConnectionKeepAliveStrategy() {

			public long getKeepAliveDuration(HttpResponse response, HttpContext context) {
				//Honor 'keep-alive' header
				HeaderElementIterator iterator=new BasicHeaderElementIterator(
						response.headerIterator(HTTP.CONN_KEEP_ALIVE));
				while (iterator.hasNext()) {
					HeaderElement he = (HeaderElement) iterator.next();
					String param=he.getName();
					String value=he.getValue();
					if(value != null && param.equalsIgnoreCase("timeout")){
						try {
							return Long.parseLong(value)*1000;
						} catch (NumberFormatException e) {
						}
					}
				}
				HttpHost target = (HttpHost) context.getAttribute(HttpClientContext.HTTP_TARGET_HOST);
				if("www.naughty-server.com".equalsIgnoreCase(target.getHostName())){
					//keep alive for 5 seconds only
					return 5*1000;
				}else{
					//otherwise keep alive for 30 seconds
					return 30*1000;
				}
			}
		};
		CloseableHttpClient client=HttpClients.custom()
				.setKeepAliveStrategy(myStrategy)
				.build();
	}

	/**
	 * 连接套接字工厂-依靠 ConnectionSocketFactory接口去创建、初始化、连接套接字。
	 * PlainConnectionSocketFactory是创建和初始化原生(非加密的)套接字的默认工厂类。
	 *
	 * 安全套接字层连接工厂类LayeredConnectionSocketFactory是ConnectionSocketFactory接口的扩展,
	 * 可以再纯socket上创建套接字层。
	 *
	 * HttpClient也可使用实现了SSL/TLS层的SSLSocketFactory。注意HttpClient不适用任何自定义的加密功能。
	 * 它完全依赖于标准的Java Cryptography(JCE) 和 安全套接字扩展(JSEE)。
	 * @throws IOException
	 */
	public void m5() throws IOException{
		HttpClientContext clientContext=HttpClientContext.create();
		PlainConnectionSocketFactory sf=PlainConnectionSocketFactory.getSocketFactory();
		//创建并初始化套接字
		Socket socket=sf.createSocket(clientContext);

		int timeout=1000;//ms
		HttpHost target=new HttpHost("localhost");
		InetSocketAddress remoteAddress=new InetSocketAddress(
				InetAddress.getByAddress(new byte[]{127,0,0,1}), 80);
		//连接套接字
		sf.connectSocket(timeout, socket, target, remoteAddress, null, clientContext);
	}

	/**
	 * 连接套接字工厂整合连接管理器:
	 * 自定义连接套接字工厂可以和一个特殊的协议如HTTP或HTTPS相关联,然后用来创建一个自定义的连接管理器
	 */
	public void m6(){
		ConnectionSocketFactory plainsf=PlainConnectionSocketFactory.getSocketFactory();
		LayeredConnectionSocketFactory sslsf=SSLConnectionSocketFactory.getSocketFactory();
		Registry<ConnectionSocketFactory> r = RegistryBuilder.<ConnectionSocketFactory>create()
				.register("http", plainsf)
				.register("https", sslsf)
				.build();

		HttpClientConnectionManager cm=new PoolingHttpClientConnectionManager(r);
		CloseableHttpClient client=HttpClients.custom()
				.setConnectionManager(cm)
				.build();
	}

	/**
	 * SSL/TLS定制化:
	 * HttpClient使用SSLConnectionSocketFactory去创建SSL连接。SSLConnectionSocketFactory允许高度定制化。
	 * 它利用javax.net.ssl.SSLContext的一个实例作为参数,并用它创建定制配置的SSL连接。
	 * 该部分需要对SSL/TLS协议有一定程度的了解。
	 */
	public void m7(){
//		KeyStore myTrustStore = <...>
//		SSLContext sslContext = SSLContexts.custom().loadTrustMaterial(myTrustStore).build();
//		SSLConnectionSocketFactory sslsf=new SSLConnectionSocketFactory(sslContext);
	}

	/**
	 * 主机名验证:除了在SSL/TSL协议层扮演信任验证和客户端认证角色之外,HttpClient能选择性的去验证
	 * 目标主机名称是否和储存在服务器中X.509证书上的名称一致。一旦连接已经建立,验证过程能提供服务器
	 * 信任材料的额外可靠性保证。
	 * javax.net.ssl.HostnameVerifier接口体现了一种主机名验证策略。
	 * javax.net.ssl.HostnameVerifier有两种实现HttpClient可以用来工作。
	 * 注意:主机名验证和SSL信任验证不可混淆。
	 *
	 * (1)DefaultHostnameVerifier:默认实现,遵从RFC2818。主机名必须符合指定证书上任意备选名称。
	 * (2)NoopHostnameVerifier:该主机名验证器本质上会关闭主机名验证。它接受任何有效的和符合目标主机的SSL会话。
	 */
	public void m8(){
		/*每一个HttpClient会使用默认的DefaultHostnameVerifier实现,如果你想也可以指定实现*/
		SSLContext sslContext=SSLContexts.createSystemDefault();
//		SSLConnectionSocketFactory sslsf = new SSLConnectionSocketFactory(sslContext, NoopHostnameVerifier.Instance);

//		HttpClient 4.4版本使用公共后缀列表来保证SSL证书中的通配符不会被误用,当应用在有一个共同顶级域名下的子域名的时候。
//		【参考:https://publicsuffix.org/list/effective_tld_names.dat】。强烈建议制作一个列表的本地副本,从源位置
//		location即https://publicsuffix.org/list/effective_tld_names.dat下载这个列表的次数每天不超过一次。
//
//		PublicSuffixMatcher publicSuffixMatcher = PublicSuffixMatcherLoader.load(
//				PublicSuffixMatcher.class.getResource("my-copy-effective_tld_names.dat"));
//		DefaultHostnameVerifier hostnameVerifier = new DefaultHostnameVerifier(publicSuffixMatcher);

//		禁用公共后缀验证
//		DefaultHostnameVerifier hostnameVerifier = new DefaultHostnameVerifier(null);
	}

	/**
	 * HttpClient代理配置:通过代理连接目标主机。
	 * 即使HttpClient意识到复杂的路由方案和代理链,它只支持简单直接的或者说箱外一跳代理连接。
	 * 例子中有三种配置方式
	 */
	public void m9(){
//		1.默认的代理参数
//		HttpHost proxy = new HttpHost("someproxy", 8080);
//		DefaultProxyRoutePlanner routePlanner=new DefaultProxyRoutePlanner(proxy);

//		或 2.用标准的JRE代理选择器获取代理信息
//		SystemDefaultRoutePlanner routePlanner=new SystemDefaultRoutePlanner(
//				ProxySelector.getDefault());

//		或 3.定制实现RoutePlanner,以完全控制HTTP路由计算的过程
		HttpRoutePlanner routePlanner = new HttpRoutePlanner() {

			public HttpRoute determineRoute(
					HttpHost target,
					HttpRequest request,
					HttpContext context) throws HttpException {

				return new HttpRoute(
						target,//目标主机
						null,//本地地址
						new HttpHost("someproxy", 8080),//代理主机
						"https".equalsIgnoreCase(target.getSchemeName()));//是否安全
			}
		};

		CloseableHttpClient client=HttpClients.custom()
				.setRoutePlanner(routePlanner)
				.build();
	}

}
</pre><pre code_snippet_id="1625884" snippet_file_name="blog_20160327_9_8684436" name="code" class="java">
</pre><pre code_snippet_id="1625884" snippet_file_name="blog_20160327_5_7004806" name="code" class="java"><pre name="code" class="java">package com.cycore.dbs.common.demo.hc;

import java.util.concurrent.TimeUnit;

import org.apache.http.conn.HttpClientConnectionManager;

/**
 * httpclient连接回收策略:
 * 经典阻塞I/O模型的一个主要缺陷是网络套接字仅在IO操作过程中被阻塞时能对I/O事件反应。
 * 当一个连接被释放回管理器,它可以保持活动然而不能监听套接字状态并对I/O事件作出反应。
 * 如果服务端的连接被关闭,客户端连接不能侦查到连接状态的改变(且不能在客户端通过关闭套接字作出合理反应)。
 *
 * HttpClient通过测试连接是否'陈腐'来尝试缓和这个问题,连接不再有效是因为在执行一个HTTP请求之前被服务端关闭了。
 * 不为空闲连接的每个套接字模型引入一个线程的唯一可行的解决方案是一个用来回收连接(这些连接被认为长时间没有活动)的专用的监听线程。
 * 监听线程可以调用 ClientConnectionManager#closeExpiredConnections()来关闭过期的连接和回收关闭的连接,也可以调用
 * ClientConnectionManager#closeIdleConnections()来关闭在一段时间内的所有空闲连接。
 * @author xiawq
 *
 */
public class IdleConnectionMonitorThread extends Thread{

	private final HttpClientConnectionManager connMgr;
	private volatile boolean shutdown;

	public IdleConnectionMonitorThread(HttpClientConnectionManager connMgr) {
		super();
		this.connMgr = connMgr;
	}

	@Override
	public void run() {
		try {
			while(!shutdown){
				synchronized (this) {
					wait(5000);
					//close expired connections
					connMgr.closeExpiredConnections();

					//optionally, close connections that have been idle longer than 30 sec.
					connMgr.closeIdleConnections(30, TimeUnit.SECONDS);
				}
			}
		} catch (InterruptedException e) {
			e.printStackTrace();
		}
	}

	public void shutdown(){
		shutdown=true;
		synchronized (this) {
			notifyAll();//唤醒其它线程
		}
	}
}

</pre><pre code_snippet_id="1625884" snippet_file_name="blog_20160327_9_8684436" name="code" class="java">
<pre name="code" class="java">package com.cycore.dbs.common.demo.hc;

import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.util.Date;

import org.apache.http.HttpEntity;
import org.apache.http.client.ClientProtocolException;
import org.apache.http.client.methods.CloseableHttpResponse;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.client.protocol.HttpClientContext;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.protocol.HttpContext;

public class GetThread extends Thread {

	private final CloseableHttpClient httpClient;
	private final HttpContext context;
	private final HttpGet httpGet;

	public GetThread(CloseableHttpClient httpClient, HttpGet httpGet) {
		this.httpClient = httpClient;
		this.context = HttpClientContext.create();
		this.httpGet = httpGet;
	}

	@Override
	public void run() {
		try {
			CloseableHttpResponse response=httpClient.execute(httpGet, context);
			try {
				HttpEntity entity = response.getEntity();
				entity.writeTo(new FileOutputStream(new File("D:\\"+new Date().getTime()+".txt")));
			} finally {
				response.close();
			}
		} catch (ClientProtocolException e) {
			//handle protocal errors
		} catch (IOException e) {
			//handle I/O errors
		}
	}

}


时间: 2024-11-13 10:49:46

httpclient 4.5.1---连接管理的相关文章

HttpClient 4.3.6教程 第2章 连接管理 【翻译】

第2章 连接管理 2.1 持久连接 一个主机与另一端建立连接是十分复杂的,并且两个终端间要交换多个信息包,这会耗费不少时间.对于低级的HTTP消息来说握手连接是尤其重要的.如果在执行多个请求时重复使用公共的连接,那么就能大大提高数据吞吐率.HTTP/1.1默认允许HTTP连接可以被多个请求复用.HTTP/1.0也兼容终端为了多个请求去使用一个明确的机制来优先保持活跃的连接.HTTP代理也能在一定的同期时间里保持活跃的空闲连接,以免同样的目标主机随后还要请求.这种保持活跃连接的能力通常都会涉及持续

HttpClient4.3教程 第二章 连接管理

2.1.持久连接 两个主机建立连接的过程是很复杂的一个过程,涉及到多个数据包的交换,并且也很耗时间.Http连接需要的三次握手开销很大,这一开销对于比较小的http消息来说更大.但是如果我们直接使用已经建立好的http连接,这样花费就比较小,吞吐率更大. HTTP/1.1默认就支持Http连接复用.兼容HTTP/1.0的终端也可以通过声明来保持连接,实现连接复用.HTTP代理也可以在一定时间内保持连接不释放,方便后续向这个主机发送http请求.这种保持连接不释放的情况实际上是建立的持久连接.Ht

Visual Studio2015下连接管理Global Azure Database

说到vistual studio相信大家已经不陌生了,当然对于开发人员来说.同时又说到windows azure的部分服务的部署,我们可以使用Visual Studio2015连接到windows azure,然后创建部分房屋.现在很多人应该用到了vistual studio2013,但是我是微软的粉丝.微软只要更新出新产品都会迫不及待的取尝试.现在微软vistual studio更新到了2015了,功能就不说了,需要大家自己去官网下载使用.在此我下载了vistual studio2015最新版

boost::asio 连接管理11 如何关闭连接

在实际产品运行中,对连接管理有了更新的认识,这里分享一下. shared_ptr管理连接对象的生命周期 shared_ptr的引用计数器决定了连接对象的生命周期.这里我说的连接对象就是在我的前文:http://blog.csdn.net/csfreebird/article/details/8522620 中的Client对象: [cpp] view plaincopyprint? #include "core/connection.h" #include <vector>

Openfire分析之三:ConnectionManager 连接管理(1)

Openfire是怎么实现连接请求的? XMPPServer.start()方法,完成Openfire的启动.但是,XMPPServer.start()方法中,并没有提及如何监听端口,那么Openfire是如何接收客户端的请求? 因为Openfire的核心功能,是通过Module来管理的,那么对应的连接管理应该就在Module中. 查看在XMPPServer.loadModules()方法中,有如下代码: //Load this module always last since we don't

常见存储的串口连接管理方法

各大存储厂商的存储大多有一个COM接口,作为存储工程师提供一些重要配置的接口,COM接口的功能非常强大,可以直接与设备连接,实现对设备的配置,调试,数据传输等一系列操作.本文我们主要讲解通过COM口对设备IP进行管理与配置.下面以常见IBM的DS4700存储和戴尔MD3200存储为例,讲解一下通过COM接口管理IP的步骤. DS4700串口管理IP配置 1.  串口线(console线)连接,COM线连接存储,其另一端是公头的9针接口,需要一条9针母头的接口转USB接口的COM线,连接到管理电脑

PLSQL_性能优化系列15_Oracle Connection Management连接管理

2014-09-25 BaoXinjian 一.摘要 在官方文档<oracle performance tuning guide>中提到Connecting to the database is an expensive operation that is highly unscalable. 数据库的连接操作是昂贵的,且难以扩展(支持大量并发). 感觉上一个数据库登录操作是瞬间的事,它有多昂贵呢? 简单说,监听器收到远程连接请求后,转给server process: 对于每个session数

通过Apache的httpClient的post方式连接服务器

客户端的代码是: package lgx.java.test; import java.io.ByteArrayOutputStream; import java.io.IOException; import java.io.InputStream; import java.io.UnsupportedEncodingException; import java.util.ArrayList; import java.util.HashMap; import java.util.List; im

用phpmyadimn来连接管理多个数据库

用phpmyadimn来连接管理多个数据库要修改配置文件,挺不爽的,并且连接远程数据库,速度不行.可以使用其他数据库管理工具, 请参考,navicat 结合快捷键 非常好用,开源,好用mysql 管理工具 HeidiSQL.如果非要用phpmyadmin,下面有二种方法连接,管理多个mysql服务器. 方法一,修改phpMyAdmin/libraries/config.default.php 修改配置文件前,最好先备份一下,万一改错地方了,显示不了,就郁闷了. /** * allow login