HTTP 1.0/1.1/2.0 在并发请求上主要区别是什么?
HTTP/1.0 每次 TCP 连接只能发送一个请求,当服务器响应后就会关闭这次连接,下一个请求需要再次建立 TCP 连接.
HTTP/1.1 默认采用持续连接(TCP 连接默认不关闭,可以被多个请求复用,不用声明 Connection: keep-alive). 增加了管道机制,在同一个 TCP 连接里,允许多个请求同时发送,增加了并发性,进一步改善了 HTTP 协议的效率, 但是同一个 TCP 连接里,所有的数据通信是按次序进行的。回应慢,会有许多请求排队,造成”队头堵塞”。
HTTP/2.0
加了双工模式,即不仅客户端能够同时发送多个请求,服务端也能同时处理多个请求,解决了队头堵塞的问题。 使用了多路复用的技术,做到同一个连接并发处理多个请求,而且并发请求的数量比 HTTP1.1 大了好几个数量级。 增加服务器推送的功能,不经请求服务端主动向客户端发送数据。
HTTP/1.1 长连接和 HTTP/2.0 多路复用的区别?
HTTP/1.1:同一时间一个 TCP 连接只能处理一个请求, 采用一问一答的形式, 上一个请求响应后才能处理下一个请求. 由于浏览器最大 TCP 连接数的限制, 所以有了最大并发请求数的限制. HTTP/2.0:同域名下所有通信都在单个连接上完成,消除了因多个 TCP 连接而带来的延时和内存消耗。单个连接上可以并行交错的请求和响应,之间互不干扰
那为什么 HTTP/1.1 不能实现多路复用?
HTTP/2 是基于二进制“帧”的协议,HTTP/1.1 是基于“文本分割”解析的协议。
HTTP1.1 的报文结构中, 服务器需要不断的读入字节,直到遇到换行符, 或者说一个空白行. 处理顺序是串行的, 一个请求和一个响应需要通过一问一答的形式才能对应起来.
1GET / HTTP/1.12Accept:text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.83Accept-Encoding:gzip, deflate, br4Accept-Language:zh-CN,zh;q=0.9,en;q=0.85Cache-Control:max-age=06Connection:keep-alive7Host:www.imooc.com8Referer:https://www.baidu.com/
HTTP2.0 中,有两个非常重要的概念,分别是帧(frame)和流(stream)。 帧代表着最小的数据单位,每个帧会标识出该帧属于哪个流,流也就是多个帧组成的数据流。 多路复用,就是在一个 TCP 连接中可以存在多条流。换句话说,也就是可以发送多个请求,对端可以通过帧中的标识知道属于哪个请求。通过这个技术,可以避免 HTTP 旧版本中的队头阻塞问题,极大的提高传输性能。
控制并发量
1const urls = [2 {3 info: "link1",4 time: 3000,5 },6 {7 info: "link2",8 time: 2000,9 },10 {11 info: "link3",12 time: 5000,13 },14 {15 info: "link4",16 time: 1000,17 },18 {19 info: "link5",20 time: 1200,21 },22 {23 info: "link6",24 time: 2000,25 },26 {27 info: "link7",28 time: 800,29 },30 {31 info: "link8",32 time: 3000,33 },34];3536// 设置我们要执行的任务37function loadImg(url) {38 return new Promise((resolve, reject) => {39 console.log("----" + url.info + " start!");40 setTimeout(() => {41 console.log(url.info + " OK!!!");42 resolve();43 }, url.time);44 });45}4647module.exports = {48 urls,49 loadImg,50};
递归法
1//index.js2const { urls, loadImg } = require("./mock");34function promiseLimit(arr, maxCount) {5 let current = 0;6 let pendingList = [];7 for (let i = 0; i < arr.length; i++) {8 doSend(arr[i]);9 }1011 function doSend(item) {12 if (current < maxCount) {13 current++;14 loadImg(item).then(() => {15 current--;16 if (pendingList.length > 0) {17 doSend(pendingList.shift());18 }19 });20 } else {21 pendingList.push(item);22 }23 }24}25promiseLimit(urls, 3);
类
1const { urls, loadImg } = require("./mock");23class PromiseLimit {4 constructor(props) {5 const { concurrency } = props;6 this.concurrency = concurrency || 1;7 this.pendingList = [];8 this.limitCount = 0;9 }1011 add(task) {12 this.pendingList.push(task);13 this.run();14 }1516 run() {17 if (this.pendingList.length === 0 || this.limitCount === this.concurrency) {18 return;19 }2021 this.limitCount++;2223 const fn = this.pendingList.shift();24 const promise = fn();25 promise.then(this.complete.bind(this)).catch(this.complete.bind(this));26 }2728 complete() {29 this.limitCount--;30 this.run();31 }32}3334const Limit = new PromiseLimit({ concurrency: 3 });3536for (let i = 0; i < urls.length; i++) {37 Limit.add(() => loadImg(urls[i]));38}