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

https://blog.csdn.net/xiaoxian8023/article/details/53064210


  1. /** 
  2.  * HCB对象,用于创建HttpClient对象 
  3.  */  
  4. private HCB hcb;  
  5.   
  6. public HCB hcb() {  
  7.     return hcb;  
  8. }  
  9.   
  10. /** 
  11.  * HCB对象,用于自动从连接池中获得HttpClient对象<br> 
  12.  * <font color="red"><b>请调用hcb.pool方法设置连接池</b></font> 
  13.  * @throws HttpProcessException  
  14.  */  
  15. public HttpConfig hcb(HCB hcb) throws HttpProcessException {  
  16.     this.hcb = hcb;  
  17.     return this;  
  18. }  
[java]  view plain  copy
  1. /** 
  2.  * 判定是否开启连接池、及url是http还是https <br> 
  3.  *      如果已开启连接池,则自动调用build方法,从连接池中获取client对象<br> 
  4.  *      否则,直接返回相应的默认client对象<br> 
  5.  *  
  6.  * @throws HttpProcessException  
  7.  */  
  8. private static void create(HttpConfig config) throws HttpProcessException  {  
  9.     if(config.hcb()!=null && config.hcb().isSetPool){ //如果设置了hcb对象,且配置了连接池,则直接从连接池取  
  10.         if(config.url().toLowerCase().startsWith("https://")){  
  11.             config.client(config.hcb().ssl().build());  
  12.         }else{  
  13.             config.client(config.hcb().build());  
  14.         }  
  15.     }else{  
  16.         if(config.client()==null){//如果为空,设为默认client对象  
  17.             if(config.url().toLowerCase().startsWith("https://")){  
  18.                 config.client(client4HTTPS);  
  19.             }else{  
  20.                 config.client(client4HTTP);  
  21.             }  
  22.         }  
  23.     }  
  24. }  
       至于关闭方面,fmt2String以及fmt2Stream方法中,在EntityUtils.toString和EntityUtils.consume方法中已经close了instream,释放了资源。最后调用close(HttpClient)即执行client.close()方法。这样就不会直接关闭链接了,会被连接池自动回收再次使用。

       最后分享一个测试类,分组测试Get请求、Down操作,在开启和关闭Http线程池完成请求的耗时情况。代码如下:
[java]  view plain  copy
  1. import java.io.File;  
  2. import java.io.FileNotFoundException;  
  3. import java.io.FileOutputStream;  
  4. import java.io.IOException;  
  5. import java.util.Arrays;  
  6. import java.util.List;  
  7. import java.util.concurrent.CountDownLatch;  
  8. import java.util.concurrent.ExecutorService;  
  9. import java.util.concurrent.Executors;  
  10.   
  11. import org.apache.http.Header;  
  12.   
  13. import com.tgb.ccl.http.common.HttpConfig;  
  14. import com.tgb.ccl.http.common.HttpHeader;  
  15. import com.tgb.ccl.http.exception.HttpProcessException;  
  16. import com.tgb.ccl.http.httpclient.HttpClientUtil;  
  17. import com.tgb.ccl.http.httpclient.builder.HCB;  
  18.   
  19. /** 
  20.  * 测试启用http连接池 
  21.  *  
  22.  * @author arron 
  23.  * @date 2016年11月7日 下午1:08:07  
  24.  * @version 1.0 
  25.  */  
  26. public class TestHttpPool {  
  27.       
  28.     // 设置header信息  
  29.     private static final Header[] headers = HttpHeader.custom().userAgent("Mozilla/5.0").from("http://blog.csdn.net/newest.html").build();  
  30.       
  31.     // URL列表数组,GET请求  
  32.     private static final String[] urls = {  
  33.             "http://blog.csdn.net/xiaoxian8023/article/details/49883113",  
  34.             "http://blog.csdn.net/xiaoxian8023/article/details/49909359",  
  35.             "http://blog.csdn.net/xiaoxian8023/article/details/49910127",  
  36.             "http://blog.csdn.net/xiaoxian8023/article/details/49910885",  
  37.             "http://blog.csdn.net/xiaoxian8023/article/details/51606865",  
  38.     };  
  39.       
  40.     // 图片URL列表数组,Down操作  
  41.     private static final String[] imgurls ={  
  42.             "http://ss.bdimg.com/static/superman/img/logo/logo_white_fe6da1ec.png",  
  43.             "https://scontent-hkg3-1.xx.fbcdn.net/hphotos-xaf1/t39.2365-6/11057093_824152007634067_1766252919_n.png"  
  44.     };  
  45.       
  46.     private static StringBuffer buf1=new StringBuffer();  
  47.     private static StringBuffer buf2=new StringBuffer();  
  48.       
  49.     //多线程get请求  
  50.     public static void testMultiGet(HttpConfig cfg, int count) throws HttpProcessException{  
  51.             try {  
  52.                 int pagecount = urls.length;  
  53.                 ExecutorService executors = Executors.newFixedThreadPool(pagecount);  
  54.                 CountDownLatch countDownLatch = new CountDownLatch(count);     
  55.                 //启动线程抓取  
  56.                 for(int i = 0; i< count;i++){  
  57.                     executors.execute(new GetRunnable(countDownLatch,cfg.headers(headers).url(urls[i%pagecount])));  
  58.                 }  
  59.                 countDownLatch.await();  
  60.                 executors.shutdown();  
  61.             } catch (InterruptedException e) {  
  62.                 e.printStackTrace();  
  63.             }  
  64.     }  
  65.       
  66.     //多线程下载  
  67.     public static void testMultiDown(HttpConfig cfg, int count) throws HttpProcessException{  
  68.         try {  
  69.             int pagecount = imgurls.length;  
  70.             ExecutorService executors = Executors.newFixedThreadPool(pagecount);  
  71.             CountDownLatch countDownLatch = new CountDownLatch(count);     
  72.             //启动线程抓取  
  73.             for(int i = 0; i< count;i++){  
  74.                 executors.execute(new GetRunnable(countDownLatch, cfg.url(imgurls[i%2]), new FileOutputStream(new File("d://aaa//"+(i+1)+".png"))));  
  75.             }  
  76.             countDownLatch.await();  
  77.             executors.shutdown();  
  78.         } catch (FileNotFoundException e) {  
  79.             e.printStackTrace();  
  80.         } catch (InterruptedException e) {  
  81.             e.printStackTrace();  
  82.         }  
  83.     }  
  84.       
  85.      static class GetRunnable implements Runnable {  
  86.             private CountDownLatch countDownLatch;  
  87.             private HttpConfig config = null;  
  88.             private FileOutputStream out = null;  
  89.   
  90.             public GetRunnable(CountDownLatch countDownLatch,HttpConfig config){  
  91.                this(countDownLatch, config, null);  
  92.             }  
  93.             public GetRunnable(CountDownLatch countDownLatch,HttpConfig config,FileOutputStream out){  
  94.                 this.countDownLatch = countDownLatch;  
  95.                 this.config = config;  
  96.                 this.out = out;  
  97.             }  
  98.               
  99.             @Override  
  100.             public void run() {  
  101.                 try {  
  102.                     config.out(out);  
  103.                     if(config.out()==null){  
  104.                         String response = null;  
  105.                         response =  HttpClientUtil.get(config);  
  106.                         System.out.println(Thread.currentThread().getName()+"--获取内容长度:"+response.length());  
  107.                         response = null;  
  108.   
  109.                     }else{  
  110.                         HttpClientUtil.down(config);  
  111.                         try {  
  112.                             config.out().flush();  
  113.                             config.out().close();  
  114.                         } catch (IOException e) {  
  115.                             e.printStackTrace();  
  116.                         }  
  117.                         System.out.println(Thread.currentThread().getName()+"--下载完毕");  
  118.                     }  
  119.                 } catch (HttpProcessException e) {  
  120.                     e.printStackTrace();  
  121.                 } finally {  
  122.                     countDownLatch.countDown();  
  123.                 }  
  124.             }  
  125.         }    
  126.   
  127.   
  128.     /** 
  129.      * 测试不启用http连接池,get100次,down20次的执行时间 
  130.      * @throws HttpProcessException 
  131.      */  
  132.     private static void testNoPool(int getCount, int downCount) throws HttpProcessException {  
  133.         long start = System.currentTimeMillis();  
  134.   
  135.         if(getCount>0){  
  136.             HttpConfig cfg1 = HttpConfig.custom().client(HCB.custom().build()).headers(headers);  
  137.             testMultiGet(cfg1, getCount);  
  138.         }  
  139.         if(downCount>0){  
  140.             HttpConfig cfg2 = HttpConfig.custom().client(HCB.custom().build());  
  141.             testMultiDown(cfg2, downCount);  
  142.         }  
  143.           
  144.         System.out.println("-----所有线程已完成!------");  
  145.         long end = System.currentTimeMillis();  
  146.         System.out.println("总耗时(毫秒): -> " + (end - start));  
  147.         buf1.append("\t").append((end-start));  
  148.     }  
  149.   
  150.       
  151.     /** 
  152.      * 测试启用http连接池,get100次,down20次的执行时间 
  153.      * @throws HttpProcessException 
  154.      */  
  155.     private static void testByPool(int getCount, int downCount) throws HttpProcessException {  
  156.         long start = System.currentTimeMillis();  
  157.           
  158.         HCB hcb= HCB.custom().timeout(10000).pool(1010).ssl();  
  159.           
  160.         if(getCount>0){  
  161.             HttpConfig cfg3 = HttpConfig.custom().hcb(hcb);  
  162.             testMultiGet(cfg3, getCount);  
  163.         }  
  164.         if(downCount>0){  
  165.             HttpConfig cfg4 = HttpConfig.custom().hcb(hcb);  
  166.             testMultiDown(cfg4, downCount);  
  167.         }  
  168.   
  169.         System.out.println("-----所有线程已完成!------");  
  170.         long end = System.currentTimeMillis();  
  171.         System.out.println("总耗时(毫秒): -> " + (end - start));  
  172.         buf2.append("\t").append((end-start));  
  173.     }  
  174.       
  175.     public static void main(String[] args) throws Exception {  
  176.         File file = new File("d://aaa");  
  177.         if(!file.exists() && file.isDirectory()){  
  178.             file.mkdir();  
  179.         }  
  180.           
  181.         //-------------------------------------------  
  182.         //  以下2个方法  
  183.         //  分别测试 (get次数,down次数)   
  184.         //  {100,0},{200,0},{500,0},{1000,0}  
  185.         //  {0,10},{0,20},{0,50},{0,100}  
  186.         //  {100,10},{200,20},{500,50},{1000,100}  
  187.         //-------------------------------------------  
  188.           
  189.         int[][] times1 = {{100,0} ,{ 200,0 },{ 500,0 },{ 1000,0}};  
  190.         int[][] times2 = {{0,10},{0,20},{0,50},{0,100}};  
  191.         int[][] times3 = {{100,10},{200,20},{500,50},{1000,100}};  
  192.         List<int[][]> list = Arrays.asList(times1,times2,times3);  
  193.         int n=5;  
  194.           
  195.         int t=0;  
  196.         //测试未启用http连接池,  
  197.         for (int[][] time : list) {  
  198.             buf1.append("\n");  
  199.             for (int i = 0; i < time.length; i++) {  
  200.                 for (int j = 0; j < n; j++) {  
  201.                     testNoPool(time[i][0],time[i][1]);  
  202.                     Thread.sleep(100);  
  203.                     System.gc();  
  204.                     Thread.sleep(100);  
  205.                 }  
  206.                 buf1.append("\n");  
  207.             }  
  208.         }  
  209.   
  210.         t=0;  
  211.         //测试启用http连接池  
  212.         for (int[][] time : list) {  
  213.             buf2.append("\n");  
  214.             for (int i = 0; i < time.length; i++) {  
  215.                 for (int j = 0; j < n; j++) {  
  216.                     testByPool(time[i][0],time[i][1]);  
  217.                     Thread.sleep(100);  
  218.                     System.gc();  
  219.                     Thread.sleep(100);  
  220.                 }  
  221.                 buf2.append("\n");  
  222.             }  
  223.             t++;  
  224.         }  
  225.           
  226.         //把结果打印到Console中  
  227.         String[] results1 = buf1.toString().split("\n");  
  228.         String[] results2 = buf2.toString().split("\n");  
  229.           
  230.         for (int i = 0; i < results1.length; i++) {  
  231.             System.out.println(results1[i]);  
  232.             System.out.println(results2[i]);  
  233.         }  
  234.           
  235.     }  
  236. }  
       测试结果如下:
操作 请求次数 是否启用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 。


猜你喜欢

转载自blog.csdn.net/qq_19299071/article/details/80759975