热门标签 | HotTags
当前位置:  开发笔记 > 编程语言 > 正文

轻松把玩HttpClient之封装HttpClient工具类(八),优化启用Http连接池策略

写了HttpClient工具类后,有人一直在问我怎么启用http连接池,其实我没考虑过这个问题。不过闲暇的时候,突然间想起了这个问题,就想把这个问题搞一搞。
       写了HttpClient工具类后,有人一直在问我怎么启用http连接池,其实我没考虑过这个问题难过。不过闲暇的时候,突然间想起了这个问题,就想把这个问题搞一搞。

       之前没用过,但是理解起来应该不算难。作为一个Coder,就算没用过http连接池,但是肯定用过数据库连接池。二者的功能是类似的,就是把建立链接和断开链接的时间节省下来。众所周知,http建立链接是需要3次握手,了解的深入一些的,还知道断开链接需要4次握手。这些操作都是自动完成的,如果我们把这些建立和断开链接的时间节省掉,对于大批量的http请求(如爬虫)是很有用的。

       关于HttpClient如何启用连接池,可以参考这篇文章:http://www.cnblogs.com/likaitai/p/5431246.html。介绍了如何通过连接池获取链接,以及在不用连接时,如果处理不会导致链接直接关闭。

       说了这么多,下面切入正题:HttpClient工具类如何启用http连接池?其实只需要修改创建链接方法即可:

  之前在工具类的核心方法execute方法里获取httpclient对象,调用的是create(String url)方法。返回的是默认的一个HttpClient对象。现在要启用连接池,必须修改此方法。配置连接池的类是HCB,而execute方法接受的参数是HttpConfig参数,所以,首先要在HttpConfig里添加一个HCB对象。然后修改create方法。具体如下:
	/**
	 * HCB对象,用于创建HttpClient对象
	 */
	private HCB hcb;

	public HCB hcb() {
		return hcb;
	}

	/**
	 * HCB对象,用于自动从连接池中获得HttpClient对象
* 请调用hcb.pool方法设置连接池 * @throws HttpProcessException */ public HttpConfig hcb(HCB hcb) throws HttpProcessException { this.hcb = hcb; return this; }
	/**
	 * 判定是否开启连接池、及url是http还是https 
* 如果已开启连接池,则自动调用build方法,从连接池中获取client对象
* 否则,直接返回相应的默认client对象
* * @throws HttpProcessException */ private static void create(HttpConfig config) throws HttpProcessException { if(config.hcb()!=null && config.hcb().isSetPool){ //如果设置了hcb对象,且配置了连接池,则直接从连接池取 if(config.url().toLowerCase().startsWith("https://")){ config.client(config.hcb().ssl().build()); }else{ config.client(config.hcb().build()); } }else{ if(config.client()==null){//如果为空,设为默认client对象 if(config.url().toLowerCase().startsWith("https://")){ config.client(client4HTTPS); }else{ config.client(client4HTTP); } } } }
       至于关闭方面,fmt2String以及fmt2Stream方法中,在EntityUtils.toString和EntityUtils.consume方法中已经close了instream,释放了资源。最后调用close(HttpClient)即执行client.close()方法。这样就不会直接关闭链接了,会被连接池自动回收再次使用。

       最后分享一个测试类,分组测试Get请求、Down操作,在开启和关闭Http线程池完成请求的耗时情况。代码如下:
import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.util.Arrays;
import java.util.List;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

import org.apache.http.Header;

import com.tgb.ccl.http.common.HttpConfig;
import com.tgb.ccl.http.common.HttpHeader;
import com.tgb.ccl.http.exception.HttpProcessException;
import com.tgb.ccl.http.httpclient.HttpClientUtil;
import com.tgb.ccl.http.httpclient.builder.HCB;

/**
 * 测试启用http连接池
 * 
 * @author arron
 * @date 2016年11月7日 下午1:08:07 
 * @version 1.0
 */
public class TestHttpPool {
	
	// 设置header信息
	private static final Header[] headers = HttpHeader.custom().userAgent("Mozilla/5.0").from("http://blog.csdn.net/newest.html").build();
	
	// URL列表数组,GET请求
	private static final String[] urls = {
			"http://blog.csdn.net/xiaoxian8023/article/details/49883113",
			"http://blog.csdn.net/xiaoxian8023/article/details/49909359",
			"http://blog.csdn.net/xiaoxian8023/article/details/49910127",
			"http://blog.csdn.net/xiaoxian8023/article/details/49910885",
			"http://blog.csdn.net/xiaoxian8023/article/details/51606865",
	};
	
	// 图片URL列表数组,Down操作
	private static final String[] imgurls ={
			"http://ss.bdimg.com/static/superman/img/logo/logo_white_fe6da1ec.png",
			"https://scontent-hkg3-1.xx.fbcdn.net/hphotos-xaf1/t39.2365-6/11057093_824152007634067_1766252919_n.png"
	};
	
	private static StringBuffer buf1=new StringBuffer();
	private static StringBuffer buf2=new StringBuffer();
	
	//多线程get请求
	public static void testMultiGet(HttpConfig cfg, int count) throws HttpProcessException{
	        try {
				int pagecount = urls.length;
				ExecutorService executors = Executors.newFixedThreadPool(pagecount);
				CountDownLatch countDownLatch = new CountDownLatch(count);   
				//启动线程抓取
				for(int i = 0; i0){
			HttpConfig cfg1 = HttpConfig.custom().client(HCB.custom().build()).headers(headers);
			testMultiGet(cfg1, getCount);
		}
		if(downCount>0){
			HttpConfig cfg2 = HttpConfig.custom().client(HCB.custom().build());
			testMultiDown(cfg2, downCount);
		}
		
		System.out.println("-----所有线程已完成!------");
        long end = System.currentTimeMillis();
        System.out.println("总耗时(毫秒): -> " + (end - start));
        buf1.append("\t").append((end-start));
	}

	
	/**
	 * 测试启用http连接池,get100次,down20次的执行时间
	 * @throws HttpProcessException
	 */
	private static void testByPool(int getCount, int downCount) throws HttpProcessException {
		long start = System.currentTimeMillis();
		
		HCB hcb= HCB.custom().timeout(10000).pool(10, 10).ssl();
		
		if(getCount>0){
			HttpConfig cfg3 = HttpConfig.custom().hcb(hcb);
			testMultiGet(cfg3, getCount);
		}
		if(downCount>0){
			HttpConfig cfg4 = HttpConfig.custom().hcb(hcb);
			testMultiDown(cfg4, downCount);
		}

		System.out.println("-----所有线程已完成!------");
        long end = System.currentTimeMillis();
        System.out.println("总耗时(毫秒): -> " + (end - start));
        buf2.append("\t").append((end-start));
	}
	
	public static void main(String[] args) throws Exception {
		File file = new File("d://aaa");
		if(!file.exists() && file.isDirectory()){
			file.mkdir();
		}
		
		//-------------------------------------------
		//  以下2个方法
		//  分别测试 (get次数,down次数) 
		//  {100,0},{200,0},{500,0},{1000,0}
		//  {0,10},{0,20},{0,50},{0,100}
		//  {100,10},{200,20},{500,50},{1000,100}
		//-------------------------------------------
		
		int[][] times1 = {{100,0} ,{ 200,0 },{ 500,0 },{ 1000,0}};
		int[][] times2 = {{0,10},{0,20},{0,50},{0,100}};
		int[][] times3 = {{100,10},{200,20},{500,50},{1000,100}};
		List list = Arrays.asList(times1,times2,times3);
		int n=5;
		
		int t=0;
		//测试未启用http连接池,
		for (int[][] time : list) {
			buf1.append("\n");
			for (int i = 0; i  
         测试结果如下: 
 
操作 请求次数 是否启用Pool 第1次 第2次 第3次 第4次 第5次 平均时间 启用后的效率
GET 100 4801 4807 4853 4810 4522 4758.6 52.89%
2146 1989 2302 2355 2416 2241.6
200 9222 9519 9085 9196 8908 9186 43.15%
4992 4711 4863 7001 4545 5222.4
500 23727 23082 23762 23427 23117 23423 45.88%
12146 12557 12581 13121 12979 12676.8
1000 47518 72445 45028 52860 55764 54723 48.62%
25073 25067 39550 26014 24888 28118.4
Down 10 10605 8273 9440 7774 8740 8966.4 4.37%
10415 7249 7331 8554 9325 8574.8
20 17306 18455 18811 19294 15430 17859.2 2.67%
17234 16028 18152 17530 17971 17383
50 46873 41528 51085 49900 40666 46010.4 -2.93%
44941 50376 46759 43774 50951 47360.2
100 89909 93065 98297 88440 92010 92344.2 -0.93%
91420 96388 94635 88424 95152 93203.8
GET,Down 100,10 15913 13465 14167 15607 11566 14143.6 27.42%
11805 10800 8322 10735 9668 10266
200,20 26579 28744 27791 29712 32360 29037.2 25.76%
20891 24664 19319 19511 23394 21555.8
500,50 71462 72694 74285 76207 72574 73444.4 13.46%
57137 75860 63288 62309 59192 63557.2
1,000,100 147264 149527 143251 143865 139723 144726 16.14%
118305 124517 122511 116823 124673 121365.8
测试使用时间(不含暂停和GC时间)、平均效率 79.1789833 23.04%




       通过测试结果可以看出来,Get请求效果明显,启用后性能要提升50%左右。而Down操作,则反而有所下降。这是为什么呢?其实很简单。连接池是节省了握手的次数,但是握手所消耗的时间,跟一个Down操作相比,肯定要小很多。所以Down操作消耗的时间已经超过了节省握手的时间了,也就起不到优化的作用了,所以要根据实际情况使用连接池。

       最新的完整代码请到GitHub上进行下载:https://github.com/Arronlong/httpclientUtil 。


       httpclientUtil (QQ交流群:548452686 httpclientUtil交流


推荐阅读
  • 本文详细介绍了Akka中的BackoffSupervisor机制,探讨其在处理持久化失败和Actor重启时的应用。通过具体示例,展示了如何配置和使用BackoffSupervisor以实现更细粒度的异常处理。 ... [详细]
  • PHP 5.2.5 安装与配置指南
    本文详细介绍了 PHP 5.2.5 的安装和配置步骤,帮助开发者解决常见的环境配置问题,特别是上传图片时遇到的错误。通过本教程,您可以顺利搭建并优化 PHP 运行环境。 ... [详细]
  • 从 .NET 转 Java 的自学之路:IO 流基础篇
    本文详细介绍了 Java 中的 IO 流,包括字节流和字符流的基本概念及其操作方式。探讨了如何处理不同类型的文件数据,并结合编码机制确保字符数据的正确读写。同时,文中还涵盖了装饰设计模式的应用,以及多种常见的 IO 操作实例。 ... [详细]
  • 本文介绍了如何通过 Maven 依赖引入 SQLiteJDBC 和 HikariCP 包,从而在 Java 应用中高效地连接和操作 SQLite 数据库。文章提供了详细的代码示例,并解释了每个步骤的实现细节。 ... [详细]
  • andr ... [详细]
  • MySQL 数据库迁移指南:从本地到远程及磁盘间迁移
    本文详细介绍了如何在不同场景下进行 MySQL 数据库的迁移,包括从一个硬盘迁移到另一个硬盘、从一台计算机迁移到另一台计算机,以及解决迁移过程中可能遇到的问题。 ... [详细]
  • Scala 实现 UTF-8 编码属性文件读取与克隆
    本文介绍如何使用 Scala 以 UTF-8 编码方式读取属性文件,并实现属性文件的克隆功能。通过这种方式,可以确保配置文件在多线程环境下的一致性和高效性。 ... [详细]
  • Explore a common issue encountered when implementing an OAuth 1.0a API, specifically the inability to encode null objects and how to resolve it. ... [详细]
  • 本文介绍了如何使用 Spring Boot DevTools 实现应用程序在开发过程中自动重启。这一特性显著提高了开发效率,特别是在集成开发环境(IDE)中工作时,能够提供快速的反馈循环。默认情况下,DevTools 会监控类路径上的文件变化,并根据需要触发应用重启。 ... [详细]
  • 主要用了2个类来实现的,话不多说,直接看运行结果,然后在奉上源代码1.Index.javaimportjava.awt.Color;im ... [详细]
  • 本文详细介绍了 Dockerfile 的编写方法及其在网络配置中的应用,涵盖基础指令、镜像构建与发布流程,并深入探讨了 Docker 的默认网络、容器互联及自定义网络的实现。 ... [详细]
  • 本文介绍了一款用于自动化部署 Linux 服务的 Bash 脚本。该脚本不仅涵盖了基本的文件复制和目录创建,还处理了系统服务的配置和启动,确保在多种 Linux 发行版上都能顺利运行。 ... [详细]
  • 深入解析Spring Cloud Ribbon负载均衡机制
    本文详细介绍了Spring Cloud中的Ribbon组件如何实现服务调用的负载均衡。通过分析其工作原理、源码结构及配置方式,帮助读者理解Ribbon在分布式系统中的重要作用。 ... [详细]
  • 在前两篇文章中,我们探讨了 ControllerDescriptor 和 ActionDescriptor 这两个描述对象,分别对应控制器和操作方法。本文将基于 MVC3 源码进一步分析 ParameterDescriptor,即用于描述 Action 方法参数的对象,并详细介绍其工作原理。 ... [详细]
  • 毕业设计:基于机器学习与深度学习的垃圾邮件(短信)分类算法实现
    本文详细介绍了如何使用机器学习和深度学习技术对垃圾邮件和短信进行分类。内容涵盖从数据集介绍、预处理、特征提取到模型训练与评估的完整流程,并提供了具体的代码示例和实验结果。 ... [详细]
author-avatar
weidnermennenga_153
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有