原创不易,如需转载请【联系作者】或【署名作者并注明文章出处】
谈起http协议,大家第一印象一般都会直接想到tcp-ip + ssl/tls 握手过程。今天从另一个角度为大家梳理一下http相关的一些知识点。
本文主要有7个方面,分别是
1、http演进历史,会大致介绍一下每个版本的更新内容、遇到的问题与当时的改进方案
2、http缓存策略
3、跨域策略
4、http的并发问题
5、get/post区别与常见端口号
6、一些开放性的问题
7、最后把本文成文过程中参考的各位巨人的肩膀给罗列出来。
一、HTTP的演进
1、http 0.9
- 只允许客户端发送GET这一种请求,且不支持请求头
- 只支持一种内容,即纯文本,支持html但 无法插入图片
2、http 1.0
- 请求与响应支持头域
- 响应对象以一个响应状态行开始
- 响应对象不只限于超文本
- 开始支持客户端通过POST方法向Web服务器提交数据,支持GET、HEAD、POST方法
- 支持长连接(但默认还是使用短连接),缓存机制,以及身份认证
3、http 1.1
新特性:
- 默认为长连接
HTTP 1.1支持长连接(PersistentConnection)和请求的流水线(Pipelining)处理,在一个TCP连接上可以传送多个HTTP请求和响应,减少了建立和关闭连接的消耗和延迟,在HTTP1.1中默认开启Connection:keep-alive,一定程度上弥补了HTTP1.0每次请求都要创建连接的缺点。
- 提供了范围请求功能(宽带优化)
HTTP1.0中,存在一些浪费带宽的现象,例如客户端只是需要某个对象的一部分,而服务器却将整个对象送过来了,并且不支持断点续传功能,HTTP1.1则在请求头引入了range头域,它允许只请求资源的某个部分,即返回码是206(Partial Content),这样就方便了开发者自由的选择以便于充分利用带宽和连接。这是支持文件断点续传的基础。
- 提供了虚拟主机的功能(HOST域)
在HTTP1.0中认为每台服务器都绑定一个唯一的IP地址,因此,请求消息中的URL并没有传递主机名(hostname)。但随着虚拟主机技术的发展,在一台物理服务器上可以存在多个虚拟主机(Multi-homed Web Servers),并且它们共享一个IP地址。HTTP1.1的请求消息和响应消息都应支持Host头域,且请求消息中如果没有Host头域会报告一个错误(400 Bad Request)。
- 多了一些缓存处理字段
HTTP/1.1在1.0的基础上加入了一些cache的新特性,引入了实体标签,一般被称为e-tags,新增更为强大的Cache-Control头。
- 错误通知的管理
在HTTP1.1中新增了24个错误状态响应码,如409(Conflict)表示请求的资源与资源的当前状态发生冲突;410(Gone)表示服务器上的某个资源被永久性的删除。
问题:
- 高延迟 — 队头阻塞(Head-Of-Line Blocking)
- 无状态特性 — 阻碍交互
协议对于连接状态没有记忆能力,服务器并不知道它与上一条请求有何关联,换句话说就是掉登录态- 明文传输 — 不安全性
传输内容没有加密,中途可能被篡改和劫持。
改进方案:
- 针对队头阻塞:
1.将同一页面的资源分散到不同域名下,提升连接上限。虽然能公用一个 TCP 管道,但是在一个管道中同一时刻只能处理一个请求,在当前的请求没有结束之前,其他的请求只能处于阻塞状态。
2.减少请求数量
3.内联一些资源:css、base64 图片等
4.合并小文件减少资源数
- 针对不安全性:
1.https
2.token验签
3.定制化/约定:数据加密方案
4、http 2
新特性:
- 二进制分帧传输:
1) HTTP 2.0 的所有帧都采用二进制编码 2) 帧是最小的数据单位,每个帧会标识出该帧属于哪个流,流也就是多个帧组成的数据流。多路复用,就是在一个 TCP 连接中可以存在多个流 3) 帧:客户端与服务器通过交换帧来通信,帧是基于这个新协议通信的最小单位。 4) 消息:是指逻辑上的 HTTP 消息,比如请求、响应等,由一或多个帧组成。 5) 流:流是连接中的一个虚拟信道,可以承载双向的消息;每个流都有一个唯一的整数标识符(1、2 … N);
- 多路复用 — 解决队头阻塞
多路复用允许同时通过单一的HTTP/2.0 连接发起多重的请求-响应消息。有了新的分帧机制后,HTTP/2.0不再依赖多个TCP 连接去处理更多并发的请求。每个数据流都拆分成很多互不依赖的帧,而这些帧可以交错(乱序发送),还可以分优先级。最后再在另一端根据每个帧首部的流标识符把它们重新组合起来。HTTP 2.0 连接都是持久化的,而且客户端与服务器之间也只需要一个连接(每个域名一个连接)即可。
- 头部压缩 — 解决巨大的 HTTP 头部
HTTP/1.1 的首部带有大量信息,而且每次都要重复发送。HTTP/2.0 要求通讯双方各自缓存一份首部字段表,从而避免了重复传输。
- 请求优先级 — 先获取重要数据
浏览器可以在发现资源时立即分派请求,指定每个流的优先级,让服务器决定最优的响应次序。这样请求就不必排队了,既节省了时间,也最大限度地利用了每个连接。
- 服务端推送 — 填补空缺,提高请求效率
请求 index.html 可以把首次依赖的js、css直接返回,方法是在nginx上配置一下就行
- 提高安全性
基于https
问题:
- TCP 以及 TCP+TLS 建立连接的延时:TCP 连接需要和服务器进行三次握手,即消耗完 1.5 个 RTT(Round-Trip Time,往返时延)
TCP 的队头阻塞并没有彻底解决:
- TCP 为了保证可靠传输,有一个“超时重传”机制,丢失的包必须等待重传确认。
- HTTP2 出现丢包时,整个 TCP 都要等待重传,那么就会阻塞该 TCP 连接中的所有请求。
- 多路复用导致服务器压力上升
- 多路复用容易 Timeout
5、http 3【Google基于 UDP 协议的 QUIC 协议】
新特性:
- 改进的拥塞控制、可靠传输
- 快速握手
- 集成了 TLS 1.3 加密
- 多路复用
- 连接迁移
问题:
NAT
在一些 NAT 网络环境下(如某些校园网),UDP 协议会被路由器等中间网络设备禁止,这时客户端会直接降级,选择 HTTPS 等备选通道,保证正常业务请求。
二、 缓存
1、缓存类型
- 200 form memory cache :不访问服务器,一般已经加载过该资源且缓存在了内存当中,直接从内存中读取缓存。浏览器关闭后,数据将不存在(资源被释放掉了),再次打开相同的页面时,不会出现from memory cache。
- 200 from disk cache:不访问服务器,已经在之前的某个时间加载过该资源,直接从硬盘中读取缓存,关闭浏览器后,数据依然存在,此资源不会随着该页面的关闭而释放掉下次打开仍然会是from disk cache。
- 优先访问memory cache,其次是disk cache,最后是请求网络资源
2、强/协商缓存
1)强缓存 expires / cache-control: max-age=600
- Expires:过期时间,如果设置了时间,则浏览器会在设置的时间内直接读取缓存,不再请求
Cache-Control:当值设为max-age=300时,则代表在这个请求正确返回时间(浏览器也会记录下来)的5分钟内再次加载资源,就会命中强缓存。
- private无表明响应只能被单个用户缓存,不能作为共享缓存(即代理服务器不能缓存它)public可省略表明响应可以被任何对象(包括:发送请求的客户端,代理服务器,等等)缓存no-cache可省略缓存前必需确认其有效性no-store无不缓存请求或响应的任何内容max-age=[s]必需响应的最大值
- (1) max-age:用来设置资源(representations)可以被缓存多长时间,单位为秒;
- (2) s-maxage:和max-age是一样的,不过它只针对代理服务器缓存而言;
- (3)public:可省略 指示响应可被任何缓存区缓存;响应可以被任何对象(包括:发送请求的客户端,代理服务器,等等)缓存
- (4)private:无参数,只能针对个人用户,而不能被代理服务器缓存;
- (5)no-cache:可省略 强制客户端直接向服务器发送请求,也就是说每次请求都必须向服务器发送。服务器接收到 请求,然后判断资源是否变更,是则返回新内容,否则返回304,未变更。这个很容易让人产生误解,使人误以为是响应不被缓存。实际上Cache-Control: no-cache是会被缓存的,只不过每次在向客户端(浏览器)提供响应数据时,缓存都要向服务器评估缓存响应的有效性。
- (6)no-store:无参数,禁止一切缓存(这个才是响应不被缓存的意思)。
2)协商缓存
last-modify + if-modify-since http1.0
- Last-Modified:浏览器向服务器发送资源最后的修改时间
- If-Modified-Since:
- 当资源过期时(浏览器判断Cache-Control标识的max-age过期),发现响应头具有Last-Modified声明,则再次向服务器请求时带上头if-modified-since,表示请求时间。服务器收到请求后发现有if-modified-since则与被请求资源的最后修改时间进行对比(Last-Modified),若最后修改时间较新(大),说明资源又被改过,则返回最新资源,HTTP 200 OK;若最后修改时间较旧(小),说明资源无新修改,响应HTTP 304 走缓存。
ETag + if-not-match HTTP 1.1
- Etag是属于HTTP 1.1属性,它是由服务器(Apache或者其他工具)生成返回给前端,用来帮助服务器控制Web端的缓存验证。Apache中,ETag的值,默认是对文件的索引节(INode),大小(Size)和最后修改时间(MTime)进行Hash后得到的。
- if-not-match 当资源过期时,浏览器发现响应头里有Etag,则再次像服务器请求时带上请求头if-none-match(值是Etag的值)。服务器收到请求进行比对,决定返回200或304
3)不缓存 Pragma: no-cache / catch-control: no-store
4)缓存优先级
- 顺序的话是先判断http1.0 的【Pragma: no-cache】再, Cache-Control 再Expires,再【 ETag,最后 Last-Modified,都满足就 304,有一项不满足就 200。】
强缓存
- expires更多是为了支持http/1.0的上古浏览器的响应头,是一个具体的时间点,可能客户端与服务器时间不一致,或者网络延迟导致时间不准确
- cache-control: max-age是一个秒数,两者同时出现以max-age为准
协商缓存
- 一般分布式环境下(比如 CDN)很少使用ETag,因为ETag依赖 Web Server 的哈希算法,不同 Web Server、不同版本、不同的配置,都会导致同样的文件 ETag可能是不相等的。当然了,如果你能限制上述信息都一样,也可以使用ETag,并不绝对。
- Last-Modified时间精度是秒的问题,如果1s内修改了, Last-Modified不会更改,eTag是使用了摘要算法,可以及时刷新
3、不同行为引起的缓存
在URI输入栏中输入然后回车/通过书签访问
- 浏览器发现该资源已经缓存了而且没有过期(通过Expires头部或者Cache-Control头部),没有跟服务器确认,而是直接使用了浏览器缓存的内容。其中响应内容和之前的响应内容一模一样,例如其中的Date时间是上一次响应的时间。所以我们也能看到该资源的Size为from cache
F5/点击工具栏中的刷新按钮/右键菜单重新加载
- F5会让浏览器无论如何都发一个HTTP Request给Server,即使先前的响应中有Expires头部
Ctl+F5
- Ctrl+F5要的是彻底的从Server拿一份新的资源过来,所以不光要发送HTTP request给Server,而且这个请求里面连If-Modified-Since/If-None-Match都没有,这样就逼着Server不能返回304,而是把整个资源原原本本地返回一份,这样,Ctrl+F5引发的传输时间变长了,自然网页Refresh的也慢一些。我们可以看到该操作返回了200,并刷新了相关的缓存控制时间。
- 为了保证拿到的是从Server上最新的,Ctrl+F5不只是去掉了If-Modified-Since/If-None-Match,还需要添加一些HTTP Headers。按照HTTP/1.1协议,Cache不光只是存在Browser终端,从Browser到Server之间的中间节点(比如Proxy)也可能扮演Cache的作用,为了防止获得的只是这些中间节点的Cache,需要告诉他们,别用自己的Cache敷衍我,往Upstream的节点要一个最新的copy吧。
- 在Chrome 51 中会包含两个头部信息, 作用就是让中间的Cache对这个请求失效,这样返回的绝对是新鲜的资源。Cache-Control: no-cache Pragma: no-cache
三、 跨域问题
1、原理
协议 | 站点域名 | 端口号 有一个不一样就是跨域
2、表现
服务端跨域请求的资源无法共享
只要不同源就不能共享localStorage的数据
3、解决方案
1) jsonP,JSONP只支持GET请求,优势在于支持老式浏览器,以及可以向不支持CORS的网站请求数据。
2) Cros,CORS支持所有类型的HTTP请求
3) iFrame嵌套
4) postMessage:postMessage(data,origin)方法允许来自不同源的脚本采用异步方式进行通信,可以实现跨文本档、多窗口、跨域消息传递* safari,父页面无法向iframe里的跨域页面传递信息,用url传值的方法来实现跨域存储功能,可以用页面url参数(safari浏览器可以支持超过64k个字符的长度)
5) 新的跨域策略:跨域隔离 COOP、COEP
四、 并发问题
1、 现代浏览器在与服务器建立了一个 TCP 连接后是否会在一个 HTTP 请求完成后断开?什么情况下会断开?
HTTP/1.0 中,一个服务器在发送完一个 HTTP 响应后,会断开 TCP 链接
- 虽然标准中没有设定,某些服务器对 Connection: keep-alive 的 Header 进行了支持。
- HTTP/1.1 就把 Connection 头写进标准,并且默认开启持久连接,除非请求中写明 Connection: close,那么浏览器和服务器之间是会维持一段时间的 TCP 连接
2、 一个 TCP 连接是可以发送多个 HTTP 请求的。
3、 一个 TCP 连接中 HTTP 请求发送可以一起发送么(比如一起发三个请求,再三个响应一起接收)?
- HTTP/1.1 存在一个问题,单个 TCP 连接在同一时刻只能处理一个请求,任意两个 HTTP 请求从开始到结束的时间在同一个 TCP 连接里不能重叠。
HTTP/1.1 规范中规定了 Pipelining
- 一个支持持久连接的客户端可以在一个连接中发送多个请求(不需要等待任意请求的响应)。收到请求的服务器必须按照请求收到的顺序发送响应。
- 但是这个功能在浏览器中默认是关闭的。由于 HTTP/1.1 是个文本协议,同时返回的内容也并不能区分对应于哪个发送的请求,所以顺序必须维持一致
- 现代浏览器默认是不开启 HTTP Pipelining
问题
- 一些代理服务器不能正确的处理 HTTP Pipelining。
- 正确的流水线实现是复杂的。
- Head-of-line Blocking 连接头阻塞
优化
- 维持和服务器已经建立的 TCP 连接,在同一连接上顺序处理多个请求。
- 和服务器建立多个 TCP 连接。
HTTP2 提供了 Multiplexing 多路传输特性
- 可以在一个 TCP 连接中同时完成多个 HTTP 请求
4、 为什么有的时候刷新页面不需要重新建立 SSL 连接?
- TCP 连接有的时候会被浏览器和服务端维持一段时间。TCP 不需要重新建立,SSL 自然也会用之前的。
5、 浏览器对同一 Host 建立 TCP 连接到数量有没有限制?
- Chrome 最多允许对同一个 Host 建立六个 TCP 连接
如果图片都是 HTTPS 连接并且在同一个域名下,那么浏览器在 SSL 握手之后会和服务器商量能不能用 HTTP2
- 能的话就使用 Multiplexing 功能在这个连接上进行多路传输
用不了 HTTP2 或 https
- 浏览器就会在一个 HOST 上建立多个 TCP 连接,连接数量的最大限制取决于浏览器设置
- 这些连接会在空闲的时候被浏览器用来发送新的请求
- 如果所有的连接都正在发送请求呢?那其他的请求就只能等等了。
五、请求相关
1、 get post 区别
* Get 方法的含义是请求从服务器获取资源,这个资源可以是静态的文本、页面、图片视频等。
* Post向 URI 指定的资源提交数据,数据就放在报文的 body 里。
* GET 方法就是安全且幂等的, POST 都不是
* 安全和幂等的概念:
* 在 HTTP 协议里,所谓的「安全」是指请求方法不会「破坏」服务器上的资源。
* 所谓的「幂等」,意思是多次执行相同的操作,结果都是「相同」的。
2、 常用端口号
200
- 「204 No Content」也是常见的成功状态码,与 200 OK 基本相同,但响应头没有 body 数据。
- 「206 Partial Content」是应用于 HTTP 分块下载或断点续传,表示响应返回的 body 数据并不是资源的全部,而是其中的一部分,也是服务器处理成功的状态。
300
- 301 Moved Permanently」表示永久重定向,说明请求的资源已经不存在了,需改用新的 URL 再次访问。
- 302 Found」表示临时重定向,说明请求的资源还在,但暂时需要用另一个 URL 来访问。
- 301 和 302 都会在响应头里使用字段 Location,指明后续要跳转的 URL,浏览器会自动重定向新的 URL。
- 304 Not Modified」不具有跳转的含义,表示资源未修改,重定向已存在的缓冲文件,也称缓存重定向,用于缓存控制。
400
- 「403 Forbidden」表示服务器禁止访问资源,并不是客户端的请求出错。
500
- 500 Internal Server Error」与 400 类型,是个笼统通用的错误码,服务器发生了什么错误,我们并不知道。
- 「501 Not Implemented」表示客户端请求的功能还不支持,类似“即将开业,敬请期待”的意思。
- 「502 Bad Gateway」通常是服务器作为网关或代理时返回的错误码,表示服务器自身工作正常,访问后端服务器发生了错误。
- 「503 Service Unavailable」表示服务器当前很忙,暂时无法响应服务器,类似“网络服务正忙,请稍后重试”的意思。
六、边界问题
* A、B 机器正常连接后,B 机器突然重启,问 A 此时处于 TCP 什么状态
* [https://github.com/Advanced-Frontend/Daily-Interview-Question/issues/21](https://github.com/Advanced-Frontend/Daily-Interview-Question/issues/21)
* 一个TCP连接可以发多少个HTTP请求
* [https://maimai.cn/article/detail?fid=1565594485&efid=2XGge6_3eNs_d2tiQsBWRw&use_rn=1](https://maimai.cn/article/detail?fid=1565594485&efid=2XGge6_3eNs_d2tiQsBWRw&use_rn=1)
七、参考链接
* [【手动加精】硬核!30 张图解 HTTP 常见的面试题](https://www.cnblogs.com/xiaolincoding/p/12442435.html)
* [HTTP 的发展和演变](https://www.cnblogs.com/xiaolincoding/p/12442435.html)
* [一个TCP连接可以发多少个HTTP请求](https://maimai.cn/article/detail?fid=1565594485&efid=2XGge6_3eNs_d2tiQsBWRw&use_rn=1)
* [浏览器HTTP缓存机制](https://juejin.cn/post/6844903554587574285)
* [强制缓存和协商缓存](https://juejin.cn/post/6844903838768431118)
* [缓存优先级](https://segmentfault.com/q/1010000022541364)
* [http 差异](https://zhuanlan.zhihu.com/p/102561034)
* [HTTP 0.9 HTTP 1.0 HTTP 1.1 HTTP 2.0区别](https://www.cnblogs.com/wupeixuan/p/8642100.html)
* [HTTP缓存控制小结](https://imweb.io/topic/5795dcb6fb312541492eda8c)
原创不易,如需转载请【联系作者】或【署名作者并注明文章出处】
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。