HTTP 前世今生
HTTP 是什么
HTTP
(Hypertext Transfer Protocol)是客户端与服务器之间的通信语言,早期用来传输超文本内容,故亦被称作超文本传输协议。
HTTP/0.9
HTTP/0.9
用来在网络之间传递 HTML
超文本内容,采用请求-响应模式,客户端发起请求,服务器返回数据。以下是 HTTP/0.9
完成一次请求的全过程:
域名解析,获得域名映射的 ip 地址
根据 ip 地址、端口号,建立
TCP
连接(三次握手)发起请求,请求行 GET /index.html
服务器返回
HTML
文本HTML
传输完成后,断开连接(四次挥手)
特点:
没有请求头和请求体,只需要一个请求头就可以清楚表达客户端的需求
没有返回头信息,服务器返回对应的数据即可,不需要告诉客户端太多的信息
文件内容以
ASCII
字节码传输
HTTP/1.0
拨号上网的出现,多媒体技术的发展以及浏览器的问世,万维网进入高速发展的阶段。在浏览器中展示的不仅仅只是 HTML
文件,还包括 JavaScript
、CSS
、音视频等不同类型的文件,如何支持多类型文件下载成为 HTTP/1.0
的核心诉求。
要支持多种类型文件的下载,浏览器需要知道服务器返回的数据是什么类型,才能根据对应的类型去解析,为此,HTTP/1.0
引入了请求头和响应头,它们都是以 Key-Value
形式保存,客户端发起请求时会携带请求头信息,服务器返回数据时先返回响应头信息。
RequestHeader
Accept 浏览器端接收的格式
Accept-Encoding 浏览器端接收的编码方式
Accept-Language 浏览器端接收的语言,用于判断服务器多语言
Cache-Control 控制缓存的时效性
Connection 连接方式,如果是 keep-alive
,且服务端支持,则会复用连接
Host HTTP
访问使用的域名
If-Modified-Since 上次访问时的更改时间,如果服务端认为此后自己没有更新,则给出 304
响应
If-None-Match 上次访问使用的 E-Tag
,通常是页面的信息摘要,这个比更改时间更准确一些
User-Agent 客户端标识
Cookie 客户端存储的 cookie
字符串
……
ResponseHeader
Cache-Control 缓存控制,用于通知各级缓存保存的时间,例如 max-age=0
,表示不要缓存
Connection 连接类型,keep-alive
表示复用连接
Content-Encoding 内容编码方式,通常是 gzip
Content-Length 内容的长度,有利于浏览器判断内容是否已经结束
Content-Type 内容类型,所有请求网页的都是 text/html
Date 当前的服务器日期
ETag 页面的信息摘要,用于判断是否需要重新到服务端取回页面,是资源的一个唯一标识
Expires 过期时间,用于判断下次请求是否需要到服务端取回页面
Keep-Alive 保持连接不断时需要的一些信息。例如,timeout=5
,max=100
Last-Modified 页面上次修改的时间
Server 服务端软件的类型
Set-Cookie 设置 cookie
,可以存在多个
Via 服务端的请求链路,对一些调试场景至关重要的一个头
……
StatusCode
有的请求服务器可能无法处理,或者处理出错,这时就需要告知浏览器最终的处理结果,这就引入了状态码
常见状态码如下:
2xx 请求成功
200 OK 请求成功
3xx 请求的资源发生了变动,希望客服端进一步处理
301 Moved Permanently 永久重定向
302 Found 临时重定向
304 Not Modified 缓存重定向
4xx 客户端错误
403 Forbidden 无权限
404 Not Found 请求的资源不存在
405 Method Not Allowed
5xx 服务端错误
500 Internal Server Error 服务端错误
503 Service Unavailable 服务端暂时性错误,可以一会再试
Cache
为了减轻服务器的压力,HTTP/1.0
提供 Cache
机制,用来缓存已经下载过的文件。
HTTP/1.1
HTTP/1.1
有两份标准,分别是:
HTTP/1.1
基于 HTTP/1.0
存在的问题做了一些改进。
增加持久连接
HTTP/1.0
每次请求都会经历建立 TCP
连接、传输数据、断开 TCP
连接三个步骤。如果一个页面引用的外部资源过多,重复这样的步骤,无疑会增加大量无谓的开销。HTTP/1.1
中增加了持久连接的方法,在一个 TCP
连接上可以传输多个 HTTP
请求,只要浏览器或者服务器没有明确断开连接,就会一直保持连接。
HTTP/1.1
中默认开启持久连接,Connection: keep-alive
目前浏览器对同一个域名,默认允许同时建立 6 个 TCP
连接,这也是一个性能优化的点。
HTTP 管线化
持久连接虽然减少了 TCP
连接建立和断开的次数,但是需要等到前面的请求响应之后,才能进行下一次请求。如果某个请求没有及时返回,就会阻塞后面的所有请求,这就是著名的队头阻塞。
HTTP
管线化是指将多个 HTTP
请求整批提交给服务器,但是服务器依然需要根据浏览器的请求顺序依次响应。FireFox
、Chrome
都做过管线化的试验,但是由于各种原因,它们最终都放弃了管线化技术。
对动态生成的内容提供完美支持
HTTP/1.0
中,需要在响应头中设置 Content-Length
,浏览器根据设置的数据大小来接收数据。而动态生成的页面,提前是无法确定文件的大小,导致浏览器不知道何时能接收完数据。HTTP/1.1
引入了 Chunk Transfer
机制,服务器会将数据分割成若干数据块,每个数据块发送时会携带上个数据块的长度,最后使用一个零长度的块作为结束的标志。
Cache
浏览器每次发起请求时,先在本地缓存中查找结果以及缓存标识,根据缓存标识来判断是否使用本地缓存。如果缓存有效则使用本地缓存;否则向服务器发器请求,并携带缓存标识。根据是否需要向服务器发起 HTTP
请求,将缓存分为:强缓存和协商缓存,强缓存优于协商缓存。
HTTP
缓存都是从第二次请求开始的:
第一次请求资源时,服务器返回资源,并在响应头中回传资源的缓存策略
第二次请求时,浏览器携带请求头信息到服务端验证,命中强缓存直接返回 200
。否则就把请求头信息传给服务器。看是否命中协商缓存,命中返回 304
;否则服务器会返回新的资源,状态码是 200
。
强缓存
服务器通知浏览器一个缓存时间,在缓存时间内,下次请求,直接用缓存;否则执行比较缓存策略。
Expires
是 HTTP/1.0
的字段,表示缓存到期时间,是一个绝对时间(服务器当前时间 + 缓存时间),在响应头中设置此字段,告诉浏览器,在缓存未过期之前不需要再次请求。由于是绝对时间,用户可能将本地时间进行修改,导致缓存失效;即使不考虑修改,时差或者误差造成两端时间不一致,也会导致缓存失效。由于上述缺点,HTTP/1.1
中增加了 Cache-control
,表示资源缓存的最大有效时间,在这个时间段内,客户端不需要向服务端发起请求。两者的区别,前者是绝对时间,后者是相对时间。
Cache-control
的优先级高于 Expires
。
Cache-control
字段常用的值:
max-age
:最大有效时间must-revalidate
:如果超过了max-age
的时间,浏览器必须向服务器发起请求,验证资源是否有效no-cache
:不使用强缓存,需要与服务器验证资源是否新鲜no-store
:真正意义上的“不要缓存”,所有内容都不走缓存,包括强制和对比public
:所有的内容都可以缓存(包括客户端和代理服务器,如 CDN 等)private
:所有的内容只有客户端才可以缓存,代理服务器不能缓存,默认值
协商缓存
当浏览器的强缓存失效或者请求头中设置了不走强缓存,并在请求头中设置了
If-Modified-Since
或者If-None-Match
,会将这两个属性值到服务端去验证是否命中协商缓存,如果命中协商缓存,会返回304
状态,使用浏览器缓存,响应头中会设置Last-Modified
或者ETag
字段。
HTTP/1.0
If-Modified-Since
/Last-Modified
服务器通过 Last-Modified
字段告知浏览器,资源最后依次修改的时间,浏览器将这个值和内容和一起记录在浏览器缓存中,下一次请求相同资源时,浏览器会在请求头中 If-Modified-Since
中写入上一次 Last-Modified
的值,服务器会将两个字段值进行比对,如果相等,表示未作修改,响应 304
;否则,表示修改了,响应 200
并返回数据。
无法缓存秒级单位以下的更新;服务器动态生成的文件,Last-Modified
的值永远是文件生成时间,起不到缓存的作用。为解决上述问题,HTTP/1.1
引入了 If-None-Match
/ ETag
。
HTTP/1.1
If-None-Match
/ETag
ETag
存储的是文件的特殊标识(一般都是 hash
生成的),服务器通过 ETag
告知浏览器文件的 hash
标识,下一次浏览器请求资源时,会在请求头 If-None-Match
写入 ETag
的值,与服务器文件的 ETag
进行比较,命中响应 304
;否则响应 200 返回新资源
。
HTTP/1.1
的主要问题:对带宽的利用率不理想。主要有以下 3 个原因
TCP
的慢启动。TCP
连接一旦建立,就开始发送数据,刚开始发送数据的速度比较慢,然后慢慢加快发送数据的速度直到达到一个理想的状态,这个过程就叫慢启动。那TCP
慢启动会带来哪些问题呢?一般来讲,一个页面的HTML
、CSS
、JavaScript
文件在TCP
连接建立之后,就要发起请求,由于慢启动的过程,首次渲染页面的时间变长。同时开启多条
TCP
连接,会竞争固定带宽,当带宽不足时,各个TCP
连接接收数据的速度都会减缓,可能会影响到关键资源的下载速度。队头阻塞问题。
基于这这些问题, HTTP/2
是如何解决的呢?
HTTP/2
HTTP/2
的解决思路是:一个域名只使用一个 TCP
长连接和消除队头阻塞,这样一个页面只需要一次慢启动,同时也避免了多个 TCP
连接竞争带宽。
HTTP/2
的核心技术是多路复用,将请求分成一帧一帧的数据去传输,把原来 “Header
+ Body
”的消息打散为二进制帧,用 HEADRS
存放头部数据,DATA
存放实体数据,这种化整为零的思路与 Chunked
分块编码有点像。每一帧都带有请求 ID
,通过协议栈将这些帧发送给服务器,服务器接收到所有帧之后,会把请求 ID
相同的帧合并为一条完整的请求,服务端响应,通过二进制分帧层将数据转换为一个个带有请求 ID
的帧,经协议栈发送给浏览器,浏览器根据 ID
编号将帧的数据提交给对应的请求。
HTTP/2
的其他特性
可以设置请求的优先级
服务器推送
HTTP/2
在一定程度上改变了传统的请求-响应工作模式,服务器可以主动向浏览器发送消息。比如,在浏览器请求 HTML
的时候,就附带把页面用到的 CSS
、JavaScript
文件一并发送给浏览器。
头部压缩
HTTP/2
对请求头和响应头进行了压缩,提升了传输效率。
HTTP/3
持续更新...
参考
浏览器工作原理与实践
透视 HTTP 协议
重学前端
版权声明: 本文为 InfoQ 作者【大导演】的原创文章。
原文链接:【http://xie.infoq.cn/article/7477cb29bf63dc64749d10382】。文章转载请联系作者。
评论