首页 > 技术文章 > http协议:四 (3)http的连接管理

xrxc 2021-08-27 09:57 原文

1、早期的 HTTP 协议使用短连接,收到响应后就立即关闭连接,效率很低;

2、HTTP/1.1 默认启用长连接,在一个连接上收发多个请求响应,提高了传输效率;

3、服务器会发送“Connection: keep-alive”字段表示启用了长连接;

4、报文头里如果有“Connection: close”就意味着长连接即将关闭;

5、过多的长连接会占用服务器资源,所以服务器会用一些策略有选择地关闭长连接;

6、“队头阻塞”问题会导致性能下降,可以用“并发连接”和“域名分片”技术缓解。域名分片,就是申请多个域名,但这些域名最后都映射到同一个网站服务器,这样就可以突破浏览器的限制,让连接数是8*域名的个数 http2在应用层解决了队头阻塞

7、因为TCP协议还有“慢启动”“拥塞窗口等特性,通常新建立的“冷连接”会比打开了一段时间的“热连接”要慢一-些,所以长连接比短连接还多了这一层的优势。

8、在长连接中的一个重要问题是如何正确地区分多个报文的开始和结束,所以最好总使用“Content-L ength”头明确响应实体的长度,正确标记报文结束。如果是流式传输,body 长度不能立即确定,就必须用分块传输编码。

9、利用HTTP的长连接特性对服务器发起大量请求,导致服务器最终耗尽资源“拒绝服务”,这就是常说的DoS。DDOS是分布式Dos

10、 HTTP 的连接管理还有第三种方式pipeline (管道,或者叫流水线),它在长连接的基础上又进了一步,可以批量发送请求批量接收响应,但因为存在一-些问题,Chrome、 Firefox 等浏览器都没有实现它,已经被事实上“废弃”了。

11、 Connection 字段还有一个取值:“Connection: Upgrade",配合状态码101表示协议升级,例如从HTTP切换到WebSocket。

 

问题:

1、在开发基于 HTTP 协议的客户端时应该如何选择使用的连接模式呢?短连接还是长连接?

回复:

开发使用长连接 但结束的时候使用Connection设置为close  避免让费资源

 

2、应当如何降低长连接对服务器的负面影响呢?

1.设置Keep-Alive: timeout=value 默认60s

2.设置请求次数Keepalive_requests 5

3.设置流控

4、增加服务器主机数量,在nginx配置合适的负载均衡策略
5、使用域名分片,并将不同域名指向不同的服务器,对连接进行分流

 

3、服务器或者客户端是怎么是判断一个连接的呢?一个连接具体是什么东西呢?

回复:

http里的连接通常就是tcp连接,也就是调用socket api打开的一个套接字,可以理解成一个流式文件的句柄,可读可写,但数据都是在网络上。
想要理解清楚应该去看一下tcp/ip相关的资料。

 

4、cp要三次握手来保证连接成功,但是老是有人问为什么是三次,不是四次不是五次

回复:

 因为tcp要一来一回才能确认消息能够正确收发,a端一个来回,然后b端一个来回,最少要三次通信,所以这是最经济的做法。

 

5、个人理解,不能把http的队首阻塞归结于请求-响应机制。
tcp是全双工的,完全可以在一个tcp连接上双端同时进行收发。http只是在tcp连接上流动的一堆数据而已,应用层的数据不存在队首阻塞。问题在于http协议自身:它无法明确的标识出某个rsp是哪个req的。如果服务端不等待上一个req-rsp结束就发出另一个rsp,那么客户端无法区分收到的数据。
单连接上的多路复用也是基于请求-响应机制的,虽然一个连接上同时流动着多个req-rsp的数据,但是应用层协议有序号可以区分出rsp是哪个req的。

回复:

 说的没错,req-rsp这个就是请求响应机制,要求必须一来一回,也就造成了消息必须排队处理。
到了http/2,一个流上只跑一个请求响应,而多个流并行,这个队头阻塞的问题也就迎刃而解了。

 

6、有个疑问,对于一个域名最多6-8个链接是指,无论我开多少个页面,只要是同一个域名,就是最多6-8个,还是一个页面最多6-8个?

回复:

 

7、这个域名背后可能有多个服务器,应该怎么理解这个这个长连接建立的双方呢?

回复:域名会映射为多个ip,但tcp建立连接只能是与一个特定的ip,所以长连接就是与这个连接保持的,ip地址的选择策略可以由客户端或者域名服务器某一方来决定。

因为这个服务器的ip地址属于这个域名,所以可以理解为与域名保持了长连接。

 

8、同个域名的并发连接是由谁控制的?

回复:

 http连接由客户端发起,所以当然是由客户端、也就是浏览器来决定了

 

9、请求响应的模式是对于整个客户端,还是对于一个连接而言?有的编程语言会提供异步的客户端类库,还有前端也有ajax请求,这些是不是违反了http协议?

回复:

HTTP协议要求消息的发送必须是一来一回,也就是请求-响应,这与客户端、连接都无关,必须是这样处理。
而那些库则是在HTTP的协议基础上做了封装,表面上看是异步处理的,但下面还是一样,否则就不是HTTP协议了。

 

10、并发连接解决队首阻塞问题,那么服务器是怎么区分这几个连接的,每个连接使用的端口号不一样吗?

回复:

服务器不会区别对待客户端的连接,服务器的端口号都是一样的(比如80),但客户端的多个连接端口号肯定是不一样的,通常是随机选择的。

 

11、我想问一下http和tcp的队首阻塞应该都是属于同步网络I/O问题吧,现在是不是好多应用场景中都在用异步的请求来避免出现这种问题呢?

回复:

不是的。
队头阻塞与同步异步无关,是请求处理的排队问题,如果多个请求不是排队处理,就没有队头阻塞。
异步处理解决的是I/O阻塞浪费CPU的问题,为的是重复利用系统资源,提高请求的处理能力。对于单个请求,该阻塞还是会阻塞,排队的请求也一样会有队头阻塞。

 

12、察到在Wireshark里面的客户端每次会先发送两次连接建立的请求,这和本节所讲的为了避免对头阻塞,客户端会发送多个请求有关系么?

回复:

为了加快下载速度,浏览器都会开多个连接,也就是并发连接,你观察到的就是这个现象。
如果有很多请求,浏览器就会在这些连接上排队发送,肯定要比只有一个连接要快。

 

13、服务端对每个客户端的连接数量没有限制吧?如果我自己实现一个http的客户端,那我可以开100个连接吗?这样只是不符合rfc规范,但是实现是没有问题的?
如果说服务器端对每个客户端的连接有限制,比如客户端发了100个连接,它是怎么识别这100个连接来自于同一个客户端呢?根据ip?貌似也不行,因为一个主机上可以开多个不同的客户端。
文中说的连接数限制是说的浏览器的限制吧,比如我们选定了一款浏览器,这个浏览器在访问一个站点时最多只开6个连接。我们在不能修改浏览器代码的情况下如果想加大连接数量,这时候就想出了域名分片这个方法,这时候同一个客户端连接到同一个服务器的连接数量就大大增加了。
如果能改浏览器代码,那么直接修改连接数量,这样也不用曲折的通过域名分片来实现了。反正服务器对连接数量没限制。

作者回复:
1.自己实现当然可以开任意多个连接,像测试工具ab就是这样,开多个并发连接测试。
2.服务器可以根据客户端的ip地址进行限制,Nginx就有相应的模块。
3.客户端并发太多连接会消耗服务器的资源,大多数服务器都会限制。
4.域名分片是为了解决浏览器的限制而产生的,但还是要考虑服务器可能的限制。

 

补充:

http有对头阻塞tcp也有对头阻塞,http运行在tcp上所以就有两种对头阻塞

队头阻塞有两个层面,一个是HTTP的长连接排队请求处理,另一个是TCP的丢包重传机制

以下引用自《Web 性能权威指南》
每个 TCP 分组都会带着一个唯一的序列号被发出,而所有分组必须按顺序传送到接收端。如果中途有一个分组没能到达接收端,那么后续分组必须保存到接收端的 TCP 缓冲区,等待丢失的分组重发并到达接收端。这一切都发生在 TCP 层,应用程序对 TCP 重发和缓冲区中排队的分组一无所知,必须等待分组全部到达才能访问数据。在此之前,应用程序只能在通过套接字读数据时感觉到延迟交互。这种效应称为 TCP 的队首阻塞。

 

tcp握手1个rtt,挥手2个rtt,一个来回就是1rtt,三次回收准确来说是1.5个rtt,四次挥手是两个来回,所以是2rtt。

“高并发请求”是服务器端的概念,意思是同时有多个客户端连接服务器。
“并发连接”是客户端的概念,意思是一个浏览器并发多个连接,访问服务器。
负载均衡是服务器端的概念,就是把大量的客户端连接均匀分散到集群里的多台服务器。

http是“半双工”?

 

 长连接就是在服务器里保存连接的状态不关闭,也就是不调用close关闭,就实现了长连接tcp不记录次数和时长,但服务器在外部可以记录,到次数或者时间就可以关闭。

 

作为 HTTP/1.x 的连接,请求是序列化的,哪怕本来是无序的,在没有足够庞大可用的带宽时,也无从优化。一个解决方案是,浏览器为每个域名建立多个连接,以实现并发请求。曾经默认的连接数量为 2 到 3 个,现在比较常用的并发连接数已经增加到 6 条。如果尝试大于这个数字,就有触发服务器 DoS 保护的风险。
如果服务器端想要更快速的响应网站或应用程序的应答,它可以迫使客户端建立更多的连接。例如,不要在同一个域名下获取所有资源,假设有个域名是 www.example.com,我们可以把它拆分成好几个域名:www1.example.com、www2.example.com、www3.example.com。所有这些域名都指向同一台服务器,浏览器会同时为每个域名建立 6 条连接(在我们这个例子中,连接数会达到 18 条)。这一技术被称作域名分片。

 

Nginx来说,一个“空闲连接”占据的内存是2K,1M内存大概支持500个连接

Nginx作为反向代理,那么它就把这些并发连接“挡”在了外面,后面的服务器不会受到冲击。

 

同一个连接中,客户端发送一个请求,在收到该请求的响应前,客户端是不能在该连接上发送下一个请求。区分多个请求报文的开始和结束,就不那么困难了,因为服务端响应前应该已经完整读取请求的内容,而第二个请求是在客户端收到响应后发送的

但有了content-length有助于客户端更好更快地处理请求

 

发送请求建立多个连接是自动的还手动的?两个是自动的  三四五呢?

 

 域名分片开了三个域名,那么客户端就可以并发18个连接,这样它就可以用比一个域名多三倍的连接来收发数据,效果肯定会好一些。
虽然实际上还有可能存在阻塞,但因为并发多了,总体上会变好。
当然,这只方便了客户端,但加大了服务器的负担。

 

推荐阅读