HttpClient4.5.2 HttpClientBuilder配置使用连接池

    技术2022-07-10  125

    先看示例代码,代码有点长,不过大家理清思路来看应该不是问题:

    import java.io.IOException; import java.security.cert.X509Certificate; import java.util.ArrayList; import java.util.List; import javax.net.ssl.SSLContext; import javax.net.ssl.TrustManager; import javax.net.ssl.X509TrustManager; import org.apache.http.HttpEntity; import org.apache.http.client.config.RequestConfig; import org.apache.http.client.methods.CloseableHttpResponse; import org.apache.http.client.methods.HttpGet; import org.apache.http.config.Registry; import org.apache.http.config.RegistryBuilder; import org.apache.http.conn.socket.ConnectionSocketFactory; import org.apache.http.conn.socket.PlainConnectionSocketFactory; import org.apache.http.conn.ssl.NoopHostnameVerifier; import org.apache.http.conn.ssl.SSLConnectionSocketFactory; import org.apache.http.impl.client.CloseableHttpClient; import org.apache.http.impl.client.HttpClientBuilder; import org.apache.http.impl.conn.PoolingHttpClientConnectionManager; import org.apache.http.message.BasicHeader; import org.apache.http.pool.PoolStats; import org.apache.http.util.EntityUtils; /*  * httpclient版本: 4.5.2  * httpclient的使用始于HttpClientBuilder  */ public class TestHttpClientPool { public static void main(String[] args) throws Exception{ HttpClientBuilder builder=HttpClientBuilder.create(); /*一、为HttpClientBuilder设置绕过不安全的https证书  */  Registry<ConnectionSocketFactory> registry         = RegistryBuilder.<ConnectionSocketFactory>create()            .register("http", PlainConnectionSocketFactory.INSTANCE)            .register("https", trustHttpsCertificates())            .build(); /*二、为HttpClientBuilder设置PoolingHttpClientConnectionManager  */ PoolingHttpClientConnectionManager cm=new PoolingHttpClientConnectionManager(registry); cm.setMaxTotal(200);//维护的httpclientConnection总数 //每一个route的最大连接数,route可以理解为一个主机,如http://www.roadjava.com/2.html //和http://www.roadjava.com/1.html是一个主机 cm.setDefaultMaxPerRoute(20); builder.setConnectionManager(cm); /*三、为HttpClientBuilder设置从连接池获取连接的超时时间、连接超时时间、获取数据响应超时时间  */ RequestConfig requestConfig=RequestConfig.custom(). setConnectionRequestTimeout(5000). setConnectTimeout(5000).                 setSocketTimeout(5000).build(); builder.setDefaultRequestConfig(requestConfig); /*  * 四、设置默认的header  */ List<BasicHeader> basicHeaders=new ArrayList<>(); BasicHeader basicHeader=new BasicHeader("User-Agent", "Mozilla/5.0 (Windows NT 6.2; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/45.0.2454.101 Safari/537.36"); basicHeaders.add(basicHeader); builder.setDefaultHeaders(basicHeaders);      PoolStats totalStats = cm.getTotalStats();          System.out.println("最大:"+totalStats.getMax());          System.out.println("占用的:"+totalStats.getLeased());          System.out.println("可用的:"+totalStats.getAvailable());          for(int i=0;i<30;i++){           //HttpClients.createDefault();内部调用的是HttpClientBuilder.create().build();           //配置是采用的默认配置           CloseableHttpClient client = builder.build();             String url="http://www.roadjava.com/";              HttpGet httpGet=new HttpGet(url);              //处理响应部分              CloseableHttpResponse response =null;              try {                  response = client.execute(httpGet);                  HttpEntity entity = response.getEntity(); //                 System.out.println("获取到的内容:"+EntityUtils.toString(entity,"UTF-8"));                                    PoolStats totalStats2 = cm.getTotalStats();                  System.out.println("最大:"+totalStats2.getMax());                  System.out.println("占用的:"+totalStats2.getLeased());                  System.out.println("可用的:"+totalStats2.getAvailable());                  EntityUtils.consume(entity);//关闭entity              } catch (Exception e) {                  e.printStackTrace();              } finally{               //不要调用这句,如果调用了就把连接从连接池真正销毁了,连接池使用失去了意义 //                 if (client!=null) { //                     try {client.close();} catch (IOException e) {e.printStackTrace();} //                 }                  if (response!=null) {                      try {response.close();} catch (IOException e) {e.printStackTrace();}                  }              }          }          PoolStats totalStats3 = cm.getTotalStats();          System.out.println("最大:"+totalStats3.getMax());          System.out.println("占用的:"+totalStats3.getLeased());          System.out.println("可用的:"+totalStats3.getAvailable()); }  //创建并返回SSLConnectionSocketFactory对象     public static ConnectionSocketFactory trustHttpsCertificates() throws Exception {           SSLConnectionSocketFactory socketFactory = null;           TrustManager[] trustAllCerts = new TrustManager[1];           TrustManager tm = new myTM();           trustAllCerts[0] = tm;           SSLContext sc = null;           try {               sc = SSLContext.getInstance("TLS");               sc.init(null, trustAllCerts, null);               socketFactory = new SSLConnectionSocketFactory(sc, NoopHostnameVerifier.INSTANCE);           } catch (Exception e) {               e.printStackTrace();           }           return socketFactory;       }      static class myTM implements TrustManager, X509TrustManager {           public X509Certificate[] getAcceptedIssuers() {               return null;           }           public void checkServerTrusted(X509Certificate[] certs, String authType) {                           }           public void checkClientTrusted(X509Certificate[] certs, String authType) {                            }       }   }

    运行结果:

    从httpclient使用连接池可以看到PoolingHttpClientConnectionManager之后,我们可以看到,针对同一个route,httpclient不再是每次请求都新建一个连接(httpconnection)了,而是从连接池里面找可以复用的连接,这样就省去了建立连接以及关闭连接时的耗时。

    因为我设置的每个route可用的并发数是20,如果我把

    EntityUtils.consume(entity);//关闭entity

    以及

    if (response!=null) {  try {response.close();} catch (IOException e) {e.printStackTrace();} }

    注释掉,也就意味着,连接使用之后,并没有放到连接池的AvailableSet里面,表示这个连接一直处于占用状态,那么我们预期在请求到20次的时候它就会从连接池里面无法获取到可用连接,又因为我设置了从连接池里面获取可用连接的等待超时时间是5秒(setConnectionRequestTimeout(5000)),在等待5秒之后就应该报错,是不是这样呢?如下图,正如我们预期,报了org.apache.http.conn.ConnectionPoolTimeoutException: Timeout waiting for connection from pool这个错误:

    这就是httpclient里面连接池PoolingHttpClientConnectionManager的使用方法。

    Processed: 0.010, SQL: 9