HTTP权威指南学习笔记(2)-TCP和HTTP连接管理

Posted by zhidaliao on May 23, 2016

核心知识点

  • HTTP协议 实际上是 TCP连接 + HTTP使用规则, 所以HTTP只是一个应用层协议.
  • TCP 为HTTP提供一条可靠的比特传输管道,按序无差错的传输。
  • HTTPS就是在 HTTP和TCP之间插入一个密码加密层
  • TCP是通过名为 IP分组(或IP数据报) 的小数据块来发送的
  • TCP是通过四个值来识别的 源IP地址、源端口号、目的IP地址、目的端口号;两条不同的TCP连接不能拥有完全相同是四个值

HTTP传输流程

  • 将报文数据通过流数据的形式,通过TCP传输
  • TCP收到数据流之后,将数据流分成小数据块,简称
  • 将段封装在IP分组中,通过因特网进行传输

IP分组的组成

  • IP分组首部(通常20字节): 源和目的IP地址、长度、和其他标记
  • TCP段首部(通常20字节): TCP端口号、TCP控制标记、用户数据排序和完整性检查的数字值
  • TCP段数据(0或更多): 流数据内容

套接字编程

套接字API允许创建TCP的端点数据结构,将这些端点与远程服务器的TCP端点进行通信,进行数据的读写,隐藏了握手细节,以及TCP数据流与IP分组之间的细节

HTTP事务的性能很大程度取决于TCP的性能,

HTTP事务的时延 一个完成的HTTP事务过程 : DNS查询 - 建立TCP连接 - 发起请求 - 事务处理 - 接收响应 - 关闭连接 其中事务处理的耗时是很短的(除非应用服务器过载)

性能聚焦区域:

常见的一些TCP相关时延

TCP连接建立握手

在建立新的TCP连接,甚至是在发送任意数据之前,TCP软件会交换一系列的IP分组, 对连接的有关参数进行沟通, 如果连接之传输少量的数据,这个交换过程会严重降低HTTP的性能。

三次握手机制:

  • 客户端会发送一个包含SYN标记的TCP分组给服务端,代表这个是连接请求
  • 服务端收到这个分组之后,会对连接参数进行计算,并回送一个带有 SYN和ACK标记的分组。代表请求已经被接受
  • 客户端回送服务器一个带有 ACK标记分组,通知服务器连接已经成功建立(现在的TCP栈可以在这个分组中发送数据)

通常HTTP事务的交换的数据不多,小的事务可能在TCP的建立上花费了50%的时间。

解决方案:

用于捎带确认的 TCP延迟确认算法

因特网无法确保数据传输中没有出错,TCP通过自身的机制来保证数据的正确传输:

  • 发送的每个TCP段都有一个数据完整性的校验值,TCP的一端接收到IP分组信息后,检验成功会回送一个确认分组,如果在指定时间内没有收到确认分组,另一端会重新发送TCP段。
  • 因为确认分组很小,所以TCP允许在回送的TCP段中捎带一份确认分组。提高效率

延迟确认算法:

  • 定义:为了提高确认分组找到同向的数据分组,会在一个特定的时间窗口内(100~200Ms),将确认分组放在缓冲池中,等同向的数据分组,如果在规定的时间内没有找到,将单独发送确认分组。
  • 弊端:如果没有那么多同向的数据分组回传,这个时间窗口就会造成性能低下,可以禁止或调整这个特性。
TCP慢启动拥塞控制

TCP的连接速度会随着传输时间自我调整,当TCP成功建立的时候,会限制发送速度;TCP会发送一组数据分组,确认没有问题之后一次发送两组数据,两组数据没有问题会发送四组数据,直到到达上限,所以长时间保持连接传输的TCP连接要比刚开始建立的连接拥有更快的传输速率。

解决方案:重用现存连接,HTTP持久连接。

面试题:HTTP持久连接的原因:TCP建立的50%耗时、传输速率

数据聚集的Nagle 算法

Nagle算法的思想是,每个IP分组至少40个字节,如果要发送的TCP段数据很小,过多的小数据分组会降低TCP性能,所以主张将小的TCP段集合在一个IP分组中发送;

鼓励发送全尺寸的段,因特网上一般是几百字节,如果TCP已经发送了一个全尺寸的段,剩下的数据有两种方式:

  • 等待上次发送的段确认分组到达之后发送
  • 等到填满了一个全尺寸的段再发送。

弊端:

  • 小的HTTP报文可能无法填满一个全尺寸段,这个时间窗口会造成延迟
  • 在等待上个数据的确认分组的时候,本身就存在延迟确认的时间窗口(100~200ms)

禁用Nagle算法的设置参数是TCP_NODELAY,但要确保你发送的数据都是大尺寸的。

TIME_WAIT 时延和端口耗尽
  • 当某个TCP端点关闭TCP连接的时候,会在内存中维持一个控制块的,用来记录最近连接的IP地址和端口号
  • 会维护一段时间,大概是两分钟(最大分段使用期的两倍),以前路由器速度很慢的时候,预估一个分组信息在因特网丢弃之前,它最多可以保存一分钟
  • 目的是防止两分钟内重新创建了相同地址端口的连接,A端发送给B端的分组信息,因为没有过期,并且是相同的TCP连接,导致数据被重复发送,破坏TCP数据。

实际场景:

  • HTTP服务器在80端口上进行监听,根据格式 <source-IP, source-Port , des-IP , 80>,地址是固定的,只有 source-Port是可以改变的
  • 客户端每次连接,为了连接的唯一性,都会获取一个端口号,假设source主机端口号只有60000个, 那么每秒的连接数就被限制在 60000/120=500个

HTTP连接管理

Connection首部:指定了连接的一些元数据;HTTP应用程序在接到带有Connection首部的报文时,会解析这些值并应用,然后在转发到下一个应用程序之前会删除Connection信息。

提高HTTP性能的几种方式:

  • 并行连接:多个TCP连接发起并行的HTTP请求
  • 持久连接:重用连接,消除TCP创建关闭时延 + 提高传输速率
  • 管道化连接:通过共享的TCP连接发起并行的HTTP请求

并行连接

弊端:

  • 客户端的速率限制,导致连接发生竞争条件。
  • 过多的并发请求消耗很多内存资源,导致客户端卡顿和服务器的压力陡增
  • 所以Agent代理一般的并发数量会控制在4个左右
  • 每个事务都会新建一个连接,会耗费时间和宽带
  • 每次创建的连接速度一开始会被限制(TCP慢启动),每条的性能都有所降低
  • 可打开的并行数量是有限的

持久连接

  • 持久连接降低了时延和建立的开销,保持在调谐状态
  • 要谨慎对待,避免累积出大量的空闲连接

持久连接+并行连接 是最高效的方式:打开少量的并行连接,每条都是持久连接。

持久连接:在事务处理结束后将TCP连接保持在打开状态,以便为未来的HTTP请求重用现存的连接

  • HTTP/1.0 + “keep-alive”
  • HTTP/1.1 “persistent” 连接
keep-alive

客户端的在HTTP首部中加上 Connection:Keep-Alive ,服务端如果支持持久连接,会回送Connection:Keep-Alive

响应首部的参数说明:

  • timeout 是服务器希望保持活跃状态的时间
  • max 服务器还希望为几个事务保持持久连接

其他限制和规则

  • Connection:Keep-Alive 必须随每次的报文带上,否则服务器会关闭此前的连接
  • 严格来说,不应该与无法确定是否支持 Connection 首部的代理服务器建立 keep-alive 连接,以防出现 哑代理的问题
  • 除非重复请求会带来副作用,不然客户端在收到完整的响应之前就关闭了,就必须进行重试

哑代理:

  • 客户端发送带有 Connection:Keep-Alive 报文给代理
  • 愚蠢的代理不做任何处理,原样发送给服务器
  • 服务器回送Connection:Keep-Alive报文给代理 ,代理原样发送给客户端
  • 代理等待服务器关闭连接
  • 客户端发送新的报文给代理,代理并不认为连接会有新的请求到来,请求被忽略
persistent

并行事务