1、HttpClient简介

HttpClient相比传统JDK自带的URLConnection,增加了易用性和灵活性,它不仅使客户端发送Http请求变得容易,而且也方便开发人员测试接口(基于Http协议的),提高了开发的效率,也方便提高代码的健壮性。因此熟练掌握HttpClient是很重要的必修内容,掌握HttpClient后,相信对于Http协议的了解会更加深入。

org.apache.commons.httpclient.HttpClient与org.apache.http.client.HttpClient的区别Commons的HttpClient项目现在是生命的尽头,不再被开发,  已被Apache HttpComponents项目HttpClient和HttpCore  模组取代,提供更好的性能和更大的灵活性。

2、HTTP的Keep-Alive

在前面的博客中http协议中,可以看到http的请求头中可以设置connection可以设置为Keep-Alive,在HTTP/1.1使用Keep-Alive为默认值,如果需要关闭则需要手动关闭。

在HTTP 1.0以前,每个http请求都要求打开一个TCP socket连接,并且使用一次之后就断开这个TCP连接,这会导致频繁地创建和销毁TCP。HTTP 1.1通过使用keep-alive可以改善这种状态,即在一次TCP连接中可以持续发送多份数据而不会断开连接,以此提高性能和提高http服务器的吞吐率(更少的tcp连接意味着更少的系统内核调用,socket的accept()和close()调用)。

2.1 HTTP的Keep-Alive

当保持长连接时,如何判断一次请求已经完成?
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连接。

2.2 Tcp的Keep-Alive

tcp链接建立之后,如果应用程序或者上层协议一直不发送数据,或者隔很长时间才发送一次数据,当链接很久没有数据报文传输时如何去确定对方还在线,到底是掉线了还是确实没有数据传输,链接还需不需要保持,这种情况在TCP协议设计中是需要考虑到的。
TCP协议通过一种巧妙的方式去解决这个问题,当超过一段时间之后,TCP自动发送一个数据为空的报文给对方,如果对方回应了这个报文,说明对方还在线,链接可以继续保持,如果对方没有报文返回,并且重试了多次之后则认为链接丢失,没有必要保持链接。

2.3 http keep-alive与tcp keep-alive

HTTP位于网络协议栈的应用层,而TCP位于网络协议栈的传输层,两者的KEEP-ALIVE虽然名称相同,但是作用不同。http keep-alive是为了让tcp活得更久一点,以便在同一个连接上传送多个http,提高socket的效率。而tcp keep-alive是TCP的一种检测TCP连接状况的保鲜机制。t检测对端是否依然存活。

2.4 开启Keep-Alive的优缺点

优点:Keep-Alive模式更加高效,因为避免了连接建立和释放的开销。

缺点:长时间的Tcp连接容易导致系统资源无效占用,浪费系统资源。

所以对于需要频繁发送HTTP请求的应用,需要在客户端开启keep-alive,使用HTTP长连接。

3 HttpClient设置

httpClient = HttpClients.custom()
//连接池配置
.setConnectionManager(poolingHttpClientConnectionManager)
//requestConfig配置
.setDefaultRequestConfig(requestConfig)
.disableCookieManagement()
.disableConnectionState()
.disableAuthCaching()
//默认socketConfig配置
.setDefaultSocketConfig(socketConfig)
//默认头配置
.setDefaultHeaders(defaultHeaders)
//重试handle
.setRetryHandler(httpRequestRetryHandler)
.build();

3.1 PoolingHttpClientConnectionManager连接池设置

两个主机建立连接的过程是很复杂的一个过程,涉及到多个数据包的交换,并且也很耗时间。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

3.2 RequestConfig设置

主要用于获取和配置一些外部的网络环境

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();

3.3 SocketConfig配置

 SocketConfig.custom()
//开启监视TCP连接是否有效
.setSoKeepAlive(false)
//是否可以在一个进程关闭Socket后,即使它还没有释放端口,其它进程还可以立即重用端口
.setSoReuseAddress(true)
//接收数据的等待超时时间,单位ms
.setSoTimeout(10000)
//是否立即发送数据,设置为true会关闭Socket缓冲,默认为false
.setTcpNoDelay(false)
.build();

3.4 defaultHeader配置

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"));

3.5 HttpRequestRetryHandler配置

//禁用重试(参数: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连接池参数配置及源码解读

httpClient 4.3.x configuration 官方样例

使用httpclient必须知道的参数设置及代码写法、存在的风险

HttpClient连接池的连接保持、超时和失效机制

HttpClient连接池原理及一次连接时序图

---恢复内容结束---

最新文章

  1. Windows 10下通过蓝牙连接iPhone个人热点进行共享上网
  2. ThoughtWorks持续集成平台GO开源了
  3. (翻译)初学者的object-C指南
  4. exe转msi
  5. Linux命令 find和mv的结合使用:查找文件,移动到某个目录
  6. VC中监测函数运行时间(一)—分钟,秒,毫秒
  7. map和lambda
  8. JS功能代码集锦
  9. Django模型中value函数运用
  10. 爬虫(scrapy第一篇)
  11. Vue—组件传值及vuex的使用
  12. vue服务器端渲染
  13. chown nagios:nagios -R /var/lib/php/
  14. Android逆向 Android平台虚拟机
  15. 理解js事件循环(event loop)
  16. three添加和移除对象
  17. 序列化和反序列化(json 和pickle)dumps 为序列化, json为反序列化
  18. KM bfs写法
  19. 从乌云的错误漏洞分析看Mifare Classic安全
  20. 差看windows上进程及线程

热门文章

  1. uiautomator 调试例子
  2. AWS云部署项目——数据库与服务器
  3. ASP.NET Core 中的 依赖注入介绍
  4. MySQL学习笔记2————基础篇记录
  5. WC 2008 观光计划(斯坦纳树)
  6. 京东联盟开发(12)——删除MySQL表中重复记录并且只保留一条
  7. No package python-pip available. 解决方法
  8. Laravel源码解析之model(代码)
  9. [转帖]Linux date命令的用法(转)
  10. sql server锁表、查询被锁表、解锁被锁表的相关语句