HttpClient4.X 升级 入门 + http连接池使用

简介:

在一次服务器异常的排查过程当中(服务器异常排查的过程我会另起文章),我们决定使用HttpClient4.X替代HttpClient3.X或者HttpConnection。

为什么使用HttpClient4?主要是HttpConnection没有连接池的概念,多少次请求就会建立多少个IO,在访问量巨大的情况下服务器的IO可能会耗尽。

HttpClient3也有连接池的东西在里头,使用MultiThreadedHttpConnectionManager,大致过程如下:

[java]  view plain  copy print?
  1. MultiThreadedHttpConnectionManager connectionManager = new MultiThreadedHttpConnectionManager();  
  2. HttpClient client = new HttpClient(connectionManager);...// 在某个线程中。  
  3. GetMethod get = new GetMethod("http://jakarta.apache.org/");  
  4. try {  
  5. client.executeMethod(get);// print response to stdout  
  6. System.out.println(get.getResponseBodyAsStream());  
  7. finally {  
  8. // be sure the connection is released back to the connection   
  9. managerget.releaseConnection();  
  10. }  

 

可以看出来,它的方式与jdbc连接池的使用方式相近,我觉得比较不爽的就是需要手动调用releaseConnection去释放连接。对每一个HttpClient.executeMethod须有一个method.releaseConnection()与之匹配。

 

HttpClient4在这点上做了改进,使用我们常用的InputStream.close()来确认连接关闭(4.1版本之前使用entity.consumeContent()来确认内容已经被消耗关闭连接)。具体方式如下:

[java]  view plain  copy print?
  1. ...HttpClient client = null;InputStream in = null;  
  2. try{  
  3. client = HttpConnectionManager.getHttpClient();  
  4. HttpGet get = new HttpGet();  
  5. get.setURI(new URI(urlPath));  
  6. HttpResponse response = client.execute(get);  
  7. HttpEntity entity =response.getEntity();  
  8. if( entity != null ){   
  9.  in = entity.getContent();  
  10.  ....  
  11. }catch (Exception e){  
  12. ....  
  13. }finally{  
  14. if (in != null){  
  15. try{in.close ();}catch (IOException e){  
  16. e.printStackTrace ();  
  17. }  
  18. }  
  19. }  

2012-03-06更新:

有网友提出调用in.close()是否会关闭底层socket,事情是这样的:

[plain]  view plain  copy print?
  1. 回复kangkang203:感谢你提出的这个问题。  
  2. 首 先我文中提出的方法in.close()它会触发一个连接的释放这个连接将重新被连接管理器收回,官网的原文是这么说 的:“Closing the input stream will trigger connection release...the underlying connection gets released back to the connection manager”。 但是底层的socket是否会被关闭是不一定的,我看了部分源码(EofSensorInputStream)发现,大多数情况socket并不会关闭, 而是否关闭socket貌似是由一个Watcher去决定的。所以in.close的调用不会引起socket的关闭。  
  3. 另外,由于http本身我们把它当做“短连接”,所以在一次请求交互完成后仍然打开socket的意义不是很大,毕竟它不像长连接那样在一个连接建立之后会有很多次数据交互。我们试用连接管理器的更多意义在于它对连接的管理。  


 

好说完了连接池的使用流程,现在来说一说连接池在使用时最重要的几个参数。我用4.1的版本实现了一个简单的HttpConnectionManager,代码如下:

 

[java]  view plain  copy print?
  1. public class HttpConnectionManager {   
  2.   
  3.     private static HttpParams httpParams;  
  4.     private static ClientConnectionManager connectionManager;  
  5.   
  6.     /** 
  7.      * 最大连接数 
  8.      */  
  9.     public final static int MAX_TOTAL_CONNECTIONS = 800;  
  10.     /** 
  11.      * 获取连接的最大等待时间 
  12.      */  
  13.     public final static int WAIT_TIMEOUT = 60000;  
  14.     /** 
  15.      * 每个路由最大连接数 
  16.      */  
  17.     public final static int MAX_ROUTE_CONNECTIONS = 400;  
  18.     /** 
  19.      * 连接超时时间 
  20.      */  
  21.     public final static int CONNECT_TIMEOUT = 10000;  
  22.     /** 
  23.      * 读取超时时间 
  24.      */  
  25.     public final static int READ_TIMEOUT = 10000;  
  26.   
  27.     static {  
  28.         httpParams = new BasicHttpParams();  
  29.         // 设置最大连接数  
  30.         ConnManagerParams.setMaxTotalConnections(httpParams, MAX_TOTAL_CONNECTIONS);  
  31.         // 设置获取连接的最大等待时间  
  32.         ConnManagerParams.setTimeout(httpParams, WAIT_TIMEOUT);  
  33.         // 设置每个路由最大连接数  
  34.         ConnPerRouteBean connPerRoute = new ConnPerRouteBean(MAX_ROUTE_CONNECTIONS);  
  35.         ConnManagerParams.setMaxConnectionsPerRoute(httpParams,connPerRoute);  
  36.         // 设置连接超时时间  
  37.         HttpConnectionParams.setConnectionTimeout(httpParams, CONNECT_TIMEOUT);  
  38.         // 设置读取超时时间  
  39.         HttpConnectionParams.setSoTimeout(httpParams, READ_TIMEOUT);  
  40.   
  41.         SchemeRegistry registry = new SchemeRegistry();  
  42.         registry.register(new Scheme("http", PlainSocketFactory.getSocketFactory(), 80));  
  43.         registry.register(new Scheme("https", SSLSocketFactory.getSocketFactory(), 443));  
  44.   
  45.         connectionManager = new ThreadSafeClientConnManager(httpParams, registry);  
  46.     }  
  47.   
  48.     public static HttpClient getHttpClient() {  
  49.         return new DefaultHttpClient(connectionManager, httpParams);  
  50.     }  
  51.   
  52. }  

 

最大连接数、获取连接的最大等待时间、读取超时时间 这些配置应该比较容易理解,一般的连接池都会有这些配置,比较特别的是 每个路由(route)最大连接数 。

 

什么是一个route?

 

这里route的概念可以理解为 运行环境机器 到 目标机器的一条线路。举例来说,我们使用HttpClient的实现来分别请求 www.baidu.com 的资源和 www.bing.com 的资源那么他就会产生两个route。

 

这里为什么要特别提到route最大连接数这个参数呢,因为这个参数的默认值为2,如果 不设置这个参数值默认情况下对于同一个目标机器的最大并发连接只有2个!这意味着如果你正在执行一个针对某一台目标机器的抓取任务的时候,哪怕你设置连接 池的最大连接数为200,但是实际上还是只有2个连接在工作,其他剩余的198个连接都在等待,都是为别的目标机器服务的。

 

怎么样蛋疼吧,我是已经有过血的教训了,在切换到HttpClient4.1的起初没有注意到这个配置,最后使得服务承受的压力反而不如从前了,所以在这里特别提醒大家注意。

 

HttpClient4.X 教程下载:

http://svn.apache.org/repos/asf/httpcomponents/httpclient/trunk/httpclient-contrib/docs/translated-tutorial/httpclient-tutorial-simplified-chinese.pdf


关于版本的补充:

网友w2449008821提醒之后我才发现在HttpClient4.1+的版本ConnManagerParams已经被Deprecated了。

我在写这篇日志的时候时候的httpclient 版本是4.0.3,从4.0版本之后ConnManagerParams被Deprecated,没想到一个小版本升级会有这么大变化。

官网教程举例了新的连接池设置:

 

[java]  view plain  copy print?
  1. SchemeRegistry schemeRegistry = new SchemeRegistry();  
  2. schemeRegistry.register(  
  3.          new Scheme("http", 80, PlainSocketFactory.getSocketFactory()));  
  4. schemeRegistry.register(  
  5.          new Scheme("https", 443, SSLSocketFactory.getSocketFactory()));  
  6.   
  7. ThreadSafeClientConnManager cm = new ThreadSafeClientConnManager(schemeRegistry);  
  8. // Increase max total connection to 200  
  9. cm.setMaxTotalConnections(200);  
  10. // Increase default max connection per route to 20  
  11. cm.setDefaultMaxPerRoute(20);  
  12. // Increase max connections for localhost:80 to 50  
  13. HttpHost localhost = new HttpHost("locahost", 80);  
  14. cm.setMaxForRoute(new HttpRoute(localhost), 50);  
  15.    
  16. HttpClient httpClient = new DefaultHttpClient(cm);  

ConnManagerParams的功能被挪到了 ThreadSafeClientConnManager 和 HttpConnectionParams两个类:

 

static ConnPerRoute getMaxConnectionsPerRoute(HttpParams params) 
          Deprecated. use ThreadSafeClientConnManager.getMaxForRoute(org.apache.http.conn.routing.HttpRoute)
static int getMaxTotalConnections(HttpParams params) 
          Deprecated. use ThreadSafeClientConnManager.getMaxTotal()
static long getTimeout(HttpParams params) 
          Deprecated. use HttpConnectionParams.getConnectionTimeout(HttpParams)
static void setMaxConnectionsPerRoute(HttpParams params, ConnPerRoute connPerRoute) 
          Deprecated. use ThreadSafeClientConnManager.setMaxForRoute(org.apache.http.conn.routing.HttpRoute, int)
static void setMaxTotalConnections(HttpParams params, int maxTotalConnections) 
          Deprecated. use ThreadSafeClientConnManager.setMaxTotal(int)
static void setTimeout(HttpParams params, long timeout) 
          Deprecated. use HttpConnectionParams.setConnectionTimeout(HttpParams, int)



参考:http://hc.apache.org/httpcomponents-client-ga/httpclient/apidocs/org/apache/http/conn/params/ConnManagerParams.html

http://hc.apache.org/httpcomponents-client-ga/tutorial/html/connmgmt.html#d4e638

 

 

公司代码:

 

复制代码
public class HttpClientMgr {//HttpConnectionManager连接池
    private static ClientConnectionManager cm;
    private static HttpParams params;

    public static final void init() {
        params = new BasicHttpParams();
        // // 设置最大连接数  ,增加最大连接到200
        ConnManagerParams.setMaxTotalConnections(params, 200);
        // 设置每个路由最大连接数  ,增加每个路由的默认最大连接到20
        ConnPerRouteBean connPerRoute = new ConnPerRouteBean(20);
        ConnManagerParams.setMaxConnectionsPerRoute(params, connPerRoute);
        SchemeRegistry schemeRegistry = new SchemeRegistry();
        schemeRegistry.register(new Scheme("http", PlainSocketFactory
                .getSocketFactory(), 80));
        cm = new ThreadSafeClientConnManager(params, schemeRegistry);
    }

    public static final DefaultHttpClient getHttpClient() {
        DefaultHttpClient httpClient = new DefaultHttpClient(cm, params);
        httpClient.getCookieSpecs().register("kds", new CookieSpecFactory() {

            @Override
            public CookieSpec newInstance(HttpParams params) {
                // TODO Auto-generated method stub
                return new KdsCookieSpec();
            }
        });
        //httpClient.getParams().setParameter(ClientPNames.COOKIE_POLICY,
        //        "kds");
        HttpClientParams.setCookiePolicy(httpClient.getParams(), "kds");
        return httpClient;
    }

    public static final void shutdown() {
        cm.shutdown();
    }
    
    public static final String[] DATE_PATTERNS = new String[] {
        "EEE, dd MMM yyyy HH:mm:ss zzz", "EEE, dd-MMM-yy HH:mm:ss zzz",
        "EEE MMM d HH:mm:ss yyyy", "EEE, dd-MMM-yyyy HH:mm:ss z",
        "EEE, dd-MMM-yyyy HH-mm-ss z", "EEE, dd MMM yy HH:mm:ss z",
        "EEE dd-MMM-yyyy HH:mm:ss z", "EEE dd MMM yyyy HH:mm:ss z",
        "EEE dd-MMM-yyyy HH-mm-ss z", "EEE dd-MMM-yy HH:mm:ss z",
        "EEE dd MMM yy HH:mm:ss z", "EEE,dd-MMM-yy HH:mm:ss z",
        "EEE,dd-MMM-yyyy HH:mm:ss z", "EEE, dd-MM-yyyy HH:mm:ss z",
        "E, dd-MMM-yyyy HH:mm:ss zzz", "EEEE, dd-MMM-yy HH:mm:ss zzz" }; 
    
    static class KdsCookieSpec extends BrowserCompatSpec {
        @SuppressWarnings("deprecation")
        public KdsCookieSpec() {
            super();
            registerAttribHandler(ClientCookie.EXPIRES_ATTR,
                    new BasicExpiresHandler(DATE_PATTERNS) {
                @Override
                public void parse(SetCookie cookie, String value)
                        throws MalformedCookieException {
                    super.parse(cookie, value);
                    
                    //Logger.d("tag", "KdsCookieSpec value:"+value);
                }
            });
        }
    }
}
复制代码

 


本文转自农夫山泉别墅博客园博客,原文链接:http://www.cnblogs.com/yaowen/p/5446512.html,如需转载请自行联系原作者

相关文章
|
24天前
|
Java 网络架构 Kotlin
kotlin+springboot入门级别教程,教你如何用kotlin和springboot搭建http
本文是一个入门级教程,介绍了如何使用Kotlin和Spring Boot搭建HTTP服务,并强调了Kotlin的空安全性特性。
46 7
kotlin+springboot入门级别教程,教你如何用kotlin和springboot搭建http
|
2月前
|
安全 应用服务中间件 网络安全
简单比较 http https http2,我们要如何把http升级为https
【9月更文挑战第13天】本文对比了HTTP、HTTPS和HTTP/2的特点与适用场景。HTTP以明文传输,适合低安全要求的环境;HTTPS通过SSL/TLS加密,适用于电子商务等安全要求高的场景;HTTP/2采用二进制格式和多路复用,适合高性能Web应用。文章还详细介绍了将HTTP升级为HTTPS的步骤,包括申请和安装SSL证书、配置Web服务器、重定向HTTP流量到HTTPS以及测试HTTPS功能。升级到HTTPS可提高数据安全性和用户信任度。
68 13
|
22天前
|
安全 应用服务中间件 网络安全
修复HTTPS升级后出现 Mixed Content: The page at 'https://xxx' was loaded over HTTPS, but requested an insecure frame 'http://xxx'. This request has been blocked; the content must be served over HTTPS. 的问题
修复HTTPS升级后出现 Mixed Content: The page at 'https://xxx' was loaded over HTTPS, but requested an insecure frame 'http://xxx'. This request has been blocked; the content must be served over HTTPS. 的问题
|
5月前
|
Java 应用服务中间件 Apache
Apache HTTP配置反向代理入门
Apache HTTP配置反向代理入门
383 0
Apache HTTP配置反向代理入门
|
5月前
|
安全 搜索推荐
基础入门 HTTP数据包&Postman构造&请求方法&请求头修改&状态码判断
基础入门 HTTP数据包&Postman构造&请求方法&请求头修改&状态码判断
|
6月前
|
域名解析 网络协议 安全
【域名解析DNS专栏】DNS-over-TLS与DNS-over-HTTPS:安全升级新标准
【5月更文挑战第26天】随着网络技术的发展,DNS协议面临安全挑战,DNS-over-TLS (DoT) 和 DNS-over-HTTPS (DoH) 作为解决方案出现,旨在通过加密增强隐私和安全。DoT使用TLS封装DNS查询,防止流量被窥探或篡改;DoH则利用HTTPS隐藏DNS查询。实施DoT需在客户端和服务器间建立TLS连接,DoH需DNS服务器支持HTTPS接口。这两种技术为网络安全提供支持,未来有望更广泛部署,提升网络环境的安全性。
488 0
|
6月前
|
数据采集 监控 前端开发
使用Python打造爬虫程序之入门探秘:掌握HTTP请求,开启你的数据抓取之旅
【4月更文挑战第19天】本文介绍了爬虫技术的基本概念和用途,阐述了HTTP协议的重要性。在Python中,借助requests库可轻松发送HTTP请求,如GET和POST。文章还展示了如何设置请求头、处理cookies和session。通过学习这些基础知识,读者将能够开始网络数据抓取,为进一步的数据分析奠定基础。后续文章将探讨HTML解析、动态内容处理及反爬虫策略。
|
6月前
|
安全 搜索推荐 前端开发
【https】如何让http升级成https
文章详细讲解了如何让HTTP到HTTPS的操作过程
223 0
【https】如何让http升级成https
|
6月前
|
前端开发 JavaScript UED
轻松入门Axios二:前端开发中的HTTP利器
轻松入门Axios二:前端开发中的HTTP利器
90 0
|
Java Apache
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd"> <html><head><meta http-equiv="Cont
hbase从集群中有8台regionserver服务器,已稳定运行了5个多月,8月15号,发现集群中4个datanode进程死了,经查原因是内存 outofMemory了(因为这几台机器上部署了spark,给spark开的...
810 0