长连接请求的执行顺序问题。HTTP1.1建立的是长连接请求,Connect:keepalive,请求建立之后不会立即中断,后续的其他请求还可以复用之前建立好的连接,但是长连接请求的顺序是串行执行的,也就是所,下一个请求的发出需要等待上一个请求的响应完成,如下图所示。
1. 实验验证
HTTP1.1默认开启长连接,为了验证这一特性,使浏览器按照顺序依次发送10个请求,后端采用express搭建服务,最终的返回结果应该和发送循序一致。
// 浏览器代码
function send(i) {
const config = {
method: 'get',
headers: {
"Content-type": "application/x-www-form-urlencoded",
"Connection":"keep-alive"
},
}
fetch('http://localhost:1234/', config).then((data) => {
console.log('Request succeeded with JSON response', i);
}).catch(() => {
console.log("no");
})
}
function startSend() {
for (let i = 0; i < 10; i++) {
setTimeout(() => {
send(i);
}, i * 100)
}
}
// 服务端代码
app.get('/', function (req, res) {
console.log("---------get---------")
// console.log(req.fields);
// console.log(req.files.theFile);
res.header("Access-Control-Allow-Origin", "*");
res.header("Access-Control-Allow-Methods", "*");
// res.header("Access-Control-Allow-Headers","X-Requestd-With");
res.header("Access-Control-Allow-Headers", "content-type, accept");
new Promise((resolve, reject) => {
setTimeout(() => {
resolve(1);
}, Math.random()*1000+1000);
}).then(() => {
console.log("123")
res.send({status: 200});
})
})
但是出现了一个奇怪的问题,在谷歌浏览器中,返回的循序和发送的顺序一致,服务器串行执行,但是在火狐浏览器中,返回的顺序和执行时间一致,服务并发执行。
谷歌浏览器:
服务器打印顺序:
---------接受请求---------
请求完毕
---------接受请求---------
请求完毕
---------接受请求---------
请求完毕
---------接受请求---------
请求完毕
---------接受请求---------
请求完毕
---------接受请求---------
请求完毕
---------接受请求---------
请求完毕
---------接受请求---------
请求完毕
---------接受请求---------
请求完毕
---------接受请求---------
请求完毕
火狐浏览器:
// 控制台打印结果
Request succeeded with JSON response 1
Request succeeded with JSON response 4
Request succeeded with JSON response 2
Request succeeded with JSON response 0
Request succeeded with JSON response 5
Request succeeded with JSON response 3
Request succeeded with JSON response 6
Request succeeded with JSON response 9
Request succeeded with JSON response 8
Request succeeded with JSON response 7
服务器打结果:
接受请求
---------接受请求---------
---------接受请求---------
---------接受请求---------
---------接受请求---------
---------接受请求---------
---------接受请求---------
请求完毕
---------接受请求---------
请求完毕
---------接受请求---------
请求完毕
---------接受请求---------
请求完毕
---------接受请求---------
请求完毕
请求完毕
请求完毕
请求完毕
请求完毕
请求完毕
2. 结果分析
- 从结果会发现,谷歌浏览器串行执行,火狐浏览器并行执行。谷歌浏览器第一个请求处理结束之后,才继续处理第二个。但是第火狐浏览器则是并发执行,所有请求同时调用后端接口,最先处理完,最先返回。并且在通过控制台发现会有一个 “已竞速” 的标识。
The Race Cache With Network (RCWN) feature in Necko adds the ability to race the cache with the network when the cache is slow. So if reading from the disk is slow, we will send a network request, and return the channel from the network, even though we have the entry in the cache. This way we provide the content to consumers faster.
There are four possible values for the Transferred column:
13.2 KB- a number saying how much data were transferred over the wire (perhaps compressed)cached- a labels saying that the request came from the cache13.2 KB (raced)- request is racing. The response was fetched from the server even if it's available in the cache. It was faster to get it from the server.cached (raced)- request is racing. The response was fetched from the cache, but a request has been also sent to the server. The cache was faster in this case.
- 观察火狐浏览器的响应头,发现问题所在。再后端设置
res.header("Connection", "Close")情况下,火狐浏览器自动添加了响应头,connection:upgrade,谷歌浏览器没有connection响应头。所以以猜想谷歌浏览器的串行调用是由于服务端没有开启长连接导致的。
火狐浏览器:
谷歌浏览器:
- 验证猜想,服务端设置
res.header("Connection", "keep-alive"),观察谷歌浏览器的反应。猜想正确谷歌浏览器并发请求。
- 那么
connection:upgrade表示什么意思呢?