1、页面卡顿,需要好长一段时间才能加载完成,可能是资源请求过多,再加上请求响应慢的原因。
每个浏览器都有资源请求并发数的限制,如何查看请求阻塞情况 到前端如何针对该限制进行优化?
先看问题:
1、设置服务端请求耗时(3S)客户端并发调用20个请求。(预留问题-见下方:20个请求建立多少个 TCP 连接?)
问:3秒左右所有请求都能获取到结果吗?
答:不能
有没有发现前六个请求基本在 3S 左右就处理完了,从第7个响应就开始越来越长。明明服务端设置的 3秒 返回,为什么会耗时这么久呢?这里就涉及到浏览器并发的问题了。
每个浏览器对应的并发数(参考值):从上述的请求截图中也可以看出来并发数量。
选择第七个请求分析一下为什么是 5S,点击查看详情 👇
参数具体含义:
- Queueing:
请求文件顺序的排序;
浏览器是有线程限制的,发请求也不能所有的请求同时发送,会将请求加入队列中(Chrome的最大并发连接数是6)。
此参数表示从添加到待处理队列,到实际开始处理的时间间隔标示。
- Stalled(阻塞):
浏览器得到要发出这个请求的指令,到请求可以发出的等待时间,一般是代理协商、以及等待可复用的TCP连接释放的时间,不包括DNS查询、建立TCP连接等时间等。
浏览器对同一个主机域名的并发连接数有限制,因此如果当前的连接数已经超过上限,那么其余请求就会被阻塞,等待新的可用连接;此外脚本也会阻塞其他组件下载;
- DNS Lookup:
请求某域名下的资源,浏览器需要先通过DNS解析器得到该域名服务器的IP地址。
在DNS查找完成之前,浏览器不能从主机名那里下载到任何东西。
DNS查询的时间,当本地DNS缓存没有的时候,这个时间可能是有一段长度的,但是比如你一旦在host中设置了DNS;
或者第二次访问,由于浏览器的DNS缓存还在,这个时间就为0了。
- Initial connection:
建立TCP连接的时间,就相当于客户端从发请求开始到TCP握手结束这一段,包括DNS查询+Proxy时间+TCP握手时间。
- SSL(包含于HTTPS连接中):
http是超文本传输协议,以明文方式发送内容,不提供任何方式的数据加密,如果被不法分子截取浏览器和服务器之间的传输报文,会获取其中的信息。
https 是安全套接字层超文本传输协议,就是在HTTP的基础上加入了SSL协议,SSL依靠证书来验证服务器的身份,并为浏览器和服务器之间的通信加密。
因此建立HTTPS连接的时间相当于三次握手的时间+SSL时间。
- Request sent(发送请求):
发送HTTP请求的时间(从第一个字节发出前到最后一个字节发出后的时间)
- Waiting(TTFB) :
请求发出后,到收到响应的第一个字节所花费的时间(Time To First Byte),发送请求完毕到接收请求开始的时间;通常是耗费时间最长的。
从发送请求到收到服务器响应的第一字节之间的时间,受到线路、服务器距离等因素的影响。
注意:网页重定向越多,TTFB越高,所以要减少重定向。
- Content Download(下载):
收到响应的第一个字节,到接受完最后一个字节的时间,就是下载时间。
如何解决浏览器并发问题?
1、将资源分散到不同的域名下(合理分配资源)
浏览器并发连接数限制只是针对同一域名,将资源分散到不同的域名下,即可实现并发数的扩展,例如一个域名是10个,两个域名就是20个并发了。
注意:资源地址的端口号不同也会被视为不同域名
2、浏览器端合理使用缓存
预留问题:
1、20个请求建立多少个 TCP 连接?
HTTP/1.0中,每个请求都需要建立一个TCP连接。
在HTTP/1.0中,默认情况下,每次HTTP请求都会建立一个新的TCP连接,并且在请求完成后,这个连接会被关闭。这种方式被称为“非持续连接”(non-persistent connection)。这种做法在发送完一个HTTP响应后,服务器会断开TCP连接,每次请求都需要重新建立和断开连接,这被称为短连接。这种做法的代价较大,因为每次连接都需要经过TCP的三次握手过程,这不仅增加了网络开销,还降低了效率。为了解决这个问题,HTTP/1.1引入了“持续连接”(persistent connection)的概念,允许在同一个TCP连接上发送多个HTTP请求和响应,从而减少了连接建立和关闭的开销,提高了效率12。
HTTP/1.1通过默认开启持久连接,即keep-alive连接,显著减少了TCP连接的建立和断开次数,从而提高了性能。在这种模式下,只要发送端和接收端都没有提出断开连接,TCP连接就可以被复用,用于发送多个HTTP请求和响应。这种模式的优点在于减少了TCP连接的重复建立和断开所造成的额外开销,同时也减轻了服务器端的负担,使得HTTP请求和响应能够更早地结束,提高了web页面的显示速度12。
总的来说,HTTP/1.0中每个请求都需要一个新的TCP连接,而HTTP/1.1通过引入持久连接的概念,允许在同一个TCP连接上发送多个HTTP请求,从而提高了效率和性能。
如上图所示:采用的 http/1.1 观察 Connection Id 同一个 TCP 连接处理了多个请求,减少了TCP的创建开销。
HTTP/2:多路复用代替原来的序列和阻塞机制,所有就是请求的都是通过一个 TCP 连接并发完成。同时也很好的解决了浏览器限制同一个域名下的请求数量的问题。
在 HTTP/2 中,有了二进制分帧之后,HTTP/2 不再依赖 TCP 链接去实现多流并行了,在 HTTP/2 中:
同域名下所有通信都在单个连接上完成,同个域名只需要占用一个 TCP 连接,使用一个连接并行发送多个请求和响应。
单个连接可以承载任意数量的双向数据流,单个连接上可以并行交错的请求和响应,之间互不干扰。
数据流以消息的形式发送,而消息又由一个或多个帧组成,多个帧之间可以乱序发送,因为根据帧首部的流标识可以重新组装。
每个请求都可以带一个 31bit 的优先值,0 表示最高优先级, 数值越大优先级越低。
在 HTTP/2 中,有两个非常重要的概念:帧(frame)和流(stream)。
帧(frame):
HTTP/2 中数据传输的最小单位,因此帧不仅要细分表达 HTTP/1.x 中的各个部份,也优化了 HTTP/1.x 表达得不好的地方,同时还增加了 HTTP/1.x 表达不了的方式。 每一帧都包含几个字段,有length、type、flags、stream identifier、frame playload等,其中type 代表帧的类型,在 HTTP/2 的标准中定义了 10 种不同的类型,包括上面所说的 HEADERS frame 和 DATA frame。此外还有: PRIORITY(设置流的优先级) RST_STREAM(终止流) SETTINGS(设置此连接的参数) PUSH_PROMISE(服务器推送) PING(测量 RTT) GOAWAY(终止连接) WINDOW_UPDATE(流量控制) CONTINUATION(继续传输头部数据)
在 HTTP 2.0 中,它把数据报的两大部分分成了 header frame 和 data frame。也就是头部帧和数据体帧。
流(stream):
流: 存在于连接中的一个虚拟通道。流可以承载双向消息,每个流都有一个唯一的整数 ID。 HTTP/2 长连接中的数据包是不按请求-响应顺序发送的,一个完整的请求或响应(称一个数据流 stream,每个数据流都有一个独一无二的编号)可能会分成非连续多次发送。它具有如下几个特点:
双向性:同一个流内,可同时发送和接受数据。
- 有序性:流中被传输的数据就是二进制帧 。帧在流上的被发送与被接收都是按照顺序进行的。
- 并行性:流中的 二进制帧 都是被并行传输的,无需按顺序等待。
- 流的创建:流可以被客户端或服务器单方面建立, 使用或共享。
- 流的关闭:流也可以被任意一方关闭。
- HEADERS 帧在 DATA 帧前面。
- 流的 ID 都是奇数,说明是由客户端发起的,这是标准规定的,那么服务端发起的就是偶数了。
发展历史:
从 Http/0.9 到 Http/2 要发送多个请求,从多个 Tcp 连接=>keep-alive=>管道化=>多路复用不断的减少多次创建 Tcp 等等带来的性能损耗。
多个 Tcp 连接:在最早的时候没有keep-alive只能创建多个Tcp连接来做多次请求。
一次请求完成就会关闭本次的 Tcp 连接,下个请求又要从新建立 Tcp 连接传输完成数据再关闭,造成很大的性能损耗。
Keep-Alive:
Keep-Alive解决的核心问题是: 一定时间内,同一域名多次请求数据,只建立一次 HTTP 请求,其他请求可复用每一次建立的连接通道,以达到提高请求效率的问题。这里面所说的一定时间是可以配置的,不管你用的是Apache还是nginx。 以往,浏览器判断响应数据是否接收完毕,是看连接是否关闭。在使用持久连接后,就不能这样了,这就要求服务器对持久连接的响应头部一定要返回content-length标识body的长度,供浏览器判断界限。有时,content-length的方法并不是太准确,也可以使用 Transfer-Encoding: chunked 头部发送一串一串的数据,最后由长度为 0 的chunked标识结束。 多次 http 请求效果如下图所示:
上图:设置 Connection:Keep-Alive,保持连接在一段时间内不断开。
Keep-Alive还是存在如下问题:
串行的文件传输。
同域并行请求限制带来的阻塞(~ 6)个
管线化:
HTTP 管线化可以克服同域并行请求限制带来的阻塞,它是建立在持久连接之上,是把所有请求一并发给服务器,但是服务器需要按照顺序一个一个响应;
而不是等到一个响应回来才能发下一个请求,这样就节省了很多请求到服务器的时间。
不过,HTTP 管线化仍旧有阻塞的问题,若上一响应迟迟不回,后面的响应都会被阻塞到。
上图:HTTPpipelining:建立多个连接
多路复用:
多路复用代替原来的序列和阻塞机制。所有就是请求的都是通过一个 TCP 连接并发完成。
因为在多路复用之前所有的传输是基于基础文本的,在多路复用中是基于二进制数据帧的传输、消息、流,所以可以做到乱序的传输。
多路复用对同一域名下所有请求都是基于流,所以不存在同域并行的阻塞。
多次请求如下图:
上图:多路复用
总结:
在 HTTP/2 中,有两个非常重要的概念,分别是帧(frame)和流(stream)。
帧代表着最小的数据单位,每个帧会标识出该帧属于哪个流,流也就是多个帧组成的数据流。
HTTP2 采用二进制数据帧传输,取代了 HTTP1.x 的文本格式,二进制格式解析更高效。
多路复用代替了 HTTP1.x 的序列和阻塞机制,所有的相同域名请求都通过同一个 TCP 连接并发完成。
同一 Tcp 中可以发送多个请求,对端可以通过帧中的标识知道属于哪个请求。
通过这个技术,可以避免 HTTP 旧版本中的队头阻塞问题,极大的提高传输性能。