理解Chrome请求流程
Timing Tab
Chrome的DevTools中提供了对请求的时间分析,如配图所示。
对每个请求,Chrome统计了它们不同阶段的通信时间,包括:
- Queueing
 - Stalled
 - DNS Lookup
 - Proxy negotiation
 - Initial Connection /Connecting
 - SSL
 - Request sent
 - ServiceWorker Preparation
 - Request to ServiceWorker
 - Waiting(TTFB)
 - Content Download
 - Receiving Push
 - Reading Push
 
Queueing
Queueing意味着请求没有马上发生,加入队列排队等候。导致这种情况一般可能的原因有:
- 有更高优先级的请求
 - 请求等待即将被释放的TCP socket
 - 与当前域名的TCP连接数已满上限6个(仅对HTTP/1.0和HTTP/1.1生效)
 - 浏览器正在分配磁盘缓存(一般非常短暂)
 
要注意的是第三点中的6个TCP连接数上限在不同浏览器中也不相同,根据HTTP协议的规定限制数量应为2个,但是各浏览器有不同的标准:
| Browser | Maximum connections |
|---------|---------------------|
| IE7     | 2                   |
| IE8/IE9 | 6                   |
| IE10    | 8                   |
| IE11    | 13                  |
| FireFox | 6                   |
| Chrome  | 6                   |
| Safari  | 6                   |
| Opera   | 6                   |
| iOS     | 6                   |
| Android | 6                   |
---------------------------------
Stalled
当请求被放入队列后,就会有阻塞的时长。Stalled描述的是请求被发送之前等待的时长,一般就是Queueing中的原因导致的。除此之外,Stalled还包含了代理协商的时长。
DNS Lookup
进行DNS查找(域名解析)的时长。每次请求一个新的域名的时候,需要进行一次完整的域名解析过程。
Proxy negotiation
浏览器与代理服务器进行协商的过程。
Initial Connection /Connecting
建立连接的时长,包括TCP握手/重试的时长和SSL协商的时长。
SSL
完成SSL握手的时长。
Request sending/sent
发布请求的过程时间,一般非常快。
ServiceWorker Preparation
浏览器启动Service Worker的时长。
Request to ServiceWorker
请求被发送至Service Worker的时长。
Waiting(TTFB)
等待最初相应的时长,A.K.A Time To First Byte。这个时长等于请求发送至服务器的延迟、响应返回至客户端的延迟、服务器处理请求的时间之和。
Content Download
浏览器接收完整相应的时长。
Receiving Push
浏览器接收服务器(HTTP/2)推送数据的时长。
Reading Push
浏览器读取接收到数据的时长。
结合上图为例,可以看到请求图中接口的时间消耗:
- 请求放入队列花费1.94ms
 - 请求在队列中被阻塞了2.25ms直至开始发送请求
 - 域名解析花费0.41ms
 - 与服务器建立连接,花费72.11ms,其中,完成SSL协商,花费的38.24ms是包含在72.11ms内的
 - 发送请求,花费0.17ms
 - 发送后到接收第一个响应数据间隔了36.07ms
 - 完成响应报文的下载花费了2.00ms
 
本次请求一共花了115.16ms(数据求和为114.95ms,推断各阶段间有细微执行时间没有统计到)。
Full Request Process
现在结合Chrome的DevTools信息,再来更新一下从请求到页面展示的流程,以用户在浏览器中输入www.baidu.com为例,其中Chrome相关内容加粗显示:
- 请求准备部分
- 用户在浏览器中输入www.baidu.com,按下回车
 - Chrome开始准备请求,如果满足被Queued规则的话放入队列中等待
 - 此时Chrome会分配磁盘的缓存空间为后续缓存操作做准备
 - 队列任务等待结束,开始准备发送请求;非队列任务准备发送请求
 - Chrome查询是否有可用的磁盘或内存缓存:
- 如有且新鲜,则获取缓存内容,跳过解析和发起请求过程
 - 如果没有缓存,继续按照后续步骤发起请求
 
 
 - 域名解析部分
- 浏览器解析URL,获取协议、路径和端口号
 - 浏览器组装一个HTTP请求报文
 - 浏览器进行域名解析,获取主机的IP地址,主要通过:
- 浏览器对域名解析结果的缓存
 - 本机对域名解析结果的缓存
 - hosts文件
 - 路由器缓存
 - ISP DNS缓存
 - DNS递归查询
 
 
 - 发起请求部分
- 获取到IP后打开一个socket,与目标建立TCP连接,进行三次握手,细节太多受限篇幅不在此叙述,花费时间即为Initial Connection中减去SSL相关的部分
 - 如果为HTTPS请求,进行SSL握手,花费时间为Timing中SSL部分
 - 发送HTTP请求
 
 - 接收响应部分
- 服务器检查HTTP请求头是否包含缓存验证信息:
- 验证缓存新鲜,返回304
 - 验证不通过,处理请求并准备HTTP相应
 
 - HTTP响应通过建立的TCP连接发送给浏览器,首个报文抵达时间即为Waiting(TTFB),完整接收时长为Waiting+Content Download时长
 - 浏览器视情况保留TCP连接或进行四次挥手结束连接
 - 浏览器根据响应状态码进行处理
 - 如果响应资源可以缓存,进行缓存
 - 对压缩(如gzip)的响应进行解码
 
 - 服务器检查HTTP请求头是否包含缓存验证信息:
 - 处理响应资源,假设资源为HTML文档(后续过程可能没有严格的先后顺序):
- 构建DOM树
 - 针对构建过程中遇到的图片、样式、js启动下载
 - 构建CSSOM树
 - 根据DOM树和CSSOM树构建渲染树
 - JS解析
 - 显示页面
 
 
Ref
chromium.org – Issue: Match Firefox’s per-host connection limit of 15