学习目标:
HTTP是如何使用TCP连接的;
TCP连接的时延、瓶颈以及存在的障碍;
HTTP的优化,包括并行连接、keep-alive(持久连接)和管道化连接;
管理连接时应该以及不应该做的事情。
-
HTTP是如何使用TCP连接的:
socket() 创建套接字
bind() 向套接字赋一个本地端口号和接口
connect() 创建一条连接本地套接字与远程主机及端口的连接
listen() 标识一个套接字,使其可以合法接受连接
accept() 等待某人建立一条到本地端口的连接
read() 尝试从缓冲区读取n个字节
write() 尝试向缓冲区写入n个字节
close() 完全关闭TCP连接
shutdown() 只关闭TCP的输入或输出端
getsockopt() 读取某个内部套接字配置选项的值
setsockopt() 修改某个内部套接字配置选项的值 -
TCP连接的时延、瓶颈以及存在的障碍:
TCP时延主要有以下几种原因:
1. TCP连接建立握手:
建立一条新的TCP连接时,甚至是在发送任意数据之前,TCP软件之间会交换一系列的IP分组,对连接的相关参数进行沟通。
2. TCP慢启动拥塞控制:
为了防止因特网的突然过载和拥塞,TCP连接起初会限制连接的最大速度,随着数据的成功传输缓慢提高。
3. 数据聚集的Nagle算法:
TCP允许传输任意字节的数据流,但是如果发送了大量包含少量数据的分组就会降低网络的性能。
Nagle算法试图在发送分组之前,将大量TCP数据绑定在一起,以提高网络效率。
Nagle算法鼓励发送TCP一次允许发送的最大字节,但是小的HTTP报文可能无法填满一份分组。
4. 用于捎带确认的TCP延迟确认算法:
每个TCO段都有一个序列号和数据完整性校验和。每个段的接收者在收到完好的段时,都会像发送者回送小的确认分组。
由于确认报文很小,一般是使用“延时确认”算法进行捎带。
“延时确认”算法会在一个特定的窗口时间内将输出确认存放在缓冲区,以寻找能够捎带它的输出数据分组。
但是HTTP通信具有双峰特征,寻找到合适的输出数据分组的可能性较小。
5. TIME_WAIT时延和端口耗尽:
为了等待旧的TCP数据流在网络上自然消亡,TCP端点在关闭TCP连接时,会在内存中维护一个小的控制块,用来记录最近关闭连接的IP地址和端口号,以保证在2MSL时间内不会创建具有相同地址和端口号的新连接。
在一些服务器进行测试的时候,使用的客户端较少,能够产生的TCP连接是有限的。在有限的时间内,客户端的端口资源可能耗尽。
同时在有大量打开的连接或控制块的情况下,有些操作系统的速度会严重减缓。 -
HTTP的优化,包括并行连接、keep-alive(持久连接)和管道化连接:
为了提高HTTP的连接性能,优化用户的使用体验,有以下几种方法:
并行连接:
通过多条TCP连接发起HTTP请求。
并行连接的连接时延和传输时延同时重叠的,在部分情况下,确实可以提高HTTP性能。
但当客户端网络带宽不足时,所有的连接会去竞争有限的带宽,HTTP性能提升就很小。
同时打开大量连接会消耗很多内存资源,从而引发自身的性能问题。
持久连接:
重用TCP连接,以消除连接及关闭时延。
持久连接可以避免缓慢的连接建立过程,但是管理持久连接时要特别小心。不然累积出大量的空闲连接,服务器的资源就会被白白损耗。
持久连接与并行连接配合使用是一种比较高效的方式。现代的很多Web服务器都会打开少量的并行连接,每一个连接又是持久连接。
持久连接的两种实现方式:
HTTP/1.0+的“Keep-Alive”:
实现HTTP/1.0“Keep-Alive”连接的客户端可以通过Connection: Keep-Alive首部请求将一条连接保持在打开状态,如果服务器支持,就在响应中包含相同的首部,否则忽略该首部。
Keep-Alive只是请求将连接保持在活跃状态,但随时可能被关闭。
Keep-Alive支持名/值对,其实timeout估计了服务器希望保持连接活跃的时间,max说明了服务器还支持为多少个连接保持活跃。
HTTP/1.1的“persistent”:
HTTP/1.1持久连接在默认情况下是激活的,因此在事务结束之后,需要向报文中显式地添加一个Connetion: close首部。
HTTP/1.1仍然可能随时关闭空闲的连接。
管道化连接:通过共享的TCP连接发送并发的HTTP请求。
HTTP/1.1允许在持久连接上可选的使用请求管道。
管道化连接允许在响应到达之前将剩余的请求放入队列。
如果HTTP客户端无法确认连接是持久的,就不应该使用管道。
必须按照与请求相同的顺序回送HTTP响应。
必须做好连接被突然关闭的准备。
只能使用管道化连接发送幂等的请求,例如GET、HEAD、PUT、DELETE,不能发送非幂等的POST。 -
管理连接时应该以及不应该做的事情:
每条HTTP响应都应该有精确的Content-Length首部或是使用分块传输编码。
如果收到的响应主体长度与Content-Lenght不匹配,应质疑数据的正确性。
连接可能在任意时刻被关闭,HTTP程序应做好正确处理非预期关闭的准备。
如果连接异常失败,除非事务处理会带来副作用,否则客户端就应该重新打开连接,重试。