无论是对于面试,还是日常的开发,我们都会无时无刻接触到 HTTP 方面的相关知识,最近打算整理一波关于 HTTP 相关的知识,如有错误🙅,请大家指出。
题目一:URI 是什么?
URI(Universal Resource Identifier)统一资源标志符,它能在某一规则下将某一个资源唯一标记出来。比如一个人的身份证在现实生活中是唯一的,那它就是 URI 的实例,因为可以通过身份证指定标记这个人是谁。
在开发的过程中,我们听说过 URL(Universal Resource Locator)统一资源定位符。这两者之前有什么区别呢?
大家可以看一下 URL 的构成内容:
通过这种图我们大体了解 URL 的组成包含了哪些内容,具体就不细讲他们之前的含义了。由于 URL 可通过上面途中这些内容可以访问到一个唯一的站点,从概念上将它是 URI 的一个子集。
题目二:HTTP 状态码
客户端的请求发送给相应的服务器,都会得到服务器返回的一个包含 HTTP 状态码的请求头。这些请求头会告诉我们请求的状态,方面我们针对性的进行处理。
了解 HTTP 状态码有哪些分类
1xx
服务端收到请求,然后需要客户端继续执行请求操作。2xx
服务端接收到请求,并且服务端正常返回请求内容给客户端。3xx
资源重定向。4xx
指客户端请求内容有误。5xx
指服务端存在问题。
常见的 HTTP 状态码有哪些?它们相关的含义是什么?
100: 表示服务端接收到请求的部分内容,客户端应继续发送其请求。101: 表示服务器接收到客户端的切换协议请求,服务器同意切换协议。需要注意的是协议只能从低版本往高版本协议进行切换。如http切换到websocket。200: 表示客户端正常请求到服务端,服务端成功返回响应给客户端。204: 含义与200相同,唯一不同的是没有响应内容。206: 表示服务端处理请求的部分内容。常用于HTTP 分块下载和资源断点续传。300: 表示服务器返回多种资源特征和地址列表供用户选择。301: 表示资源被永久的替换了。之后相应的 URL 请求会自动从新的地址上获取。需要注意的是,被永久替换的地址会被浏览器缓存起来。302: 表示资源被临时改变。由于当前情况不会将改变后的 URL 缓存起来,所以每次请求依然会先调用老的地址,这也是跟301不一样的地方。304: 表示资源未被修改,当前情况服务器不会返回资源,而是会触发协商缓存,从缓存中直接获取。305: 表示所有请求被代理完成307: 临时重定向,跟302的区别在于,它只支持GET请求。400: 表示请求发出的语法存在问题。401: 表示需要进行用户身份验证。403: 表示服务端成功接收到客户端的请求,但是拒绝执行。404: 表示资源找不到。405: 表示当前请求的方法不被支持。408: 表示服务端等待客户端的请求过长,超时了。500: 表示服务端语法有误,导致请求异常。502: 表示网关或者代理服务器存在问题。503: 表示被请求的服务器暂时无法使用,可能在维护或者正在更新发布。504: 表示服务端为及时响应,请求超时。
看到上面的请求状态码是不是很多,估计头会稍微大一点😊,请不要担心,可以先尝试大体看一遍,然后结合实际工作中遇到的,再二次看会容易记住。
题目三:GET 和 POST 的区别
这一个问题其实我在之前写过了,但为了方便再在这边写一下。
首先,我们需要了解 GET 和 POST 是什么。当然,大家应该都知道的。但我还是想多说一下。GET 和 POST 都属于 HTTP 的请求方法。只是设计出来的用途不一样。
- GET 方法用于获取资源,用于
拿取操作。 - POST 方法用于提交数据,用于
提交指定资源。
那它们的区别有哪些呢?下面会详细的列出。
- 我们需要了解,
冥等性和非冥等性。冥等性指对资源无害,不会操作资源的。反之就是非冥等性。GET 方法作为仅仅获取资源,不会对资源进行操作,所以它是冥等的。而 POST 会往服务器推送数据,所以它是非冥等的。 - GET 请求存在安全问题,GET 请求的参数会被按照
&的方式拼接在 URL 链接上。它的内容是可见的,敏感信息如果被携带上去,会被相关不法分子获取,进行非法操作。而 POST 请求它的请求参数会被放置在请求Body中。 - 针对于安全问题,还有一点需要提及的是 GET 请求的参数由于被携带在 URL 上,所以参数只能按照 URL 编码方式进行加密处理,而 POST 请求的参数没有编码格式限制。
- GET 请求的参数大小存在限制。URL 长度是存在限制的,链接最大只能到达
4kb,所以 GET 请求不能携带过多的参数。而 POST 的请求参数大小数量没有限制。 - GET 请求会可以存在书签中,但是 POST 请求是不可以的。
- GET 请求的参数可以被完整的保留下来,而 POST 是不可以的。
- GET 请求参数只能接受 ASCLL 字符,其他字符会被转化为十六进制,而 POST 没有限制。
- GET 请求可以被浏览器主动缓存,而 POST 方法是不可以的,只能通过设置。
- GET 浏览器回退是无害的,但是 POST 回退就会再次发送提交操作。
题目四:网页加载的过程
一个网页的加载过程,很多面试官们都会去询问,毕竟你是前端开发者,如果连这个过程的细节都不知道,那你可能就不是一个合格的前端开发者了。下面是我自己根据自己的理解和相关材料总结的,如有不对的,请指出🙏。
首先,我们需要知道的是当网页访问的之前,肯定有一个契机。这个契机可能是你在浏览器上,输入了网址或者通过 a 标签访问等等。
然后,我们会去浏览器上访问相应的地址,比如访问 https://www.ghost.com 这个地址,当浏览器拿到这个地址之后,会先去本地 HOST 文件中查询有没有相应的 IP,如果有则从 HOST 取。如果没有则向更高层的 DNS 服务中获取。总之,网址存在,就会找到相应的 IP 的。需要注意的是如果 DNS 缓存中存在了这个 IP 就会取缓存中的,缓存中没有,就执行 DNS 查询,查询到 IP 地址。
拿到 IP 之后,客户端和服务端会建立 TCP 连接,然后进行 TCP/IP 三次握手。三次握手流程如下:
- 客户端发送携带
SYN的信息给服务端。 - 服务端接收到客户端的携带的
SYN信息后,要告诉客户端“我接收到了,没问题!”,所以会发送一个携带SYN-ACK的信息给客户端。 - 客户端接受到服务端发送的携带
SYN-ACK的信息之后,也需要告诉服务端"我这边也能接受到你给我的数据了,我们可以正式进行资源互通了",所以会发送一个包含ACK的信息给服务端。
如果网站是 HTTPS 的网站,则要比 HTTP 的网站多一步:TSL 握手。握手流程如下:
- 客户端发送客户端问候消息,包含协议版本、客户端随机数和密码套件列表。
- 服务端接收到客户端随机数以及客户端的参数和密码套件。因此服务端可以创建主密钥。
- 服务器的问候包含服务器证书、数字签名、服务器随机数和公钥给客户端。
- 最后客户端验证签名和证书,通过生成随机数,通过服务端给的公钥进行加密,返回给服务端。
- 服务端接收到加密的数据之后,进行私钥解密,然后结束。
其次,浏览器发送 HTTP 请求拿去网站 HTML 代码。
随后,服务端响应请求,返回相应的 HTML 文件,浏览器拿到 HTML 代码。
再随后,浏览器解析 HTML 代码,获取相应所需要的资源(如js、css 等)
最后,浏览器引擎渲染页面呈现给用户。
🎉 上面就是所有的浏览器访问的流程了。
题目五:缓存
讲到缓存,在前端这边可以分为浏览器缓存和 HTTP 缓存两种。
浏览器缓存下面分 Service Worker Cache、Memory Cache、Disk Cache 和 Push Cache。
HTTP 缓存下面又分强缓存和协商缓存。
下面不会讲的很仔细,但是每个点会大致的过一遍,如果有人想看更细的缓存知识,可以查阅其它文章。
浏览器缓存
Service Worker Cache
Service Worker Cache 的出现是为了处理离线应用。它可以很容易地指定需要离线缓存的资源。浏览器可以在这边进行查看。 Service Worker Cache 有别于其它缓存机制,我们可以自由的控制缓存、文件匹配、读取缓存、并且是持续性的。当没有命中规则的时候才会去请求数据。
可以访问 Service Worker API 官网[1] 进行查阅更多内容。
Memory Cache
Memory Cache 看意思就明白缓存数据存放在浏览器内存中,读取内存的数据是快速和高效的。但是放在内存的数据过多,就会导致浏览器占据计算机内存过多,也会影响计算机运行性能。还有一个缺陷是浏览器或者 Tab 关闭,内存就会被释放。
Disk Cache
Disk Cache 将数据存放在硬盘中,读取速度仅次于 Memory Cache,但是可以长期存储,可以自定义失效时间,覆盖面比较大。Disk Cache 会根据 HTTP Header 来启动缓存策略做缓存。
Push Cache
当以上三种缓存都没有命中时才会使用Push Cache。它只在会话(Session)中存在,一旦会话结束就被释放,并且缓存时间也很短暂。
HTTP 缓存
Http 请求第一次的流程图如下: 由图可知:
- 每次发送 Http 请求都会从浏览器缓存中获取缓存结果和缓存标记,无论有没有。
- 每次拿到数据,都会将请求结果写入到浏览器缓存中,同时也会将缓存标记写入。
强缓存
强缓存就是每次都会去浏览器获取缓存结果,然后根据缓存结果的有效性来决定是否使用缓存结果。
存在以下三种情况:
- 浏览器向浏览器缓存获取缓存结果和缓存标记,发现没有,则直接向服务器获取。
- 浏览器向浏览器缓存获取缓存结果和缓存标记,发现缓存结果和缓存标记已经失效,则直接向服务器获取。
- 浏览器向浏览器缓存获取缓存结果和缓存标记,发现缓存结果和缓存标记存在,则直接使用缓存结果,而不会向服务器请求。
通常问这方面问题的话都会去问请求头中涉及的两个字段 Expires 和 Cache-Control。下面解释一下这两个字段。
Expires
Expires 属于 HTTP/1.0 所定义的字段。然后,它的值表示请求过期时间,如果当前客户端时间比它的小,则使用缓存数据。反之,不使用。当然,在 HTTP/1.1 中,当前字段已经被替换掉了。由于客户端如果和服务端的时间有误差,就会因缓存结果有误出现问题。
Cache-Control
Cache-Control 用于 HTTP/1.1 用于控制网页缓存。当 Expires 和 Cache-Control 同时存在的时候,会使用 Cache-Control。它允许设置的值如下:
public:所有内容都将被缓存(客户端和代理服务器都可缓存)
private:所有内容只有客户端可以缓存,Cache-Control 的默认取值
no-cache:客户端缓存内容,但是是否使用缓存则需要经过协商缓存来验证决定
no-store:所有内容都不会被缓存,即不使用强制缓存,也不使用协商缓存
max-age=xxx (xxx is numeric):缓存内容将在xxx秒后失效
在浏览器中,浏览器会在js和图片等文件解析执行后直接存入内存缓存中,那么当刷新页面时只需直接从内存缓存中读取(from memory cache);而css文件则会存入硬盘文件中,所以每次渲染页面都需要从硬盘读取缓存(from disk cache)。
协商缓存
协商缓存就是强制缓存失效后,浏览器携带缓存标识向服务器发起请求,由服务器根据缓存标识决定是否使用缓存的过程。
协商缓存结果有两种情况。
- 第一种,命中🎯缓存,返回
304状态码 - 第二种,没有命中🎯缓存,从服务器正常请求资源
控制协商缓存的字段有下面几种:
- Last-Modified / If-Modified-Since
- Etag / If-None-Match
备注:
Etag / If-None-Match的优先级比Last-Modified / If-Modified-Since高。
下面解释一下上面两个请求头的含义:
Last-Modified / If-Modified-Since
Last-Modified 表示服务器响应请求时,返回该资源在服务器最后被修改的时间。
If-Modified-Since 表示上一次请求返回的 Last-Modified 的值,通过这个值告诉服务器该资源上次请求返回的最后修改时间。当服务器发现请求中存在 If-Modified-Since,则会将它的值跟现在服务器上的值进行对比,如果发现服务器最后修改时间大于这个值,则返回新的资源,从缓存中获取。反之,则返回 304,可继续从缓存中获取。
Etag / If-None-Match
Etag 表示服务器响应请求时,返回当前资源的唯一标识(服务器自己生成的)。
If-None-Match 表示客户端再给服务器发送请求的时候,这个值指的是上一次请求返回的 Etag。当服务器发现请求中存在 If-None-Match,则会将它的值跟现在服务器上的值进行对比,如果一样则返回 304,可继续从缓存中获取。如果不一样,则返回新的资源。
题目六:HTTP 和 HTTPS
我们无时无刻都在跟网络打关系,所以经常看到网页上要么是 http://www.ghost.com,要么是 https://www.ghost.com。所以,这个 http 和 https 有啥区别呢?它们个自己优缺点又是什么呢?可以阅读下面这一模块的内容。
HTTP 的特点和缺点
特点:无连接、无状态、灵活 和 简单快速
无连接:每次请求都要连接一次,请求结束就会断掉,不会保持连接。无状态:每次请求都是独立的,请求结束不会记录连接的任何信息,减少网络开销。灵活:通过 HTTP 协议中头部Content-Type标记,可以传输任何数据类型的数据对象(文本、图片、视频等),非常的灵活。快速简单:发送请求访问某个资源时,只需传送请求方法和 URL 即可,使用简单,正由于 HTTP 协议简单,是的 HTTP 服务器的程序规模小,因而通信速度很快。
缺点:无状态、不安全、明文传输 和 队头阻塞
无状态:请求不会记录任何连接信息,没有记忆,就无法区分多次请求者是否是一个客户端发出的,意味者后续身份信息都需要重新传递,这样可能导致不必要的信息传递,导致传送数据量增大。不安全:明文传输能被窃听篡改,导致数据不安全。明文传输:报文使用的以明文的方式传递的。队头阻塞:开启长连接时,只建立一个 TCP 连接,同一时刻只能处理一个请求,那么一个请求耗时过长,就会导致其他请求被阻塞到。
HTTPS 的特点和缺点
特点:内容加密、身份认证、数据完整性
内容加密:请求的内容会被加密,中间内容无法查看原始内容。身份认证:保证用户访问正确。如果站点被劫持,也会提醒用户当前站点被劫持,警告用户。数据完整性:数据完整,防止内容被第三方冒充或者篡改
缺点:需要花钱、证书需要绑定 IP、耗费服务器资源会增多 和 耗时
需要花钱:需要花钱买证书,功能越强大的证书,需要花费的费用会越贵。证书需要绑定 IP:不能再同一个 IP 上绑定多个域名。耗费服务器资源会增多:HTTPS 双方加解密,耗费更多的服务器资源。耗时:中间增加了 TLS/SSL握手,一定程度上降低可用户访问的速度。
备注:整体上 HTTPS 不是绝对的安全,但是这是相对来说最安全的解决方案了,大大减少了被攻击的几率。
HTTP 和 HTTPS 有什么不同?
- HTTP 是
明文传输的,不安全,HTTPS 是加密传输的,安全的多。 - HTTP 标准端口是
80,HTTPS 标准端口是443。 - HTTP 不用认证证书
免费,HTTPS 需要购买证书进行认证。 - 连接方式不同, HTTP
只需要进行三次握手,但是 HTTPS需要进行更多。 - HTTP 在 OSI 网络模型中是
应用层,而 HTTPS 的 TLS 是在传输层。 - HTTP 是
无状态的,而 HTTPS 是有状态的。
题目七:跨域
在学习跨域之前,我们需要了解为什么会出现跨域?什么是跨域?然后,下面也会告诉大家如何解决跨域问题。
为什么会出现跨域?什么是跨域?
跨域是浏览器为了保护网站资源的一种手段。浏览器规定的同源策略的限制如下,不满足同源策略都会被限制。
- 不同站点之间不能互相访问彼此的 Cookie、localStorage、IndexedDB 等存储性内容
- 不同站点之间不能操作彼此的 DOM 节点。
- 不同站点之间彼此发送 AJAX 请求会被浏览器拦截。
但是有以下几个标签,不会受同源策略的限制
<img src=xxx /><link href=xxx /><script src=xxx>
什么是跨域?就是当两个或多个站点的 协议、主机 和 端口号 不一样时,它们彼此访问,就会出现跨域。
怎么解决跨域问题?
1. JSONP
利用 script 标签没有跨域限制的特点,网页可以得到从其他来源动态产生的 JSON 数据。JSONP 请求一定需要服务端做支持才可以。实现的核心是 回调函数。
前端实现代码:
// index.html
function jsonp({ url, params, callback }) {
return new Promise((resolve, reject) => {
let script = document.createElement('script')
window[callback] = function(data) {
resolve(data)
document.body.removeChild(script)
}
params = { ...params, callback } // wd=b&callback=show
let arrs = []
for (let key in params) {
arrs.push(`${key}=${params[key]}`)
}
script.src = `${url}?${arrs.join('&')}`
document.body.appendChild(script)
})
}
jsonp({
url: 'http://localhost:3000/say',
params: { wd: 'Iloveyou' },
callback: 'show'
}).then(data => {
console.log(data)
})
后端实现代码:
// server.js
let express = require('express')
let app = express()
app.get('/say', function(req, res) {
let { wd, callback } = req.query
console.log(wd) // Iloveyou
console.log(callback) // show
res.end(`${callback}('T hate you, javascript')`)
})
app.listen(3000)
2. CORS
CORS 需要浏览器和后端同时支持。实现 CORS 的关键是在后端。服务端需要设置 Access-Control-Allow-Origin 就可以开启 CORS 了。如果设置了只有哪些站点可以访问,那么只有这些站点访问当前接口不会出现跨域,其他的站点都会出现。如果设置为 Access-Control-Allow-Origin: *,则所有的站点访问当前接口都不会出现跨域。
这里只大体列一下核心,想要了解更多可自行到网上去查找。毕竟一个知识内容实在挺多的😢。或者关注我,等我后期梳理完这次要梳理的知识再一一做讲解😄。
3. postmessage
postmessage 方法允许来自不同同源脚本采用异步方式进行有限的通信,可以实现跨文本、多窗口、跨域消息传递。
语法: otherWindow.postMessage(message, targetOrigin, [transfer]);
message:将要发送到其他 window的数据。targetOrigin:通过窗口的 origin 属性来指定哪些窗口能接收到消息事件,其值可以是字符串"*"(表示无限制)或者一个 URI。transfer:是一串和 message 同时传递的Transferable对象。
代码示例
前端实现代码:
/*
* A 窗口的域名是<http://example.com:8080>,以下是 A 窗口的 script 标签下的代码:
*/
var popup = window.open(...popup details...);
// 如果弹出框没有被阻止且加载完成
// 这行语句没有发送信息出去,即使假设当前页面没有改变 location(因为 targetOrigin 设置不对)
popup.postMessage("The user is 'bob' and the password is 'secret'",
"https://secure.example.net");
// 假设当前页面没有改变 location,这条语句会成功添加 message 到发送队列中去(targetOrigin 设置对了)
popup.postMessage("hello there!", "http://example.org");
function receiveMessage(event)
{
// 我们能相信信息的发送者吗?(也许这个发送者和我们最初打开的不是同一个页面).
if (event.origin !== "http://example.org")
return;
// event.source 是我们通过 window.open 打开的弹出页面 popup
// event.data 是 popup 发送给当前页面的消息 "hi there yourself! the secret response is: rheeeeet!"
}
window.addEventListener("message", receiveMessage, false);
/*
* 弹出页 popup 域名是<http://example.org>,以下是 script 标签中的代码:
*/
//当 A 页面 postMessage 被调用后,这个 function 被 addEventListener 调用
function receiveMessage(event)
{
// 我们能信任信息来源吗?
if (event.origin !== "http://example.com:8080")
return;
// event.source 就当前弹出页的来源页面
// event.data 是 "hello there!"
// 假设你已经验证了所受到信息的 origin (任何时候你都应该这样做), 一个很方便的方式就是把 event.source
// 作为回信的对象,并且把 event.origin 作为 targetOrigin
event.source.postMessage("hi there yourself! the secret response " +
"is: rheeeeet!",
event.origin);
}
window.addEventListener("message", receiveMessage, false);
更多内容请查阅 MDN postmessage 官网文档[2]。
4. Websocket
Websocket 是 HTML5 的一个持久化的协议,它实现了浏览器与服务器的全双工通信,同时也是跨域的一种解决方案。WebSocket 和 HTTP 都是应用层协议,都基于 TCP 协议。但是 WebSocket 是一种双向通信协议,在建立连接之后,WebSocket 的 server 与 client 都能主动向对方发送或接收数据。同时,WebSocket 在建立连接时需要借助 HTTP 协议,连接建立好了之后 client 与 server 之间的双向通信就与 HTTP 无关了。
代码示例
前端实现代码:
// socket.html
<script>
let socket = new WebSocket('ws://localhost:3000');
socket.onopen = function () {
socket.send('Hello,javascript');//向服务器发送数据
}
socket.onmessage = function (e) {
console.log(e.data);//接收服务器返回的数据
}
</script>
服务端实现代码:
// server.js
let express = require('express');
let app = express();
let WebSocket = require('ws');
let wss = new WebSocket.Server({ port: 3000 });
wss.on('connection',function(ws) {
ws.on('message', function (data) {
console.log(data);
ws.send('T hate you, javascript')
});
})
5. nginx 反向代理
通过 nginx 配置一个代理服务器(域名与客户端相同,端口不同)做跳板机,反向代理访问服务端接口,并且可以顺便修改 cookie 中 domain 信息,方便当前域 cookie 写入,实现跨域登录。
nginx 配置如下:
// proxy服务器
server {
listen 81;
server_name www.ghost.com;
location / {
proxy_pass http://www.xxxx.com:8080; #反向代理
proxy_cookie_domain www.xxxx.com www.ghost.com; #修改 cookie 里域名
index index.html index.htm;
# 当用 webpack-dev-server 等中间件代理接口访问 nignx 时,此时无浏览器参与,故没有同源限制,下面的跨域配置可不启用
add_header Access-Control-Allow-Origin http://www.ghost.com; #当前端只跨域不带 cookie 时,可为 *
add_header Access-Control-Allow-Credentials true;
}
}
6. window.name + iframe
通过 iframe 的 src 属性由外域转向本地域,跨域数据即由 iframe 的 window.name 从外域传递到本地域。这个就巧妙地绕过了浏览器的跨域访问限制,但同时它又是安全操作。
代码示例: a.html(b.html 和 a.html 同源)
// a.html(http://localhost:3000/b.html)
<iframe src="http://localhost:4000/c.html" frameborder="0" onload="load()" id="iframe"></iframe>
<script>
let first = true
// onload事件会触发2次,第1次加载跨域页,并留存数据于 window.name
function load() {
if(first){
// 第1次 onload (跨域页)成功后,切换到同域代理页面
let iframe = document.getElementById('iframe');
iframe.src = 'http://localhost:3000/b.html';
first = false;
} else {
// 第2次onload(同域 b.html 页)成功后,读取同域 window.name中数据
console.log(iframe.contentWindow.name);
}
}
</script>
c.html (c.html 和 a.html 不同源)
// c.html(http://localhost:4000/c.html)
<script>
window.name = 'T hate you, javascript'
</script>
题目八:如何处理大文件传输
- 开启 gzip 进行数据压缩。请求头示例:
content-encoding: gzip。 - 通过分块传输编码,设置
Transfer-Encoding: chunked和Transfer-Encoding: gzip, chunked。 - 流式传输(需要了解的可关注后期仔细讲解)。
题目九:Cookie、sessionStorage 和 localstorage
共同点:
- 都保存在浏览器端,且是同源的。
不同点:
- cookie 数据始终在同源的 http 请求中携带,而 sessionStorage 和 localstorage 不会再请求中携带,仅仅在本地存储
- 存储大小区别,cookie 是4k,sessionStorage 和 localstorage 可以达到5M甚至更大。
- 数据有效时间区别: sessionStorage 仅仅是会话级别的存储,它只在当前浏览器关闭前有效,不能持久保持;localStorage 始终有效,即使窗口或浏览器关闭也一直有效,除非用户手动删除,其才会失效;cookie 只在设置的 cookie 过期时间之前一直有效。
- 作用域区别:sessionStorage 不在不同的浏览器窗口中共享,即使是同一个页面; localStorage 和 cookie 在所有同源窗口是共享的。
- sessionStorage 和 localstorage 支持事件通知机制,可以将数据更新的通知发送给监听者。sessionStorage 和 localstorage 的 api 接口使用更方便。
题目十:如何解决 HTTP 的队头阻塞问题
什么是队头阻塞
引用知乎 Xiaojian Hong[3] 的话:当单个(慢)对象阻止其他/后续的对象前进时。
更简单的讲解HTTP灵魂之问,巩固你的 HTTP 知识体系 :从前面的小节可以知道,HTTP 传输是基于请求-应答的模式进行的,报文必须是一发一收,但值得注意的是,里面的任务被放在一个任务队列中串行执行,一旦队首的请求处理太慢,就会阻塞后面请求的处理。这就是著名的HTTP队头阻塞问题。
解决方案
并发连接
对于一个域名允许分配多个长连接,那么相当于增加了任务队列,不至于一个队伍的任务阻塞其它所有任务。在 RFC2616 规定过客户端最多并发 2 个连接,不过事实上在现在的浏览器标准中,这个上限要多很多,Chrome 中是 6 个。
但其实,即使是提高了并发连接,还是不能满足人们对性能的需求。
域名分片
一个域名不是可以并发 6 个长连接吗?那我就多分几个域名。 比如 a.ghost.com 、b.ghost.com。
这样一个 ghost.com 域名下可以分出非常多的二级域名,而它们都指向同样的一台服务器,能够并发的长连接数更多了,事实上也更好地解决了队头阻塞的问题。
细节上请参考:关于队头阻塞(Head-of-Line blocking),看这一篇就足够了[4]
参考资料
[1]
Service Worker API 官网: developer.mozilla.org/zh-CN/docs/…
[2]
MDN postmessage 官网文档: developer.mozilla.org/zh-CN/docs/…
[3]
Xiaojian Hong: zhuanlan.zhihu.com/p/330300133
[4]
关于队头阻塞(Head-of-Line blocking),看这一篇就足够了: zhuanlan.zhihu.com/p/330300133