HttpClient 基于连接池的使用

时间:2023-02-01 04:28:41

场景:调用外部系统接口的http请求

要求:

     1:可能是http请求,也可能是https请求

     2:需要加入连接池的概念,不能每次发起请求都新建一个连接(每次连接握手三次,效率太低)

 

准备使用httpclient 4.5的版本

HTTPClient的特性

  1. 基于标准、纯净的Java语言。实现了Http1.0和Http1.1

  2. 以可扩展的面向对象的结构实现了Http全部的方法(GET, POST, PUT, DELETE, HEAD, OPTIONS, and TRACE)。

  3. 支持HTTPS协议。

  4. 通过Http代理建立透明的连接。

  5. 利用CONNECT方法通过Http代理建立隧道的https连接。

  6. Basic, Digest, NTLMv1, NTLMv2, NTLM2 Session, SNPNEGO/Kerberos认证方案。

  7. 插件式的自定义认证方案。

  8. 便携可靠的套接字工厂使它更容易的使用第三方解决方案。

  9. 连接管理器支持多线程应用。支持设置最大连接数,同时支持设置每个主机的最大连接数,发现并关闭过期的连接。

  10. 自动处理Set-Cookie中的Cookie。

  11. 插件式的自定义Cookie策略。

  12. Request的输出流可以避免流中内容直接缓冲到socket服务器。

  13. Response的输入流可以有效的从socket服务器直接读取相应内容。

  14. 在http1.0和http1.1中利用KeepAlive保持持久连接。

  15. 直接获取服务器发送的response code和 headers。

  16. 设置连接超时的能力。

  17. 实验性的支持http1.1 response caching。

  18. 源代码基于Apache License 可免费获取。

 

     在HttpClient 4.x版本中引入了大量的构造器设计模式,很多的配置都不建议直接new出来,而且相关的API也有所改动,例如连接参数,以前是直接new出HttpConnectionParams对象后通过set方法逐一设置属性,现在有了构造器,可以通过如下方式进行构造:

  1. ConnectionConfig.custom().setCharset(Charsets.toCharset(defaultEncoding)).build();  
  2. SocketConfig.custom().setSoTimeout(100000).build();

    实现访问自签名https的要点就是建立一个自定义的SSLContext对象,该对象要有可以存储信任密钥的容器,还要有判断当前连接是否受信任的策略,以及在SSL连接工厂中取消对所有主机名的验证。他的代码将会在本文最后贴出来,以下代码均针对新HttpClient

   首先建立一个信任任何密钥的策略。代码很简单,不去考虑证书链和授权类型,均认为是受信任的

  1. class AnyTrustStrategy implements TrustStrategy{  

    @Override
    public boolean isTrusted(X509Certificate[] chain, String authType) throws CertificateException {
    return true;
    }
    }

     

      HttpClient既能处理常规http协议,又能支持https,根源在于在连接管理器中注册了不同的连接创建工厂。当访问url的schema为http时,调用明文连接套节工厂来建立连接;当访问url的schema为https时,调用SSL连接套接字工厂来建立连接。对于http的连接我们不做修改,只针对使用SSL的https连接来进行自定义:

 

RegistryBuilder<ConnectionSocketFactory> registryBuilder = RegistryBuilder.<ConnectionSocketFactory>create();  
ConnectionSocketFactory plainSF
= new PlainConnectionSocketFactory(); registryBuilder.register("http", plainSF);
//指定信任密钥存储对象和连接套接字工厂
try {
KeyStore trustStore
= KeyStore.getInstance(KeyStore.getDefaultType());
SSLContext sslContext
= SSLContexts.custom().useTLS().loadTrustMaterial(trustStore, new AnyTrustStrategy()).build();
LayeredConnectionSocketFactory sslSF
= new SSLConnectionSocketFactory(sslContext, SSLConnectionSocketFactory.ALLOW_ALL_HOSTNAME_VERIFIER);
registryBuilder.register(
"https", sslSF);
}
catch (KeyStoreException e) {
throw new RuntimeException(e);
}
catch (KeyManagementException e) {
throw new RuntimeException(e);
}
catch (NoSuchAlgorithmException e) {
throw new RuntimeException(e);
}
Registry
<ConnectionSocketFactory> registry = registryBuilder.build();

 

      在上述代码中可以看到,首先建立了一个密钥存储容器,随后让SSLContext开启TLS,并将密钥存储容器和信任任何主机的策略加载到该上下文中。构造SSL连接工厂时,将自定义的上下文和允许任何主机名通过校验的指令一并传入。最后将这样一个自定义的SSL连接工厂注册到https协议上。

 

      前期准备已经完成,接下来我们要获得HttpClient对象:

//设置连接管理器  
PoolingHttpClientConnectionManager connManager = new PoolingHttpClientConnectionManager(registry);
connManager.setDefaultConnectionConfig(connConfig);
connManager.setDefaultSocketConfig(socketConfig);
//构建客户端
HttpClient client= HttpClientBuilder.create().setConnectionManager(connManager).build();

       为了让我们的HttpClient具有多线程处理的能力,连接管理器选用了PoolingHttpClientConnectionManager,将协议注册信息传入连接管理器,最后再次利用构造器的模式创建出我们需要的HttpClient。随后的GET/POST请求发起方法http和https之间没有差异。