轻松把玩HttpClient之封装HttpClient工具类(八),优化启用Http连接池策略
https://blog.****.net/xiaoxian8023/article/details/53064210
- /**
- * HCB对象,用于创建HttpClient对象
- */
- private HCB hcb;
- public HCB hcb() {
- return hcb;
- }
- /**
- * HCB对象,用于自动从连接池中获得HttpClient对象<br>
- * <font color="red"><b>请调用hcb.pool方法设置连接池</b></font>
- * @throws HttpProcessException
- */
- public HttpConfig hcb(HCB hcb) throws HttpProcessException {
- this.hcb = hcb;
- return this;
- }
- /**
- * 判定是否开启连接池、及url是http还是https <br>
- * 如果已开启连接池,则自动调用build方法,从连接池中获取client对象<br>
- * 否则,直接返回相应的默认client对象<br>
- *
- * @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);
- }
- }
- }
- }
最后分享一个测试类,分组测试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.****.net/newest.html").build();
- // URL列表数组,GET请求
- private static final String[] urls = {
- "http://blog.****.net/xiaoxian8023/article/details/49883113",
- "http://blog.****.net/xiaoxian8023/article/details/49909359",
- "http://blog.****.net/xiaoxian8023/article/details/49910127",
- "http://blog.****.net/xiaoxian8023/article/details/49910885",
- "http://blog.****.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; i< count;i++){
- executors.execute(new GetRunnable(countDownLatch,cfg.headers(headers).url(urls[i%pagecount])));
- }
- countDownLatch.await();
- executors.shutdown();
- } catch (InterruptedException e) {
- e.printStackTrace();
- }
- }
- //多线程下载
- public static void testMultiDown(HttpConfig cfg, int count) throws HttpProcessException{
- try {
- int pagecount = imgurls.length;
- ExecutorService executors = Executors.newFixedThreadPool(pagecount);
- CountDownLatch countDownLatch = new CountDownLatch(count);
- //启动线程抓取
- for(int i = 0; i< count;i++){
- executors.execute(new GetRunnable(countDownLatch, cfg.url(imgurls[i%2]), new FileOutputStream(new File("d://aaa//"+(i+1)+".png"))));
- }
- countDownLatch.await();
- executors.shutdown();
- } catch (FileNotFoundException e) {
- e.printStackTrace();
- } catch (InterruptedException e) {
- e.printStackTrace();
- }
- }
- static class GetRunnable implements Runnable {
- private CountDownLatch countDownLatch;
- private HttpConfig config = null;
- private FileOutputStream out = null;
- public GetRunnable(CountDownLatch countDownLatch,HttpConfig config){
- this(countDownLatch, config, null);
- }
- public GetRunnable(CountDownLatch countDownLatch,HttpConfig config,FileOutputStream out){
- this.countDownLatch = countDownLatch;
- this.config = config;
- this.out = out;
- }
- @Override
- public void run() {
- try {
- config.out(out);
- if(config.out()==null){
- String response = null;
- response = HttpClientUtil.get(config);
- System.out.println(Thread.currentThread().getName()+"--获取内容长度:"+response.length());
- response = null;
- }else{
- HttpClientUtil.down(config);
- try {
- config.out().flush();
- config.out().close();
- } catch (IOException e) {
- e.printStackTrace();
- }
- System.out.println(Thread.currentThread().getName()+"--下载完毕");
- }
- } catch (HttpProcessException e) {
- e.printStackTrace();
- } finally {
- countDownLatch.countDown();
- }
- }
- }
- /**
- * 测试不启用http连接池,get100次,down20次的执行时间
- * @throws HttpProcessException
- */
- private static void testNoPool(int getCount, int downCount) throws HttpProcessException {
- long start = System.currentTimeMillis();
- if(getCount>0){
- 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<int[][]> 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 < time.length; i++) {
- for (int j = 0; j < n; j++) {
- testNoPool(time[i][0],time[i][1]);
- Thread.sleep(100);
- System.gc();
- Thread.sleep(100);
- }
- buf1.append("\n");
- }
- }
- t=0;
- //测试启用http连接池
- for (int[][] time : list) {
- buf2.append("\n");
- for (int i = 0; i < time.length; i++) {
- for (int j = 0; j < n; j++) {
- testByPool(time[i][0],time[i][1]);
- Thread.sleep(100);
- System.gc();
- Thread.sleep(100);
- }
- buf2.append("\n");
- }
- t++;
- }
- //把结果打印到Console中
- String[] results1 = buf1.toString().split("\n");
- String[] results2 = buf2.toString().split("\n");
- for (int i = 0; i < results1.length; i++) {
- System.out.println(results1[i]);
- System.out.println(results2[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 。