最近使用到了HttpClient,看了一下官方文档:HttpClient
实现是线程安全的。建议将这个类的实例重用于多个请求执行,翻译过来的意思就是:HttpClient的实现是线程安全的,可以重用相同的实例来执行多次请求。遇到这种描述的话,我们就应该想到,需要对HttpClient来进行封装了。由于是使用的spring boot,所以下面来结合spring boot来封装HttpClient。
一,请求重试处理
为了使自定义异常机制生效,实现需要HttpRequestRetryHandler
接口,代码如下:
二连池池管理
PoolingHttpClientConnectionManager用来管理客户端的连接池,并且可以为多个线程的请求提供,代码如下:
注意:当HttpClient的实例不再需要并且即将超出范围时,重要的是关闭其连接管理器,以确保管理器保持活动的所有连接都被关闭,并释放由这些连接分配的系统资源
上面PoolingHttpClientConnectionManager类的构造函数如下:
在PoolingHttpClientConnectionManager的配置中有两个最大连接数量,分别控制着总的最大连接数量和每个路线的最大连接数量。如果没有显式设置,默认每个路径只允许最多2个连接,总的连接数量不超过20这个值对于很多并发度高的应用来说是不够的,必须根据实际的情况设置合适的值,思路和线程池的大小设置方式是类似的,如果所有的连接请求都是到同一个网址,那可以把MaxPerRoute的值设置成和MaxTotal一致,这样就能更高效地复用连接
特别注意:想要复用一个连接就必须要让它占有的系统资源得到正确释放,释放方法如下:
如果是使用outputStream就要保证整个实体都被写出,如果是inputStream,则再最后要记得调用inputStream.close()。或者使用EntityUtils.consume(entity)或EntityUtils.consumeQuietly(entity)来让实体被完全耗尽(后者不抛异常)来做这一工作.EntityUtils中有个toString方法也很方便的(调用这个方法最后也会自动把inputStream close掉的,但是在实际的测试过程中,会导致连接没有释放的现象),不过只有在可以确定收到的实体不是特别大的情况下才能使用如果没有让整个实体被完全消费,则该连接是不能被复用的,很快就会因为在连接所以如果想要复用连接,一定一定要记得把实体完全消耗掉,只要检测到流的EOF,是会自动调用ConnectionHolder的releaseConnection方法 行处理的
三,连接保持策略(保持连接策略)
HTTP规范没有指定持久连接可能和应该保持存活多久。一些HTTP服务器使用非标准的Keep-Alive标头来向客户端通信它们打算在服务器端保持连接的时间段(以秒为单位)HttpClient可以使用这些信息。如果响应中不存在保持活动头,HttpClient的会假定连接可以无限期地保持活动。然而,一般使用的许多HTTP服务器都配置为在一段不活动状态之后删除持久连接,以便节省系统资源,而不会通知客户端。如果默认策略过于乐观,则可能需要提供自定义的保持活动策略,代码如下:
注意:长连接并不使用于所有的情况,尤其现在的系统,大都是部署在多台服务器上,且具有负载均衡的功能,如果我们在访问的时候,一直保持长连接,一旦那台服务器挂了,就会影响客户端,同时也不能充分的利用服务端的负载均衡的特性,反而短连接更有利一些,这些需要根据具体的需求来定,而不是一言概括。
四,HttpClient代理配置(代理配置)
用来配置代理,代码如下:
HttpClient的不仅支持简单的直连,复杂的路由策略以及代理.HttpRoutePlanner是基于HTTP上下文情况下,客户端到服务器的路由计算策略,一般没有代理的话,就不用设置这个东西。这里有一个很关键的概念 - 路线指示运行环境机器
五,RequestConfig
用来设置请求的各种配置,代码如下:
RequestConfig是对请求的一些配置。里面比较重要的有三个超时时间,默认的情况下这三个超时时间都为0(如果不设置请求的配置,会在执行的过程中使用HttpClientParamConfig的getRequestConfig中用默认参数进行设置),这也就意味着无限等待,很容易导致所有的请求阻塞在这个地方无限期等待这三个超时时间为:
a,connectionRequestTimeout-从连接池中取连接的超时时间
这个时间定义的是从ConnectionManager管理的连接池中取出连接的超时时间,如果连接池中没有可用的连接,则请求被阻塞,最长等待connectionRequestTimeout的时间,如果还没有被服务,则抛出ConnectionPoolTimeoutException异常,不继续等待
b
连接等待时间定时 时,会抛出ConnectionTimeoutException异常
c,socketTimeout-请求超时时间
这个时间定义了socket读数据的超时时间,也就是连接到服务器之后到从服务器获取响应数据需要等待的时间,或者说是连接上一个url之后到获取响应的返回等待时间。发生超时,会抛出SocketTimeoutException异常。
六,实例化的HttpClient
通过实现FactoryBean的来实例化HttpClient的,代码如下:
七,增加配置文件
八,测试
测试代码如下:
通过上面的几个步骤,就基本上完成了对HttpClient的的封装,如果需要更细致的话,可以按照上面的思路,逐步完善,将HttpClient的封装成HttpClientTemplate,因为CloseableHttpClient内部使用了回调机制,和JdbcTemplate的,或者是RedisTemplate类似,直到可以以spring boot starter的方式提供服务。