老青菜

HTTP/1.1

2019-06-07

1997年,HTTP/1.1发布,参见rfc2068。1999年,HTTP/1.1发布了更新,参见rfc2616。总的来说这个版本做了很多优化,大致如下:

  1. Header支持Host,可以指定要访问服务器域名。
  2. 支持Persistent Connection,持久连接。
  3. 增加Content-Length,表示响应的数据长度,通过长度来表示Response是否结束,区分Request。
  4. 增加Transfer-Encoding: chunked,分块传输编码,适用于动态内容。

Host

HTTP Header增加Host字段,值对应服务器域名,这样就可以访问到一台机器上部署的不同服务了。

GET /api/user HTTP/1.1\r\n
Host: www.laoqingcai.com\r\n
User-Agent: curl/7.54.0\r\n
Accept: */*\r\n
\r\n

第一行是请求行。
第二行开始是Header信息,可以看到,这里指定了Host是www.laoqingcai.com

Persistent Connection

持久连接,即不断开TCP连接,可以被其他请求复用。和HTTP/1.0不同,HTTP/1.1 Header里默认Connection:keep-alive。当然我们也可以指定Connection: close关闭持久连接。
持久连接的数量不是无限制的,连接数太多,一个是效率问题,一个是服务端的并发数利用率问题。rfc2616里建议针对单个域名连接数不要超过2个,chrome是6个,iOS NSURLSession默认是4个(可修改)。

我们来看下HTTP/1.1的连接的流图:

持久连接的好处很明显,减少了不必要的三次握手和四次挥手,还有TCP连接成功后慢启动的拥塞控制策略。那么是不是持久连接数量越少越好呢?答案当然是否定的,越少的话,队头阻塞现象越多,可能效率会更差。那么什么是队头阻塞(Header Block)?我们接着往下看。

pipelining

HTTP/1.1新增了pipelining(管道化),基于持久连接,一个连接可以同时发送多个请求,不需要等待上个请求的响应。
但是Request和Response没有序号标识关联,服务器只能按照收到Request的顺序依次响应(FIFO)。依然解决不了队头阻塞(Header Block)的问题。
而且pipelining只支持幂等请求,即多次调用不会对服务器资源产生影响,比如GET、HEAD、PUT、DELETE。

Header Block

什么是队头阻塞?简单来说就是队列的头部阻塞后面了。归根结底是因为在HTTP/1.1及以前,Request和Response没有序号标识符,无法关联的,所以下一个Request需要在上一个Response响应之后才能发出去。
在HTTP/1.1里,这个问题就变得明显了,特别是持久连接数设置的太少。如果太多,效率上就和HTTP/1.0就没什么区别了。

下面我们做一个测试,我们在云服务器部署了几个简单的Rest API,用chrome同时打开8个URL地址:

    #api/user这个API会根据delay参数延迟响应,模拟耗时操作
    http://www.laoqingcai.com/api/user?delay=50
    http://www.laoqingcai.com/api/user?delay=51
    http://www.laoqingcai.com/api/user?delay=52
    http://www.laoqingcai.com/api/user?delay=40
    http://www.laoqingcai.com/api/user?delay=41
    http://www.laoqingcai.com/api/user?delay=42
    http://www.laoqingcai.com/api/user
    http://www.laoqingcai.com/api/hello

下面是wireshark抓包的结果:

仔细观察,我们发现了几个现象:

  1. 针对前6个请求,chrome都会进行三次握手,也就是新开一个TCP连接。
  2. 请求7在请求4响应之后才发出去,虽然我们几乎是同时发起的。也就是请求7被阻塞了40s。

第1点验证了chrome针对单个域名最多6个持久连接的设置,关于为什么设置6这个数量,可以看这里Chrome Configurable connections-per-host
第2点很清楚的解释了队头阻塞的现象。

优化

针对队头阻塞和持久连接数的限制,出现了很多优化方案,大致如下:

  1. 域名分片,资源分布在多个子域名。
  2. 资源合并,js文件合并、图片合并css sprites(background-position)、js css内联。

上面几种优化方式效果还是很明显的,第1点增加了连接数,第2点减少或者合请求数量。但是带来了新的问题:

  1. 增加了持久连接数,服务器的并发数就会大大减少。
  2. 文件合并,增加了维护成本。

参考链接

HTTP/1.1 RFC2068
HTTP/1.1 RFC2616
Chrome Configurable connections-per-host
`

Tags: HTTP
使用支付宝打赏
使用微信打赏

若你觉得我的文章对你有帮助,欢迎点击上方按钮对我打赏

扫描二维码,分享此文章