首页 > 代码库 > JAVA: httpclient 详解——第二章;

JAVA: httpclient 详解——第二章;



相对于httpurlconnection ,httpclient更加丰富,也更加强大,其中apache有两个项目都是httpclient,一个是commonts包下的,这个是通用的,更专业的是org.apache.http.包下的,所以我一般用后者;


httpclient可以处理长连接,保存会话,重连接,以及请求过滤器,连接重用等等...


下面是测试代码(全部总结来自官方文档,以及翻译)


须要下载核心包:httpclient-4.3.4.jar ,也可在官网下载:http://hc.apache.org/downloads.cgi


/**
	 * 多线程并发访问服务器
	 * 
	 * 每次http连接需要三次握手,开销很大,http/1.1默认支持连接复用;
	 * 
	 * PoolingHttpClientConnectionManager 允许管理器限制最大连接数 ,还允许每个路由器针对某个主机限制最大连接数。
	 * 
	 * 如下:setDefaultMaxPerRoute(3)之后,每次并发3个访问,所以打印输出是每次输出三个"test",验证了http连接管理器生效;
	 * 
	 */
	private static void test9() throws InterruptedException,ExecutionException, IOException {
		
		PoolingHttpClientConnectionManager connManager = new PoolingHttpClientConnectionManager();
		connManager.setMaxTotal(200);//设置最大连接数200
		connManager.setDefaultMaxPerRoute(3);//设置每个路由默认连接数
		HttpHost host = new HttpHost("webservice.webxml.com.cn");//针对的主机
		connManager.setMaxPerRoute(new HttpRoute(host), 5);//每个路由器对每个服务器允许最大5个并发访问
		
		CloseableHttpClient httpClient = HttpClients.custom().setConnectionManager(connManager).build();
		
		String[] urisToGet = { 
				"http://webservice.webxml.com.cn/WebServices/MobileCodeWS.asmx/getDatabaseInfo",
				"http://webservice.webxml.com.cn/WebServices/MobileCodeWS.asmx/getDatabaseInfo", 
				"http://webservice.webxml.com.cn/WebServices/MobileCodeWS.asmx/getDatabaseInfo",
				"http://webservice.webxml.com.cn/WebServices/MobileCodeWS.asmx/getDatabaseInfo", 
				"http://webservice.webxml.com.cn/WebServices/MobileCodeWS.asmx/getDatabaseInfo",
				"http://webservice.webxml.com.cn/WebServices/MobileCodeWS.asmx/getDatabaseInfo", 
				"http://webservice.webxml.com.cn/WebServices/MobileCodeWS.asmx/getDatabaseInfo",
				"http://webservice.webxml.com.cn/WebServices/MobileCodeWS.asmx/getDatabaseInfo", 
				"http://webservice.webxml.com.cn/WebServices/MobileCodeWS.asmx/getDatabaseInfo",
				"http://webservice.webxml.com.cn/WebServices/MobileCodeWS.asmx/getDatabaseInfo" };
		
		GetThread[] threads = new GetThread[urisToGet.length];
		
		for (int i = 0; i < threads.length; i++) {
			HttpGet httpget = new HttpGet(urisToGet[i]);
			threads[i] = new GetThread(httpClient, httpget);
		}
		
		for (int j = 0; j < threads.length; j++) {
			threads[j].start();
		}
		
		for (int j = 0; j < threads.length; j++) {
			threads[j].join();
		}
				
				
	}
	
	static 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();
					if(entity!=null){
						System.out.println("test");
					}
				} finally {
					response.close();
				}
			} catch (ClientProtocolException ex) {
			} catch (IOException ex) {
			}
		}
	}
	
	
	/**
	 *  清空失效连接:
	 * 
	 * 	连接的有效性检测是所有连接池都面临的一个通用问题,大部分HTTP服务器为了控制资源开销,并不会
		永久的维护一个长连接,而是一段时间就会关闭该连接。放回连接池的连接,如果在服务器端已经关闭,客
		户端是无法检测到这个状态变化而及时的关闭Socket的。这就造成了线程从连接池中获取的连接不一定是有效的。
		这个问题的一个解决方法就是在每次请求之前检查该连接是否已经存在了过长时间,可能已过期。
		但是这个方法会使得每次请求都增加额外的开销。HTTP Client4.0的ThreadSafeClientConnManager 提供了
		closeExpiredConnections()方法和closeIdleConnections()方法来解决该问题。
		前一个方法是清除连接池中所有过期的连接,至于连接什么时候过期可以设置,设置方法将在下面提到,
		而后一个方法则是关闭一定时间空闲的连接,可以使用一个单独的线程完成这个工作。
	 */
	private static void test10(){
		
		HttpClientConnectionManager manager = new BasicHttpClientConnectionManager();
		
		new IdleConnectionMonitorThread(manager).start();//启动线程,5秒钟清空一次失效连接
		
		CloseableHttpClient client = HttpClients.custom().setConnectionManager(manager).build();
		
		URI uri = null;//构建uri实体
		try {
			uri = new URIBuilder()
			.setScheme("http")
			.setHost("webservice.webxml.com.cn")
			.setPath("/WebServices/MobileCodeWS.asmx/getDatabaseInfo")
			.setParameter("", "")
			.build();
		} catch (URISyntaxException e) {
			e.printStackTrace();
		}
		
		HttpPost post = new HttpPost(uri);
		
		try {
			client.execute(post);
		} catch (Exception e) {
			e.printStackTrace();
		}
	}
	
	//这个线程负责使用连接管理器清空失效连接和过长连接
	private static 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);
						System.out.println("清空失效连接...");
						// 关闭失效连接
						connMgr.closeExpiredConnections();
						//关闭空闲超过30秒的连接
						connMgr.closeIdleConnections(30, TimeUnit.SECONDS);
					}
				}
			} catch (InterruptedException ex) {
			}
		}
		
		public void shutdown() {
			shutdown = true;
			synchronized (this) {
				notifyAll();
			}
		}
	}
	
	
	/**
	 * 
	 * http长连接策略:
	 * 可以根据须要定制所须要的长连接策略,可根据服务器指定的超时时间,也可根据主机名自己指定超时时间;
	 */
	private static void test11(){
		
		//参考第一章的DefaultConnectionKeepAliveStrategy类
		ConnectionKeepAliveStrategy myStrategy = new ConnectionKeepAliveStrategy() {
			public long getKeepAliveDuration(HttpResponse response,HttpContext context) {
				// 遍历response的header
				HeaderElementIterator it = new BasicHeaderElementIterator(response.headerIterator(HTTP.CONN_KEEP_ALIVE));
				
				while (it.hasNext()) {
					HeaderElement he = it.nextElement();
					String param = he.getName();
					String value = http://www.mamicode.com/he.getValue();>