企业🤖AI智能体构建引擎,智能编排和调试,一键部署,支持私有化部署方案 广告
[TOC] # 持久连接 HTTP请求都要经过TCP三次握手建立连接,四次分手断开连,如果每个HTTP请求都要建立TCP连接的话是极其费时的。 <br> 请求头中的 `Connection: keep-alive` 的作用可以在请求完成后,保持TCP连接一段时间而不关闭,如果这个期间又有HTTP请求的话,直接使用这个TCP连接,省去了建立新的连接的时间。 <br> HTTP/1.1中浏览器默认开启了`Connection: keep-alive`。服务端可以设置 `Connection: close` 来关闭持久连接。 <br> 通过设置 `timeout `,一定时间后在此 TCP 连接没有请求就会关闭。 <br> 同时服务端还会设置一个参数叫最大请求数,比如当最大请求数是300时,只要请求次数超过300次,即使还没到超时时间,服务端也会主动关闭连接。 <br> 不同 ID 代表不同的 TCP 连接。不同域名需要分别创建 TCP连接。 TCP 请求的连接有先后顺序,无法并发执行。浏览器允许并发创建 TCP,如Chrome 允许最多6个并发。 ![](https://box.kancloud.cn/e289ffdfc051e6e77e3410f85745a6ec_1899x456.png) <br> ## 好处 大大减少了连接的建立以及关闭时延。 ![](https://box.kancloud.cn/b9e19e46ce0d352becf0f2f90e44c771_912x304.png) 如图:多个请求响应在一条连接内完成了。但是,这里也看出一个“缺点”,请求响应是顺序执行的。只有在请求1的响应收到之后,才会发送请求2,这就是持久连接与管道化连接不同的地方。 ## 信道复用 在HTTP2里面有个**信道复用**的概念,在TCP/IP连接上面,可以并发的发送HTTP请求。在我们连接网站的时候,只需要一个TCP连接。 如果不同域,则会有多个TCP连接。 ![](https://box.kancloud.cn/9435da4b4075b664643a8956445397fc_1909x486.png) # 例子 ## 浏览器 ### 持久连接 body.js ~~~ <body> <img src="./test1.jpg" alt=""> <img src="./test2.jpg" alt=""> <img src="./test3.jpg" alt=""> <img src="./test4.jpg" alt=""> <img src="./test5.jpg" alt=""> <img src="./test6.jpg" alt=""> <img src="./test7.jpg" alt=""> </body> ~~~ <br> server.js ~~~ const http = require('http') const fs = require('fs') http.createServer(function (req, res) { console.log(`requeset come ${req.url}`) const html = fs.readFileSync('index.html', 'utf8') const img = fs.readFileSync('test.jpg') if (req.url === '/') { res.writeHead(200, { 'Content-type': 'text/html', // 'connection': 'close' // 非持久连接 }) res.end(html) } else { res.writeHead(200, { 'Content-type': 'image/jpg', // 'connection': 'close' // 非持久连接 }) res.end(img) } }).listen(8888) console.log('server listen on 8888') ~~~ ![](https://box.kancloud.cn/431e6e6c03c2520bff6267d971cd2dda_1903x514.png) 分析: * 第一张图片 test1.jpg需要等 html 解析完才能加载,因此使用同一个 TCP 连接 * test1.jpg 到 test6.jpg 为并发请求,从Waterflow可以看出。connection ID不同。 * Chrome 只能并发创建6个 TCP 连接,因此 test7.jpg 只能等任意一个连接完成后才能加载 ### 非持久连接 反注释上一节代码的 `'connection': 'close'` ![](https://box.kancloud.cn/1c49308aee785b84f37610c9fe1de878_1899x483.png) <br> 分析: * 所有请求的connection ID 都不同 ## Node ### 串行请求(不使用keep-alive) ~~~ let request = require('request'); ;(async function fn() { for (let i = 0; i < 10; i ++) { await new Promise((resolve, reject) => { request({ method: 'GET', uri: 'http://localhost:8887/a', time: true, // 配置这个属性可以看到时间信息 // forever: true }, (error, response, body) => { console.log('timingPhases', response.timingPhases); resolve(); }); }); } return 'success'; })() ~~~ ![](https://box.kancloud.cn/e01648dbd5edecd373160f4ba6e81b95_495x670.png) ### 串行请求(使用keep-alive) ~~~ let request = require('request'); ;(async function fn() { for (let i = 0; i < 10; i ++) { await new Promise((resolve, reject) => { request({ method: 'GET', uri: 'http://localhost:8887/a', time: true, // 配置这个属性可以看到时间信息 forever: true }, (error, response, body) => { console.log('timingPhases', response.timingPhases); resolve(); }); }); } return 'success'; })() ~~~ ![](https://box.kancloud.cn/fc0b959bc298894cce5b44e11c19aa77_551x749.png) ### 并行请求 HTTP/1.1并行请求的时候会建立多个TCP连接,在浏览器中针对同一域名只可以同时建立6个连接,Node中没有这个限制 ~~~ let request = require('request'); ;(async function fn() { let promiseArr = []; for (let i = 0; i < 10; i ++) { let newP = new Promise((resolve, reject) => { request({ method: 'GET', uri: 'http://localhost:8887/a', time: true, forever: true }, (error, response, body) => { console.log('timingPhases', response.timingPhases); resolve(); }); }); promiseArr.push(newP); } Promise.all(promiseArr) })() ~~~ ![](https://box.kancloud.cn/ccb867bf0092b96166fb37d3b703703d_490x828.png) ![](https://box.kancloud.cn/b9987872cce7e3f684cfc0bad8e5cd6e_783x285.png) <br> <br> # 参考资料 [HTTP总结(六):长连接](https://zhuanlan.zhihu.com/p/57142432) [长连接及在Node中的应用——HTTP/1.1 keep-alive](https://juejin.im/post/5c7b1be9f265da2dac4568a2)