为什么每次app访问服务器都建立新连接 导致服务器大量连接疯涨

时间:2022-10-11 16:11:33

运维发现服务器有大量连接不释放,而且每次app访问都会建立新连接。

netstat -antlp |grep ESTAB|grep 8080|wc -l    (访问服务器8080端口的已建立的连接数)

后来用浏览器测试没问题。接着发现终端连接时,设置了http header: Connection: keep-alive 。但是同样用postman模拟,也没问题。最终发现应该是app写了keepalive,连接调用时确没有用以前打开的连接,而是新建一个连接。

而服务器端wildfly默认打开了keepalive,而且永远不会超时。wildfy的undertow添加keepalive超时设置如下:ms

<http-listener name="default" max-post-size="204857600" socket-binding="http" redirect-socket="https" tcp-keep-alive="true" read-timeout="30000"/>


用telnet模拟http 客户端调用,可以看到当服务器返回内容后,连接并没有断开,直到超时。

如下示例在一个打开的连接里面,做了两次post。

>telnet 211.100.75.241 8080
Trying 211.100.75.241...
Connected to 211.100.75.241.
Escape character is '^]'.
POST /Message/update HTTP/1.1
Host: 211.100.75.241:8080
Connection: keep-alive
Content-Length: 31
Cache-Control: max-age=0
Upgrade-Insecure-Requests: 1
Origin: null
Content-Type: application/x-www-form-urlencoded
User-Agent: Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/63.0.3239.26 Safari/537.36 Core/1.63.5702.400 QQBrowser/10.2.1893.400
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8
DNT: 1
Accept-Encoding: gzip, deflate
Accept-Language: zh-CN,zh;q=0.9 mac=00%3A07%3A63%3Ae2%3Aa7%3A62
HTTP/1.1 200 OK
Connection: keep-alive
X-Powered-By: Undertow/1
Server: WildFly/10
Content-Type: application/json;charset=UTF-8
Content-Length: 1
Date: Mon, 06 Aug 2018 08:48:58 GMT POST /Message/update HTTP/1.1
Host: 211.100.75.241:8080
Connection: keep-alive
Content-Length: 31
Cache-Control: max-age=0
Upgrade-Insecure-Requests: 1
Origin: null
Content-Type: application/x-www-form-urlencoded
User-Agent: Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/63.0.3239.26 Safari/537.36 Core/1.63.5702.400 QQBrowser/10.2.1893.400
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8
DNT: 1
Accept-Encoding: gzip, deflate
Accept-Language: zh-CN,zh;q=0.9 mac=00%3A07%3A63%3Ae2%3Aa7%3A62
HTTP/1.1 200 OK
Connection: keep-alive
X-Powered-By: Undertow/1
Server: WildFly/10
Content-Type: application/json;charset=UTF-8
Content-Length: 1
Date: Mon, 06 Aug 2018 08:49:05 GMT Connection closed by foreign host.

HTTP长连接--Keep-Alive

 

一、HTTP/1.0

HTTP1.0版本的Keep-alive并不像HTTP1.1那样是默认发送的,所以要想连接得到保持,必须手动配置发送connection:keep-alive字段。若想断开keep-alive连接,需发送Connection:close字段

注意:这里的连接是HTTP依赖的传输协议TCP,而不是HTTP本身。

为什么需要长连接?

长连接可以提高连接的利用效率,即HTTP可以复用一条连接。例如某个用户浏览一个网站,他很长一段时间才跳转到另一个链接,似乎保持长连接没有什么好处,很浪费资源。

但如果这个网站的并发量很大,保持长连接的好处就凸显了。如果每个请求都需要建立新的TCP连接,那对服务器的开销是相当的。

建立连接报文首部格式:

Connection:Keep-Alive

Keep-Alive:timeout ; max       #参数之间用;分号隔开

参数:max 最大处理事务数量

timeout 连接保持的时间,单位为秒

断开连接报文首部格式:

Connection:close

Keep-Alive的建立过程

客户端向服务器在发送请求报文同时在首部添加发送Connection字段》服务器收到请求并处理connection字段》服务器回送Connection:Keep-Alive字段给客户端》客户端接收到connection字段》Keep-Alive连接建立成功

服务端自动断开过程:

客户端向服务器只是发送内容报文(不包含Connection字段)》服务器收到请求并处理》服务器返回客户端请求的资源并关闭连接》客户端接收资源,发现没有Connection字段,断开连接

客户端请求断开连接过程:

客户端向服务器发送Connection:close字段》服务器收到请求并处理connection字段》服务器回送响应资源并断开连接》客户端接收资源并断开连接

事务:

在基于TCP连接的http协议中,一个事务表示客户端向服务端请求一个资源并得到响应返回的过程。这个资源可能是html,css,js文件。

例如:

为什么每次app访问服务器都建立新连接 导致服务器大量连接疯涨

表示连接的最大处理事务数是5,连接时间是2分钟

Keep-Alive的限制和规则

(1)事务处理的接收端必须接收到发送端发送的正确的Content-Length字段,或者接收到MIME多部件多媒体类型(允许连接多次发送不同类型的资源)字段或者接收到Chunked分块模式字段。以客户端向服务端发送资源为例(实际上,在一条HTTP信道上,数据传输是双向的,这里只是其中一种情形来讲述)。在一个非Keep—Alive的连接中,每一次资源请求都需要创建一个HTTP连接,出错了重传不会担心前后两次的Content-Length不相同问题。而如果是在Keep-Alive连接中,问题就没有那么简单了。想象一下出错客户端需要重新发送资源给服务端,而此时服务端端保存的可能还是上一条错误报文的Content-Length(可能是0),那么在接收新资源时候就会出现实体内容长度和content-length不一致等问题(这里存在了太多的细节问题)。

(2)不应该与无法确定是否支持Connection首部的代理服务器建立keep-alive连接,以防止出现下面要介绍的哑代理问题。但实际应用中,这一点不是总是能做到。

哑代理问题

盲中继指的是一些自身没有处理Connection:Keep-Alive的能力代理服务器。当客户端向服务端发送Connection字段的时候,通过代理服务器,而代理服务器没有处理Connection字段,而只将其当一个扩展首部来处理,将报文原原本本发送给服务端,服务端接收到Connecton字段后回送Connection字段。这时代理服务器还是原封不动将报文转送给客户端。此时客户端和服务器都建立了Keep-Alive连接,而代理服务器却毫不知情。它一直在等待客户端给它发送断开连接的字段,因为一个事务已经完成,已经没有必要保持连接了。这是建立了Keep-Alive连接客户端继续互相发送数据,而代理服务器收到的不是Connection:close字段,所以对此就会视而不见,就像‘盲’了一样。

根据《HTTP权威指南》中的描述,目前浏览器解决哑代理问题的一个解决方案使扩展首部字段Proxy-Connection,是由Netscape提出的。大致的实现原理是,客户端服务器会向代理服务器发送Proxy-Connection字段,如果对方是聪明的代理,它就会用Connection代替Proxy-Connection字段,发送给服务端,服务端接受到Connection字段之后就知道是要建立持久连接,这样客户端,代理,服务端三者都会明白需要建立一个持久连接。而如果对方是一个盲中继,它就会直接把Proxy-Connection当做扩展首部直接转发给服务端,服务端接收到后发现是Proxy-Connection字段,会自动忽略,也就是说不会建立持久连接,通常接下来事务完成之后就是直接关闭连接了。这样就不会产生上文所述的问题。

由于本人对这方面知识还接触比较少,在这里就不对Proxy-Connection作更深入阐述了。

二、HTTP/1.1

客户端(浏览器)如果使用的是HTTP/1.1版本,默认会发送connection:keep alive字段

,如果没有特殊说明,TCP连接是默认保持的,而需要将一个连接关闭,则需要客户端发送Connection:close首部字段。

HTTP1.1经过版本的更迭,HTTP1.1逐渐停止了对Keep-alive连接的支持,用一种名为持久连接(persistent connection)的改进型机制设计取代了它。这种机制默认连接建立之后是持久的,也就是说客户端不需要依靠首部添加Keep-alive字段来保持连接。在希望断开连接的时候,则需要客户端首部添加Connection:close字段。

持久连接的限制和规则:

(1)HTTP/1.1对代理服务器的要求更高,它必须能管理与客户端和服务端之间的持久连接。

(2)由于HTTP/1.1设备随时可能断开连接(因为出错;或者是连接空闲一段时间后,服务端做了逻辑处理,主动关闭;也可能是其他原因),客户端可能在没有接收到整条完整的响应,服务端就断开了连接。所以客户随时要做好重新发送请求的准备。

(3)一个用户客户端对任何服务器或者代理最多只能维持两条持久连接,以防止服务器过载。所以作为中间者身份的代理服务器可能需要更多到服务器的连接来支持并发的用户请求。例如有N个用户请求不同的服务器,代理需要维护2N条到任意服务器或者父代理的连接。

管道化连接

HTTP/1.1允许在持久连接的基础上*选择使用请求管道。管道的原理很像队列。在响应到达前,客户端发送的请求可以放进管道里面,当第一条请求发送到服务端而还客户端还没有接收到响应的时候,后面的请求可以接着发送了。为什么可以这样?这就是管道的作用了,因为管道中的请求是‘提前准备好的’,它无需等待客户端判断已经接收到响应了再发送新的请求(这是非管道连接的串行请求)。在高时延的网络中,管道化连接有利于降低网络的环回时间,提高网络传输性能。

虽然管道化连接有它强大的优势,但是管道化连接也有它的局限性。

1)客户端的请求在管道中是有一定次序的。但由于HTTP报文是没有顺序,响应报文一旦次序出错,就无法与请求一一对应匹配起来。

2)一般来说,非幂等性(例如POST请求,下文会讲述)请求是不希望放进管道的。原因是,管道传输中如果出现了错误,请求和响应的次序无法得到保障,在一些订单或者涉及资金的POST提交中,结果是灾难性的。

3)在管道传输过程中,服务器关闭了连接,这时有许多没有完成的请求事务,那么客户端和服务端采取什么机制来确认已经处理和没有处理的事务这一问题值得商榷。客户端可能必须重新发送请求连接并且重新发送所有的管道中的请求。

三、重试和幂等性

当事务因为一条持续的连接的意外关闭而被终端,客户端需要重新发送请求。这种重试在管道化连接中更复杂。

一次HTTP请求的类型可能是GET , POST , HEAD , PUT , DELETE , TRACE , OPTION,如果重试前后对服务端的数据没有很大影响的请求类型就是幂等性的。显然,POST是非幂等的。关于幂等性的更多讨论,可以看看这篇文章:https://www.jianshu.com/p/178da1e2903c

四、最后,关于连接的关闭:

什么时候控制性地关闭连接,以及如何关闭连接,以达到信道上最高效率的数据传输是一个值得研究和讨论的问题。《HTTP权威指南》中也没有提供一些可行的方案。以后有接触到相关知识,我会继续补充。

为什么每次app访问服务器都建立新连接 导致服务器大量连接疯涨