httpclient的连接池
为什么要使用httpclient连接池
连接池是为了复用连接而存在的,就像线程池一样,创建了的线程在执行完成任务后不销毁,而是放入池中待命,以便执行下次任务的时候可以直接从池中取出线程执行,而不是先创建线程再执行,省去了创建线程带来的开销和时间。http连接也是一样的思路,http1.1支持了
我们都知道,http建立连接的过程是比较繁琐的,要经历3次握手和4次挥手,那么省去这个建立连接的过程在高并发的时候就会比较的有必要;同时,类似线程池,总有一个最大的线程数,和线程失效时间,因为在任务空闲的时候,这些空闲线程占用系统资源,所以我们要释放空闲时间长的线程。同样对于http连接,使用的是tcp的长连接,但是长连接的持有是非常耗资源的,特别是对于服务端,链接数是有限的,所以我们同样需要释放一定时间空闲的连接;
什么时候使用httpclient连接池
对自己的系统有正确的预估:
- 调用的请求是否对同一个host大量请求;因为只有对于同一个host,长连接才可能建立,如果每次请求一会一个http://a.com 一会一个 http://b.com 这样是起不到连接池的效果的,并且http/https也是不能共用的,因为他们的端口不同;
- 是否请求会达到一定的量级;如果请求的数量不高,连接池体现不出多大的效果;如果请求达到一定的量级,可能成为系统瓶颈或有较大影响的时候,可以尝试使用;
- 对请求的系统有一定的了解;长连接是双向的,客户端维护连接,服务端同样需要维护连接,并且服务端服务的可能不止一个客户端,就怕到时候这边使用的连接池把服务端的连接占满了,导致服务端无法为其他的客户端提供服务,这个锅可能会扣在自己的头上。
如何使用httpclient
httpclient给我们提供了
还是一样,最简单的用法先来一个使用单例的:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 | 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请求的时候,从这个工具中获取客户端来使用
1 | HttpClientUtils.getHttpClient(); |
为什么要使用单例呢?其实直接
1 2 3 | public static CloseableHttpClient getHttpClient() { return HttpClients.custom().setDefaultRequestConfig(requestConfig).build(); } |
每次使用的时候都新建一个客户端,每个客户端是独立的,这样的话每次使用都完全是从头来一次。就好像使用线程池的时候,每次都是一个新的ExecutorService。
我见过的连接池配置人家写了一堆这里就这?
的确我见过人家配置了一堆的东西:
- https/http协议策略,这个在
PoolingHttpClientConnectionManager 的空参数构造函数中就有;除非自己需要一些高级的自定义,否则不用重复添加; -
HttpRequestRetryHandler 配置的重试策略,对于个人来说像这样的请求不太喜欢重试,失败自己处理比较好; - 有一个定时任务的线程定时检测超过多长时间空闲的连接并销毁;
对于第三点,当我看到人家自己实现的定时检测任务的时候,我就在想,一个成熟的框架,人家不知至于想不到这点,那么就去看看它到底有没有做这件事。果然,框架的确考虑到了,但是这个默认没有开启,先看源码:在类
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 | 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(); } |
可以看到,在
1 2 3 4 5 6 7 8 | client = HttpClients.custom(). setConnectionManager(manager). setDefaultRequestConfig(requestConfig). // 简单开启 evictExpiredConnections(). // 如果还要自定义超时时间(可以看到它默认的是10s) evictIdleConnections(30L, TimeUnit.SECONDS). build(); |
关于这些东西的使用,官方文档也有描述,但是描述的时候,他不会和你解释为什么,所以有的时候,看看源码就能理解它为什么要这样做以及他是如何做到这些的,httpclient的配置可不止这些,如果使用的话可以先看看它有没有帮我们实现,没有的话再去自己做;