1. HTTP连接
HTTP连接实际上就是TCP连接和一些使用连接的规则。TCP为HTTP提供了一条可靠的比特传输管道。从TCP连接一端填入的字节会从另一端以原有的顺序、正确地传送出来。
E.g. http://blog.youkuaiyun.com/okingniko/article/details/50986039
1. 浏览器解析出主机名blog.youkuaiyun.com
2. 浏览器查询这个主机的IP地址(DNS) 101.200.96.31
3. 浏览器获得端口号(80)
4. 浏览器发起到101.200.96.31端口80的连接
5. 浏览器向服务器发送一条HTTP GET报文
6. 浏览器从服务器读取HTTP响应报文
7. 浏览器关闭连接
TCP连接通过4个值<源IP、源端口、目的IP、目的端口>
来识别每一条连接。
2.以TCP套接字的角度看HTTP连接
TODO: 添加常见套接字API的说明
更详细的说明可参见 Unix网络编程:套接字编程
3. HTTP性能的优化
3.1 TCP性能的考虑
- TCP连接建立握手
- TCP慢启动拥塞控制
- 数据聚集的Nagle算法
- Nagle算法试图在发送一个分组之前,将大量的TCP数据绑定在一起,以提高网络效率。Nagle算法会引发几种HTTP性能问题,首先,小的HTTP报文可能无法填满一个分组,辣么就需要等待额外数据,其次,Nagle算法会阻止数据的发送,直到有确定分组抵达为止,但确定分组本身会被延迟确定算法延迟100~200毫秒。
- 可以通过设置参数TCP_NODELAY禁用Nagle算法。
- 用于捎带确定的TCP延迟确定算法
- 为了有效利用网络,TCP将返回的确定信息与输出的数据分组结合在一起。为了增加确定报文找到同向传输报文的可能性,TCP协议栈实现了一种”延迟确认”算法。
- 延迟确定算法会在一个特定的窗口时间(通常是100~200ms)内将输出确认存放在缓冲区中,以寻找能够捎带它的输出数据分组。如果在那个时间段内没有数据分组,就将确认信息放在单独的分组传送。
- TIME_WAIT时延和端口耗尽
3.2 HTTP连接的处理
- 串行事务的处理时延
- 提高HTTP的连接性能
- 并行连接
通过多条TCP连接发起并发的HTTP请求 - 持久连接
重用TCP连接,以消除连接及关闭时延 - 管道化连接
通过共享的TCP连接发起并发的HTTP请求 - 复用的连接
交替传送请求和响应报文(试验阶段)
- 并行连接
3.3 持久连接
- 在事务处理结束之后仍然保持在打开状态的TCP连接被称为持久连接。重用已对目标服务器打开的空闲持久连接,就可以避开缓慢的连接建立阶段。而且,已经打开的连接还可以避免慢启动的拥塞适应阶段,以便更快速地进行数据的传输。
- 持久连接与并行连接配合使用可能是最高效的方式。持久连接有两种类型:HTTP/1.0+的keep-alive连接,以及现代的HTTP/1.1的persistent连接。
keep-alive连接:
- 通过形如
Connection : Keep-Alive
Keep-Alive: max=5, timeout=120
来指定持久连接。上述例子还说明服务器最多还会为另外5个事务保持连接的打开状态,或者将打开状态保持到连接空闲了2分钟之后。
2. Keep-alive和(不理解Connection首部的)代理一起使用时,会引发请求被忽略的情况,所以现代的代理都不转发Connection首部和所有名字出现在Connection值中的首部。可通过插入Proxy-Connection来解决。
HTTP/1.1 持久连接:
- 与HTTP/1.0+的keep-alive连接不同,HTTP/1.1持久连接在默认情况下是激活的。若要在事务处理结束之后将连接关闭,HTTP/1.1应用程序必须向报文中显示添加一个
Connection:close
首部。 - 持久连接的限制和规则
- 发送了Connection:close请求报文后,客户端就无法在那条连接上发送更多的请求了。
- 只有当连接上所有的报文都有正确的、自定义报文长度时——也就是说,实体主体部分的长度都和响应的Content-Length一致,或者是用分块传输编码方式编码的——连接才能持久保持。
- HTTP/1.1的代理必须能够分别管理与客户端和服务器的持久连接——每个持久连接都只适用于一跳传输。
- HTTP/1.1的应用程序必须能够从异步的关闭中恢复回来。只要不存在可能会累积起来的副作用,客户端都应该重试这条请求。
3.4 管道化连接
HTTP/1.1允许在持久连接上可选的使用请求管道。在响应到达之前,可以将多条请求放入队列。
3.5 关闭连接的奥秘
- TCP连接是双向的,TCP连接的每一端都有一个输入队列和一个输出队列,用于数据的读或写。放入一端输出队列中的队列最终会出现在另一端的输入队列中。
- 套接字调用
close()
会将TCP连接的输入和输出信道都关闭了,这被称为完全关闭,而shutdown()
可以单独关闭输入或输出信道,这叫做半关闭。 - 想要正常关闭连接的应用程序应该先关闭其输出信道,然后周期性的检查信道的状态(查找数据或流的末尾)。如果在一定的时间内对端没有关闭输入信道,应用程序可以强制关闭连接,以节省资源。
4. 参考资料
- HTTP权威指南