HTTP权威指南-4
2016-09-29TCP连接
- TCP的可靠数据管道
- TCP流是分段的、由IP分组传送。(TCP的数据是通过名为IP分组或者IP数据报的小数据块来发送的)
HTTP与HTTPS
HTTP | 应用层 |
---|---|
TCP | 传输层 |
IP | 网络层 |
网络接口 | 数据链路层 |
HTTP | 应用层 |
---|---|
TSL or SSL | 安全层 |
TCP | 传输层 |
IP | 网络层 |
网络接口层 | 数据链路层 |
- 保持TCP连接持续不断地运行(TCP通过端口号保持所有持续运行的TCP连接),TCP连接通过4个值来识别
<源IP地址、源端口号、目的IP地址、目的端口号>
进而一起定义唯一的一条连接。 - 使用TCP套接字编程
套接字API调用 | 描述 |
---|---|
s = socket(<parameters> ) |
创建一个新的、未命名、未关联的套接字 |
bind(s,<local IP:port> ) |
向套接字赋一个本地端口号和接口 |
connect(s, <remote IP:port> ) |
创建一条连接本地套接字与远程主机及端口的连接 |
listen(s,….) | 标识一个本地套接字,使其可以合法接受连接 |
s2 = accept(s) | 等待某人建立一条到本地端口的连接 |
n = read(s, buffer, n) | 尝试从套接字向缓冲区读取n个字节 |
n = write(s, buffer, n) | 尝试从缓冲区中向套接字写入n个字节 |
close(s) | 完全关闭TCP连接 |
shutdown(s, <side> ) |
只关闭TCP连接的输入或输出端 |
getsockopt(s,…) | 读取某个内部套接字配置选项值 |
setsockopt(s,…) | 修改某个内部套接字配置选项值 |
对TCP性能的考虑
- HTTP事务的时延
- 客户端首选需要根据URL确定Web服务器的IP地址和端口号,如果最近没有对URL中的主机名进行访问,通过DNS解析系统将URL中的主机名转换成一个IP地址可能需要花费数十秒的时间
- 客户端会向服务器发送一条TCP连接请求,并等待服务器回送一个请求接受应答,每条新的TCP连接都会有连接时延,这个值通常最多只有一两秒钟,但如果数百个HTTP事务的话,这个值会快速地叠加上去。
- 一旦连接建立起来了,客户端就会通过新建立的TCP管道来发送HTTP请求,数据到达时,Web服务器会从TCP连接中读取请求报文,并对请求进行处理,网络传输请求报文以及服务器处理请求报文都需要时间。
- Web服务器会会送HTTP响应,这也需要花费时间。
总结:这些TCP网络时延的大小取决于硬件速度,网络和服务器的负载,请求和响应报文的尺寸,以及客户端和服务器之间的距离。TCP协议技术的复杂性也会对时延产生巨大对影响。
- 性能聚集区域
- TCP连接建立握手
- TCP慢启动拥塞控制
- 数据聚集的Nagle算法
- 用于捎带确认的TCP延迟确认算法
- TIME_WAIT时延和端口耗尽
- TCP连接的握手时延(SYN/SYN+ACK三次握手)SYN是一种标记,表明是连接请求
- 延迟确认
- TCP慢启动‘自动调谐’
- Nagle算法与TCP_NODELAY(每个TCP段至少装载40个字节的标记和首部,如果TCP发送了大量包含少量数据的分组,网络性能就会严重下降,这种行为称为”发送端傻窗口综合症”;Nagle算法就是试图在发送一个分组之前,将大量TCP数据绑定在一起,以提高效率。可以在栈中设置参数TCP_NODELAY来禁用Nagle算法,如果需要这么做一定要确保会向TCP写入大块的数据,这样就不会产生一堆小分组了)
- TIME_WAIT累积与端口耗尽
HTTP连接的处理
- 常被误解的Connection首部
- HTTP首部字段名,列出了只与此链接有关的首部
- 任意标签值,用于描述此连接的非标准选项
- 值close,说明操作完成之后需关闭这条持久连接
- ⚠️如果报文中包含连接相关的信息,不能将其转发出去,在转发之前,必须删除Connection首部列出的所有首部字段。进而可以防止无意中对本地首部的转发。被称为对首部的保护。
HTTP/1.1 200 OK
Cache-control: max-age=3600
Connection: meter, close, bill-my-credit-card
Meter: max-users=3, max-refuses=6, dont-report
首部说明: 不应该转发Meter首部,要应用假象的bill-my-credit-card选项,且本次事务结束之后应关闭持久连接。
- 串行事务处理时延
四类提高HTTP的连接性能的方法
- 并行连接(通过多条TCP连接发起并发的HTTP请求)
- 持久连接(重用TCP连接,以消除连接及关闭时延)
- 管道化连接(通过共享的TCP连接发起并发的HTTP请求)
- 复用的连接(交替传送请求和响应报文)
并行连接
- 并行连接可能会提高页面的加载速度
- 并行的连接不一定更快
- 并行连接可能让人“感觉”更快一些
持久连接
- 在HTTP事务处理结束之后仍然保持在打开状态的TCP连接被称为持久连接。重用已对目标服务器打开的空闲持久连接,就可以避开缓慢的连接建立阶段,并且已经打开的连接还可以避免慢启动的拥塞适应阶段,以便更快速地进行数据的传输。
- 持久及并行连接,缺点:每个事务都会打开/关闭一条新的连接,会耗费时间和带宽,由于TCP慢启动特性的村子啊,每条新连接的性能都会有所降低,可打开的并行连接数量实际上是有限的。所以持久连接与并行连接配合使用是最高效的方式。
- HTTP/1.0 + keep-alive连接
Connection: Keep-Alive
Keep-Alive: max=5, timeout=120
响应报文表明服务器最多还会为另外5个事务保持连接的打开状态,或者将打开状态保持到连接空闲了2分钟之后。
- Keep-Alive连接的限制和规则
- HTTP/1.0中keep-alive并不是默认使用的,在需要保持持久连接的请求报文中添加请求首部Connection: Keep-Alive的请求首部来激活keep-alive本次TCP的持久连接。
- TODO note page: 100
- Keep-Alive哑代理
- 插入Proxy-Connection解决哑代理问题,客户端和服务器之间只有一个代理时可以使用此方式进行解决。如果是多层代理时,则仍然会出现哑代理问题。
- HTTP/1.1持久连接(默认激活,HTTP1.1客户端假定在收到响应后,除非响应中包含了Connection:close首部,否则HTTP/1.1连接就仍然维持打开状态。如果需要在事务结束之后将连接关闭,HTTP/1.1必须向报文中显示地添加一个Connection:close首部,不发送Connection: close并不意味值服务器承若永远连接保持在打开状态)
- 持久连接的限制和规则
- 如果客户端不想在连接上发送请求了就在最后一条请求报文中发送一个Connection: close首部。
- 只有连接上所有的报文都是正确的、自定义报文长度时,连接才能持久保持,也就是说异常时可能会关闭。
- HTTP/1.1的代理必须能够分别管理与客户端和服务器的持久连接–每个持久连接都只适用于一跳传输。
- (由于较老的代理会转发Connection首部)所以HTTP/1.1的代理服务器不应该与HTTP/1.0客户端建立持久连接,会造成哑代理
- HTTP/1.1要求应用程序能够从异步的关闭中恢复出来,只要不村子啊可能累积起来的副作用,客户端都应该重复这条请求。
- 除非重复发起请求会产生副作用,否则如果在客户端收到整条响应之前连接关闭了,客户端就必须要重新发起请求。
- 一个用户客户端对任何服务器或代理最多只能维护两条持久连接,以防服务器过载,代理可能需要更多到服务器的连接来支持并发用户的通信,所以,如果有N个用户试图访问服务器的话,代理最多要维持2N条到任意服务器或父代理的连接。
管道化连接
- HTTP/1.1允许持久连接上可选地使用请求管道,管道化:客户端在响应到达之前,可以将多条请求放入队列,当第一条请求通过网络流向地球另一端的服务器时,第二条,第三条也可以开始发送了,在高时延网络条件下,这样可以降低网络的环回时间,提高性能。
- 对管道化连接有几条限制
- 如果HTTP客户端无法确认连接是持久的,就不应该使用管道。
- 必须按照与请求相同的顺序回送HTTP响应,HTTP报文中没有序号标签,因此如果收到的响应失序了,就没有办法将其与请求匹配起来了。
- HTTP客户端必须做好连接会随时关闭的准备,并准备好重发所未完成的管道化请求。
- HTTP客户端不应该使用管道化的方式发送会产生副作用的请求(比如POST)。
关闭连接的奥秘
- 所有HTTP客户端、服务器端可以在任意时刻关闭一条TCP传输连接。
- 管道连接必须是幂等操作如GET、HEAD、PUT、DELETE、TRACE、OPTIONS,客户端不应该使用非幂等请求如POST,否则在传输连接的过程中连接过早终止就会造成一些不确定的后果,要发送一条非幂等请求,就需要等待来自前一条请求的响应状态。
- TCP连接是双向的,TCP连接的每一个端都有一个输入队列和一个输出队列对于数据的读或写,放入一端输出队列中的数据最终会出现在另一端的输入队列中。应用程序可以关闭TCP输入和输出信道中的任意一个,或者将两者都关闭了,套接字调用close()会将TCP连接的输入和输出信道都关闭调,这种方式称为”完全关闭”,可以用套接字调用shutdown()单独关闭输入或输出信道,被称为”半关闭”。
⚠️关闭连接的输入信道比较危险,因为当另一端向你关闭输入信道时,操作系统会向请求方发送一条TCP”连接被对端重置”的报文,大部分操作系统会将这种情况作为很严重的错误来处理,比如你的连接中有部分数据在缓存中还未读取,这时返回的”连接被对端重置”信息会讲你的缓冲区清空,所以慎重!关闭连接的输出信道总是安全的。