httpclient连接池使用及简单分析

httpclient的连接池

为什么要使用httpclient连接池

连接池是为了复用连接而存在的,就像线程池一样,创建了的线程在执行完成任务后不销毁,而是放入池中待命,以便执行下次任务的时候可以直接从池中取出线程执行,而不是先创建线程再执行,省去了创建线程带来的开销和时间。http连接也是一样的思路,http1.1支持了keep-alive,我们再对同一个网址进行请求的时候,就可以不用每次都建立连接;
我们都知道,http建立连接的过程是比较繁琐的,要经历3次握手和4次挥手,那么省去这个建立连接的过程在高并发的时候就会比较的有必要;同时,类似线程池,总有一个最大的线程数,和线程失效时间,因为在任务空闲的时候,这些空闲线程占用系统资源,所以我们要释放空闲时间长的线程。同样对于http连接,使用的是tcp的长连接,但是长连接的持有是非常耗资源的,特别是对于服务端,链接数是有限的,所以我们同样需要释放一定时间空闲的连接;

什么时候使用httpclient连接池

对自己的系统有正确的预估:

  1. 调用的请求是否对同一个host大量请求;因为只有对于同一个host,长连接才可能建立,如果每次请求一会一个http://a.com 一会一个 http://b.com 这样是起不到连接池的效果的,并且http/https也是不能共用的,因为他们的端口不同;
  2. 是否请求会达到一定的量级;如果请求的数量不高,连接池体现不出多大的效果;如果请求达到一定的量级,可能成为系统瓶颈或有较大影响的时候,可以尝试使用;
  3. 对请求的系统有一定的了解;长连接是双向的,客户端维护连接,服务端同样需要维护连接,并且服务端服务的可能不止一个客户端,就怕到时候这边使用的连接池把服务端的连接占满了,导致服务端无法为其他的客户端提供服务,这个锅可能会扣在自己的头上。

如何使用httpclient

httpclient给我们提供了PoolingHttpClientConnectionManager这个类帮助我们来管理连接(版本4.5及以上)。在我们使用httpclient的时候,如果配置了这个连接管理,那么就会通过这个来按host管理连接;
还是一样,最简单的用法先来一个使用单例的:

public final class HttpClientUtils {

    private static CloseableHttpClient client;

    public static CloseableHttpClient getHttpClient() {

        if(client == null) {
            synchronized(HttpClientUtils.class) {
                if(client == null) {
                    // 先设置http连接的一些配置
                    requestConfig = RequestConfig.custom()
                                    // 从连接池获取连接的超时时间
                                    .setConnectionRequestTimeout(3000)
                                    // 建立连接的超时时间
                                    .setConnectTimeout(3000)
                                    // 请求的超时时间
                                    .setSocketTimeout(3000).build();

                    // 有的地方会配置一堆的https ssl的策略,点进这个构造函数他默认已经配置了,所以不用再配;
                    PoolingHttpClientConnectionManager manager = new PoolingHttpClientConnectionManager();
                    // 配置最大的连接数
                    manager.setMaxTotal(300);
                    // 每个路由最大连接数,路由是根据host来管理的,所以这里的数量不太容易把握;
                    manager.setDefaultMaxPerRoute(20);
                    client = HttpClients.custom().
                            setConnectionManager(manager).
                            setDefaultRequestConfig(requestConfig).
                            build();
                }
            }
        }
        return client;
    }

}

这样每次要使用http请求的时候,从这个工具中获取客户端来使用

HttpClientUtils.getHttpClient(); 

为什么要使用单例呢?其实直接HttpClients.custom().setDefaultRequestConfig(requestConfig).build();同样也是使用了连接池的,可以看build中的源码,没有设置manager的时候默认有一个。既然要管理连接池,那么这个管理器就只能有一个,不然管理就乱了,所以我们在使用的时候,只需要要一个CloseableHttpClient,这个client配置一个连接池的管理,每次使用都去找他才能达到连接管理的目的,否则这样写:

public static CloseableHttpClient getHttpClient() {
    return HttpClients.custom().setDefaultRequestConfig(requestConfig).build();
}

每次使用的时候都新建一个客户端,每个客户端是独立的,这样的话每次使用都完全是从头来一次。就好像使用线程池的时候,每次都是一个新的ExecutorService。

我见过的连接池配置人家写了一堆这里就这?

的确我见过人家配置了一堆的东西:

  1. https/http协议策略,这个在PoolingHttpClientConnectionManager的空参数构造函数中就有;除非自己需要一些高级的自定义,否则不用重复添加;
  2. HttpRequestRetryHandler配置的重试策略,对于个人来说像这样的请求不太喜欢重试,失败自己处理比较好;
  3. 有一个定时任务的线程定时检测超过多长时间空闲的连接并销毁;

对于第三点,当我看到人家自己实现的定时检测任务的时候,我就在想,一个成熟的框架,人家不知至于想不到这点,那么就去看看它到底有没有做这件事。果然,框架的确考虑到了,但是这个默认没有开启,先看源码:在类 HttpClientBuilder

if (this.evictExpiredConnections || this.evictIdleConnections) {
    final IdleConnectionEvictor connectionEvictor = new IdleConnectionEvictor((HttpClientConnectionManager)connManagerCopy, this.maxIdleTime > 0L ? this.maxIdleTime : 10L, this.maxIdleTimeUnit != null ? this.maxIdleTimeUnit : TimeUnit.SECONDS, this.maxIdleTime, this.maxIdleTimeUnit);
    closeablesCopy.add(new Closeable() {
        public void close() throws IOException {
            connectionEvictor.shutdown();

            try {
                connectionEvictor.awaitTermination(1L, TimeUnit.SECONDS);
            } catch (InterruptedException var2) {
                Thread.currentThread().interrupt();
            }

        }
    });
    connectionEvictor.start();
}

可以看到,在evictExpiredConnections 或者 evictIdleConnections其中一个属性是true的时候,就会开启定时检测关闭连接的任务,那么我们就可以使用这样的方式来开启它:

client = HttpClients.custom().
            setConnectionManager(manager).
            setDefaultRequestConfig(requestConfig).
            // 简单开启
            evictExpiredConnections().
            // 如果还要自定义超时时间(可以看到它默认的是10s)
            evictIdleConnections(30L, TimeUnit.SECONDS).
            build();

关于这些东西的使用,官方文档也有描述,但是描述的时候,他不会和你解释为什么,所以有的时候,看看源码就能理解它为什么要这样做以及他是如何做到这些的,httpclient的配置可不止这些,如果使用的话可以先看看它有没有帮我们实现,没有的话再去自己做;

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 221,576评论 6 515
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 94,515评论 3 399
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 168,017评论 0 360
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 59,626评论 1 296
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 68,625评论 6 397
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 52,255评论 1 308
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,825评论 3 421
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 39,729评论 0 276
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 46,271评论 1 320
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 38,363评论 3 340
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 40,498评论 1 352
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 36,183评论 5 350
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,867评论 3 333
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 32,338评论 0 24
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 33,458评论 1 272
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 48,906评论 3 376
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 45,507评论 2 359

推荐阅读更多精彩内容