容器云问题1:容器云spring boot应用TCP连接释放问题:Keep-Alive

容器云问题1:容器云spring boot应用TCP连接释放问题:Keep-Alive

在容器云环境中,会存在容器应用TCP连接复用,因为容器的端口资源需要进行有效的释放和复用,因此容器底层实际上已经释放了这个连接。我的spring boot应用中使用的是apache httpclient框架。一般完整的一次http交互,3次握手建立连接,4次握手关闭链接。

1.1持久连接

Web 客户端经常会打开到同一个站点的连接。比如,一个Web 页面上的大部分内嵌图片通常都来自同一个Web 站点,而且相当一部分指向其他对象的超链通常都指向同一个站点。因此,初始化了对某服务器HTTP 请求的应用程序很可能会在不久的将来对那台服务器发起更多的请求(比如,获取在线图片)。这种性质被称为站点局部性(site locality)。

HTTP/1.1(以及HTTP/1.0 的各种增强版本) 允许HTTP 设备在事务处理结束之后将TCP 连接保持在打开状态,以便为未来的HTTP 请求重用现存的连接。在事务处理结束之后仍然保持在打开状态的TCP 连接被称为持久连接。非持久连接会在每个事务结束之后关闭。持久连接会在不同事务之间保持打开状态,直到客户端或服务器决定将其关闭为止。

重用已对目标服务器打开的空闲持久连接,就可以避开缓慢的连接建立阶段。而且,已经打开的连接还可以避免慢启动的拥塞适应阶段,以便更快速地进行数据的传输。目前,各种Http请求框架默认是打开keep-alive,以提高连接性能。

1.1.1 HTTP/1.0的持久连接

在 HTTP/1.0 中,keep-alive 并 不 是 默 认 使 用 的。 客 户 端 必 须 发 送 一 个Connection: Keep-Alive 请求首部来激活keep-alive 连接。虽然,目前keep-alive 已经不再使用了,而且在当前的HTTP/1.1 规范中也没有对它的说明了。但浏览器和服务器对keep-alive 握手的使用仍然相当广泛,因此,HTTP 的实现者应该做好与之进行交互操作的准备。

实现HTTP/1.0 keep-alive 连接的客户端可以通过包含Connection: Keep-Alive首部请求将一条连接保持在打开状态。如果响应中没有Connection: Keep-Alive 首部,客户端就认为服务器不支持keep-alive,会在发回响应报文之后关闭连接。

Connection: Keep-Alive
Keep-Alive: max=5, timeout=120

这个例子说明服务器最多还会为另外5 个事务保持连接的打开状态,或者将打开状态保持到连接空闲了2 分钟之后。

Keep-Alive连接的限制和规则

使用keep-alive 连接时有一些限制和一些需要澄清的地方。
• 在 HTTP/1.0 中,keep-alive 并 不 是 默 认 使 用 的。 客 户 端 必 须 发 送 一 个Connection: Keep-Alive 请求首部来激活keep-alive 连接。
• Connection: Keep-Alive 首部必须随所有希望保持持久连接的报文一起发送。如果客户端没有发送Connection: Keep-Alive 首部,服务器就会在那条请求之后关闭连接。
• 通过检测响应中是否包含 Connection: Keep-Alive 响应首部,客户端可以判断服务器是否会在发出响应之后关闭连接。
• 只有在无需检测到连接的关闭即可确定报文实体主体部分长度的情况下,才能将连接保持在打开状态——也就是说实体的主体部分必须有正确的Content-Length,有多部件媒体类型,或者用分块传输编码的方式进行了编码。在一条keep-alive 信道中回送错误的Content-Length 是很糟糕的事,这样的话,事务处理的另一端就无法精确地检测出一条报文的结束和另一条报文的开始了。
• 代理和网关必须执行 Connection 首部的规则。代理或网关必须在将报文转发出去或将其高速缓存之前,删除在Connection 首部中命名的所有首部字段以及Connection 首部自身。
• 严格来说,不应该与无法确定是否支持 Connection 首部的代理服务器建立keep-alive 连接,以防止出现下面要介绍的哑代理问题。在实际应用中不是能做到这一点的。
• 从技术上来讲,应该忽略所有来自 HTTP/1.0 设备的 Connection 首部字段(包括Connection: Keep-Alive),因为它们可能是由比较老的代理服务器误转发的。但实际上,尽管可能会有在老代理上挂起的危险,有些客户端和服务器还是会违反这条规则。
• 除非重复发送请求会产生其他一些副作用,否则如果在客户端收到完整的响应之前连接就关闭了,客户端就一定要做好重试请求的准备。

1.1.2 HTTP1.1的持久连接

与HTTP/1.0+ 的keep-alive 连接不同,HTTP/1.1 持久连接在默认情况下是激活的。除非特别指明,否则HTTP/1.1 假定所有连接都是持久的。要在事务处理结束之后将连接关闭,HTTP/1.1 应用程序必须向报文中显式地添加一个Connection: close 首部。这是与以前的HTTP 协议版本很重要的区别,在以前的版本中,keepalive连接要么是可选的,要么根本就不支持。

HTTP/1.1 客户端假定在收到响应后,除非响应中包含了Connection: close 首
部,不然HTTP/1.1 连接就仍维持在打开状态。但是,客户端和服务器仍然可以随时关闭空闲的连接。不发送Connection: close 并不意味着服务器承诺永远将连接保持在打开状态。

HTTP1.1持久连接的限制和规则

在持久连接的使用中有以下限制和需要澄清的问题。
• 发送了 Connection: close 请求首部之后,客户端就无法在那条连接上发送更多的请求了。
• 如果客户端不想在连接上发送其他请求了,就应该在最后一条请求中发送一个Connection: close 请求首部。
• 只有当连接上所有的报文都有正确的、自定义报文长度时——也就是说,实体主体部分的长度都和相应的Content-Length 一致,或者是用分块传输编码方式编码的——连接才能持久保持。
• HTTP/1.1 的代理必须能够分别管理与客户端和服务器的持久连接——每个持久连接都只适用于一跳传输。
• (由于较老的代理会转发 Connection 首部,所以)HTTP/1.1 的代理服务器不应该与HTTP/1.0 客户端建立持久连接,除非它们了解客户端的处理能力。实际上,这一点是很难做到的,很多厂商都违背了这一原则。
• 尽管服务器不应该试图在传输报文的过程中关闭连接,而且在关闭连接之前至少应该响应一条请求,但不管Connection 首部取了什么值,HTTP/1.1 设备都可以在任意时刻关闭连接。
• HTTP/1.1 应用程序必须能够从异步的关闭中恢复出来。只要不存在可能会累积起来的副作用,客户端都应该重试这条请求。
• 除非重复发起请求会产生副作用,否则如果在客户端收到整条响应之前连接关闭了,客户端就必须要重新发起请求。
• 一个用户客户端对任何服务器或代理最多只能维护两条持久连接,以防服务器过载。代理可能需要更多到服务器的连接来支持并发用户的通信,所以,如果有N 个用户试图访问服务器的话,代理最多要维持2N 条到任意服务器或父代理的连接。

1.2总结

在实际的应用环境中,会存在各种代理、网关组件、HTTP1.0/1.1协议混杂等因素,因此我们用充分了解自己系统的情况,实现适合自己系统的持久连接方案。

2 容器云Spring boot应用Apache HTTPClient框架中持久连接问题

介绍一下我的系统,系统采用了微服务架构,前后端分离,所有微服务被微服务网关代理。应用部署在容器云平台中,由于应用的Spring bootHTTPClient请求框架和容器不兼容,导致TCP连接复用报错。
为了解决这个问题,我做了2个改造
1、在请求头中加keep-alive参数如下:

Connection: Keep-Alive
Keep-Alive: max=5, timeout=120

2、设置Apache HTTPClient底层工厂类。

HttpClientBuilder clientBuilder = HttpClientBuilder.create()
.setConnectionTimeToLive(120, TimeUnit.SECONDS);
HTTPComponentsClinetHttpRequestFactory reqFactory = new HTTPComponentsClinetHttpRequestFactory (clientBuilder.builder());
restTemplate.setRequestFactory(reqFactory);

最终解决了TCP连接复用的问题。但是不知道改造1有没有产生什么效果。

为了可以提高HTTP 的连接性能。HTTP协议引入了四种技术。
• 并行连接
通过多条TCP 连接发起并发的HTTP 请求。
• 持久连接
重用TCP 连接,以消除连接及关闭时延。
• 管道化连接
通过共享的TCP 连接发起并发的HTTP 请求。
• 复用的连接
交替传送请求和响应报文(实验阶段)。

HTTP/1.1 允许在持久连接上可选地使用请求管道。这是相对于keep-alive 连接的又一性能优化。在响应到达之前,可以将多条请求放入队列。当第一条请求通过网络流向地球另一端的服务器时,第二条和第三条请求也可以开始发送了。在高时延网络条件下,这样做可以降低网络的环回时间,提高性能。

延伸阅读:

https://stackoverflow.com/questions/31566851/setconnecttimeout-vs-setconnectiontimetolive-vs-setsockettimeout
https://www.cnblogs.com/yunnick/p/11304097.html

参考:

《HTTP 权威指南》

你可能感兴趣的:(容器云)