HttpClient相比传统JDK自带的URLConnection,增长了易用性和灵活性,它不只使客户端发送Http请求变得容易,并且也方便开发人员测试接口(基于Http协议的),提升了开发的效率,也方便提升代码的健壮性。所以熟练掌握HttpClient是很重要的必修内容,掌握HttpClient后,相信对于Http协议的了解会更加深刻。html
org.apache.commons.httpclient.HttpClient与org.apache.http.client.HttpClient的区别Commons的HttpClient项目如今是生命的尽头,再也不被开发, 已被Apache HttpComponents项目HttpClient和HttpCore 模组取代,提供更好的性能和更大的灵活性。java
在前面的博客中http协议中,能够看到http的请求头中能够设置connection能够设置为Keep-Alive,在HTTP/1.1使用Keep-Alive为默认值,若是须要关闭则须要手动关闭。nginx
在HTTP 1.0之前,每一个http请求都要求打开一个TCP socket链接,而且使用一次以后就断开这个TCP链接,这会致使频繁地建立和销毁TCP。HTTP 1.1经过使用keep-alive能够改善这种状态,即在一次TCP链接中能够持续发送多份数据而不会断开链接,以此提升性能和提升http服务器的吞吐率(更少的tcp链接意味着更少的系统内核调用,socket的accept()和close()调用)。apache
当保持长链接时,如何判断一次请求已经完成?
Content-Length
Content-Length表示实体内容的长度。浏览器经过这个字段来判断当前请求的数据是否已经所有接收。
因此,当浏览器请求的是一个静态资源时,即服务器能明确知道返回内容的长度时,能够设置Content-Length来控制请求的结束。但当服务器并不知道请求结果的长度时,如一个动态的页面或者数据,Content-Length就没法解决上面的问题,这个时候就须要用到Transfer-Encoding字段。浏览器
Transfer-Encoding
Transfer-Encoding是指传输编码,在上面的问题中,当服务端没法知道实体内容的长度时,就能够经过指定Transfer-Encoding: chunked来告知浏览器当前的编码是将数据分红一块一块传递的。固然, 还能够指定Transfer-Encoding: gzip, chunked代表实体内容不只是gzip压缩的,仍是分块传递的。最后,当浏览器接收到一个长度为0的chunked时, 知道当前请求内容已所有接收。服务器
Keep-Alive timeout:
Httpd守护进程,通常都提供了keep-alive timeout时间设置参数。好比nginx的keepalive_timeout,和Apache的KeepAliveTimeout。这个keepalive_timout时间值意味着:一个http产生的tcp链接在传送完最后一个响应后,还须要hold住keepalive_timeout秒后,才开始关闭这个链接。
当httpd守护进程发送完一个响应后,理应立刻主动关闭相应的tcp链接,设置 keepalive_timeout后,httpd守护进程会想说:”再等等吧,看看浏览器还有没有请求过来”,这一等,即是keepalive_timeout时间。若是守护进程在这个等待的时间里,一直没有收到浏览器发过来http请求,则关闭这个http链接。cookie
tcp连接创建以后,若是应用程序或者上层协议一直不发送数据,或者隔很长时间才发送一次数据,当连接好久没有数据报文传输时如何去肯定对方还在线,究竟是掉线了仍是确实没有数据传输,连接还需不须要保持,这种状况在TCP协议设计中是须要考虑到的。
TCP协议经过一种巧妙的方式去解决这个问题,当超过一段时间以后,TCP自动发送一个数据为空的报文给对方,若是对方回应了这个报文,说明对方还在线,连接能够继续保持,若是对方没有报文返回,而且重试了屡次以后则认为连接丢失,没有必要保持连接。网络
HTTP位于网络协议栈的应用层,而TCP位于网络协议栈的传输层,二者的KEEP-ALIVE虽然名称相同,可是做用不一样。http keep-alive是为了让tcp活得更久一点,以便在同一个链接上传送多个http,提升socket的效率。而tcp keep-alive是TCP的一种检测TCP链接情况的保鲜机制。t检测对端是否依然存活。并发
优势:Keep-Alive模式更加高效,由于避免了链接创建和释放的开销。 app
缺点:长时间的Tcp链接容易致使系统资源无效占用,浪费系统资源。
因此对于须要频繁发送HTTP请求的应用,须要在客户端开启keep-alive,使用HTTP长链接。
httpClient = HttpClients.custom() //链接池配置 .setConnectionManager(poolingHttpClientConnectionManager) //requestConfig配置 .setDefaultRequestConfig(requestConfig) .disableCookieManagement() .disableConnectionState() .disableAuthCaching() //默认socketConfig配置 .setDefaultSocketConfig(socketConfig) //默认头配置 .setDefaultHeaders(defaultHeaders) //重试handle .setRetryHandler(httpRequestRetryHandler) .build();
两个主机创建链接的过程是很复杂的一个过程,涉及到多个数据包的交换,而且也很耗时间。Http链接须要的三次握手开销很大,这一开销对于比较小的http消息来讲更大。可是若是咱们直接使用已经创建好的http链接,这样花费就比较小,吞吐率更大。在高并发大量的请求网络的时候,使用链接池能提高吞吐量。
PoolingHttpClientConnectionManager cm = new PoolingHttpClientConnectionManager(registry); // 设置整个链接池的最大链接数 cm.setMaxTotal(maxTotal); // 设置每一个route默认的最大链接数 cm.setDefaultMaxPerRoute(maxPerRoute); HttpHost httpHost = new HttpHost(hostname, port); // 设置某个route的最大链接数,优先于defaultMaxPerRoute。 cm.setMaxPerRoute(new HttpRoute(httpHost), maxRoute); //该方法关闭超过链接保持时间的链接,并从池中移除。 cm.closeExpiredConnections(); //该方法关闭空闲时间超过timeout的链接,空闲时间从交还给链接池时开始,不论是否已过时,超过空闲时间则关闭。 cm.closeIdleConnections(timeout,tunit);
connectionConfig配置
//消息约束 MessageConstraints messageConstraints = MessageConstraints.custom() .setMaxHeaderCount(200) .setMaxLineLength(2000) .build(); //Http connection相关配置 ConnectionConfig connectionConfig = ConnectionConfig.custom() .setMalformedInputAction(CodingErrorAction.IGNORE) .setUnmappableInputAction(CodingErrorAction.IGNORE) .setCharset(Consts.UTF_8) .setMessageConstraints(messageConstraints) .build(); //通常不修改HTTP connection相关配置,故不设置 //cm.setDefaultConnectionConfig(connectionConfig); //cm.setConnectionConfig(new HttpHost("somehost", 80), ConnectionConfig.DEFAULT);
具体源码解析可参考:https://www.cnblogs.com/shoren/p/httpclient-leaseConnection.html
主要用于获取和配置一些外部的网络环境
RequestConfig requestConfig = RequestConfig.custom() //设置从connectManager获取Connection 超时时间 .setConnectionRequestTimeout(1000) //设置链接超时时间 .setConnectTimeout(10000) //请求获取数据的超时时间 .setSocketTimeout(10000) //肯定是否应自动处理身份验证 .setAuthenticationEnabled(true) //肯定循环重定向(重定向到相同位置)是否应该重定向 .setCircularRedirectsAllowed(false) //重定向的最大数目。对重定向次数的限制是为了防止无限循环 .setMaxRedirects(5) //肯定是否应自动处理重定向 .setRedirectsEnabled(true) //肯定是否应拒绝相对重定向。HTTP规范要求位置值是一个绝对URI .setRelativeRedirectsAllowed(true) //肯定是否应自动解压缩压缩实体 .setContentCompressionEnabled(true) //肯定用于HTTP状态管理的cookie规范的名称 .setCookieSpec("") //返回用于请求执行的本地地址。在具备多个网络接口的计算机上,此参数可用于选择其中的网络接口链接产生。 .setLocalAddress() //代理配置 .setProxy() //在使用代理主机进行身份验证时,肯定支持的身份验证方案的优先顺序。 .setProxyPreferredAuthSchemes() //在使用目标主机进行身份验证时,肯定受支持的身份验证模式的首选项顺序 .setTargetPreferredAuthSchemes() .build();
SocketConfig.custom() //开启监视TCP链接是否有效 .setSoKeepAlive(false) //是否能够在一个进程关闭Socket后,即便它尚未释放端口,其它进程还能够当即重用端口 .setSoReuseAddress(true) //接收数据的等待超时时间,单位ms .setSoTimeout(10000) //是否当即发送数据,设置为true会关闭Socket缓冲,默认为false .setTcpNoDelay(false) .build();
Collection<Header> defaultHeaders = new ArrayList<>(); defaultHeaders.add(new BasicHeader(HttpHeaders.ACCEPT_ENCODING, "gzip, deflate")); defaultHeaders.add(new BasicHeader(HttpHeaders.ACCEPT_LANGUAGE, "zh-CN,zh;q=0.8,en-US;q=0.5,en;q=0.3")); defaultHeaders.add(new BasicHeader(HttpHeaders.ACCEPT, "text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8")); defaultHeaders.add(new BasicHeader(HttpHeaders.CONNECTION, "keep-alive"));
//禁用重试(参数:retryCount、requestSentRetryEnabled) HttpRequestRetryHandler requestRetryHandler = new DefaultHttpRequestRetryHandler(0, false); //自定义重试策略 httpRequestRetryHandler = new HttpRequestRetryHandler() { public boolean retryRequest(IOException exception,int executionCount, HttpContext context) { if (executionCount >= 3) {// 若是已经重试了3次,就放弃 return false; } if (exception instanceof NoHttpResponseException) {// 若是服务器丢掉了链接,那么就重试 return true; } if (exception instanceof SSLHandshakeException) {// 不要重试SSL握手异常 return false; } if (exception instanceof InterruptedIOException) {// 超时 return false; } if (exception instanceof UnknownHostException) {// 目标服务器不可达 return false; } if (exception instanceof ConnectTimeoutException) {// 链接被拒绝 return false; } if (exception instanceof SSLException) {// SSL握手异常 return false; } return false; }
}
其实咱们在实际使用中也是使用默认的 socketConfig 和 connectionConfig。在实际应用中链接数相关配置(如maxTotal、maxPerRoute),还有请求相关的超时时间设置(如connectionTimeout、socketTimeout、connectionRequestTimeout)是比较重要的。
具体链接池原理参考文档:
httpClient 4.3.x configuration 官方样例
使用httpclient必须知道的参数设置及代码写法、存在的风险
---恢复内容结束---