Intro to HTTP/1
![C-8t64sXYAEf7GG](https://cosmos-x.oss-cn-hangzhou.aliyuncs.com/C-8t64sXYAEf7GG.jpeg)
前言
在 HTTP 发布正式版之前,HTTP 的版本号被定位在 0.9 以区分后来的版本。HTTP/0.9 极其简单:请求由单行指令构成,以唯一可用方法 GET 开头,其后跟目标资源的路径。
GET /mypage.html
请求的响应不包含 HTTP 头,且只支持传输 HTML 类型的文档,无法传输其他类型的文件;也没有状态码或错误代码:一旦出现问题,一个特殊的包含问题描述信息的 HTML 文件将被发回,供人们查看。
HTTP/1.0
由于 HTTP/0.9 协议的应用十分有限,HTTP/1.0 版本发布丰富了 HTTP 协议的内容,增加了 HTTP 的应用场景。HTTP/1.0 的改进具体如下:
- 除了支持 GET 请求类型外,还支持 POST 和 HEAD 请求。
- 为请求和回应都增加了头信息(HTTP header)。
- 为头信息添加了
Content-Type
字段,可以传输任何类型的内容,包括文字、图像、视频等。 - 增加了状态码、权限、缓存(Expires 等)、内容编码(Content-Encoding)等功能。
请求和回应格式
- 请求格式
GET /myimage.gif HTTP/1.0
User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_10_5)
Accept: */*
- 回应格式
HTTP/1.0 200 OK
Content-Type: text/gif
Content-Length: 137582
Expires: Thu, 05 Dec 1997 16:00:00 GMT
Last-Modified: Wed, 5 August 1996 15:55:28 GMT
Server: Apache 0.84
(这里是图片内容)
在上述的请求和响应的头信息中有几个头信息字段,例如 Content-Type、Last-Modified 等,这些字段都包含特定的含义,用来描述一些元数据。但本篇并不会对这些字段做过多的解释,后面会单独有一章详细介绍这些字段,如果 这些字段让你感到困惑,可以先百度查询。
HTTP/1.0 的缺点
HTTP/1.0 版的主要缺点是,每个 TCP 连接只能发送一个请求。发送数据完毕,连接就关闭,如果还要请求其他资源,就必须再新建一个连接。
TCP 连接的新建成本很高,因为需要客户端和服务器三次握手,并且开始时发送速率较慢(slow start)。所以,HTTP 1.0 版本的性能比较差。随着网页加载的外部资源越来越多,这个问题就愈发突出了。
为了解决这个问题,有些浏览器在请求时,用了一个非标准的Connection: keep-alive
字段。这个字段要求服务器不要关闭 TCP 连接,以便其他请求复用。服务器同样回应这个字段。但是,这不是标准字段,不同实现的行为可能不一致,因此不是根本的解决办法。
HTTP/1.1
HTTP/1.1 版本发布,只比 1.0 版本晚了半年。它进一步完善了 HTTP 协议,一直用到了 20 年后的今天,直到现在还是最流行的版本。
长连接
HTTP/1.1 版的最大变化,就是引入了持久连接(persistent connection),持久连接解决的核心问题是一定时间内,同一域名多次请求数据,只建立一次 HTTP 请求,其他请求可复用每一次建立的连接通道,以达到提高请求效率的问题。这里面所说的一定时间是可以配置的,不管你用 的是 Apache 还是 nginx。
因为 TCP 连接长时间不关闭,服务器必须在内存里保存它的状态,这就占用了服务器的资源。如果有大量的空闲长连接只连不发,就会很快耗尽服务器的资源,导致服务器无法为真正有需要的用户提供服务。所以当客户端和服务器发现对方一段时间没有活动,就可以主动关闭连接。不过,规范的做法是,客户端在最后一个请求时,发送 Connection: close
,明确要求服务器关闭 TCP 连接。
![short-lived-persistent](https://cosmos-x.oss-cn-hangzhou.aliyuncs.com/short-lived-persistent.png)
通过上图可以看出,在客户端和服务器需要多次传输时,长链接相比与短链接的效率要高很多。
目前,对于同一个域名,大多数浏览器允许同时建立 6 个持久连接。
![](https://cosmos-x.oss-cn-hangzhou.aliyuncs.com/aomjQ8.png)
由上图可知 HTTP/1.1 客户端和服务端建立链接的特点如下:
- 每个连接都会产生完整的 TCP 握手
- 每个连接都会产生 TLS 握手开销(最好的情况,已恢复)
- 每个连接占用服务器/代理资源(内存,CPU 等)
- 每个连接都与其他连接竞争(拥塞控制中断)
Keep-Alive,他解决了多次连接的问题,但是依然有两个效率上的问题:
-
串行的文件传输。当请求 a 文件时,b 文件只能等待,等待 a 连接到服务器、服务器处理文件、服务器返回文件,这三个步骤。我们假设这三步用时都是 1 秒,那么 a 文件用时为 3 秒,b 文件传输完成用时为 6 秒,依此类推。(注:此项计算有一个前提条件,就是浏览器和服务器是单通道传输)
-
连接数过多。我们假设 Apache 设置了最大并发数为 300,因为浏览器限制,浏览器发起的最大请求数为 6,也就是服务器能承载的最高并发为 50,当第 51 个人访问时,就需要等待前面某个请求处理完成。
域名分片
因为同一个域名,大多数浏览器允许同时建立 6 个持久连接且只有收到回应才会释放,所以如果想让服务器端要更快速的响应网站或应用程序的应答,可以将资源放在不同的域名下。例如,有个域名是 www.example.com
,我们可以把它拆分成好几个域名:www1.example.com
、www2.example.com
、www3.example.com
。所有这些域名都指向同一台服务器,浏览器会同时为每个域名建立 6 条连接。这种技术就被成为域名分片。
![](https://cosmos-x.oss-cn-hangzhou.aliyuncs.com/N2VrQ7.png)
由上图可以看出域名分片前(图右)的网络显然比域名分片后(图左)更加拥挤,适当的域名分片可以提高页面的响应速率。
除非你有紧急而迫切的需求,不要使用这一过时的技术,升级到 HTTP/2 就好了。在 HTTP/2 里,做域名分片就没必要了,HTTP/2 的连接可以很好的处理并发的无优先级的请求,在 HTTP/2 中使用域名分片甚至会影响性能。
管道机制
HTTP/1.1 版还引入了管道机制(pipelining),即在同一个 TCP 连接里面,客户端可以同时发送多个请求。这样就进一步改进了 HTTP 协议的效率。
举例来说,客户端需要请求两个资源。以前的做法是,在同一个 TCP 连接里面,先发送 A 请求,然后等待服务器做出回应,收到后再发出 B 请求。管道机制则是允许浏览器同时发出 A 请求和 B 请求,但是服务器还是按照顺序,先回应 A 请求,完成后再回应 B 请求。
由于浏览器供应商难以实现,现被禁用或删除。
![](https://cosmos-x.oss-cn-hangzhou.aliyuncs.com/RUc1N2.png)
图:管道机制