3. 浏览器请求与响应全过程详解
前言
当在浏览器上输入一个网站链接时,它是如何运行将网页内容呈现在我们的浏览器上的呢?
本文旨在对www.yangoogle.com网页进行详细分析,了解浏览器展示内容的整个流程。下图是在网上检索到一个较清晰的流程图,本文通过这张图进行展开讲解:
- 首先,对输入的url进行DNS解析(找出对端服务器ip地址)
- 通过ip地址,建立TCP请求(三次握手协议)
- 发送HTTP请求与响应
- 解析渲染服务端的响应数据
- 断开TCP连接
流程
- 首先在浏览器中,输入**//www.yangoogle.com**回车
1.解析域名(DNS查询)
DNS(Domain Name System,域名系统)因特网上作为域名和IP(Internet Protocol Address)地址相互映射。
优点:通过域名访问ip,可以不用记住繁杂的ip数字串。
查询规则
简单来说,一条域名的DNS记录会在本地有两种缓存:
- 浏览器缓存
- 操作系统(OS)缓存
在浏览器中访问的时候,会优先访问浏览器缓存,如果未命中则访问OS缓存,最后再访问DNS服务器(一般是ISP提供),然后DNS服务器会递归式的查找域名记录,然后返回。
查询浏览器缓存
浏览器会缓存DNS记录,在chorme中,输入下面指令就可以查看:
chrome://net-internals/#dns
可以查询到yangoogle.com对应的ip地址(当前ip显示已经过期)
查询本地host文件
当按下回车键起,一个HTTP请求就已经开始发出了。那么,浏览器首先要做的就是解析这个域名,通过本地的host文件(ps:推荐一款修改host文件的神器swtichhost)里面的ip地址查询。
如下图,我将yangoogle.com域名对应的ip地址修改为本机地址(127.0.0.1)。当我访问**//www.yangoogle.com**之后,不出意外,结果显示404未找到该页面。
这是因为输入的域名(yangoogle.com),映射的ip地址(127.0.0.1)并没有找到正确的资源,因此会返回404。
注意
利用上面的特性,可以将任意域名映射到指定的ip地址上,这样就可以达到欺骗的目的。这也是常用的一种病毒手法。
查询系统DNS缓存
如果浏览器中还没有,就在系统缓存中查询:
- mac 指令
$ nslookup yangoogle
- windows 指令
$ ipconfig /displaydns
拓展
DNS服务器
如果上面查询到未得到匹配的ip地址,那么浏览器会想DNS服务器发送一条DNS查询的请求,流程如下:
- 路由器缓存
如果系统缓存中没有找到匹配的IP,那么接着会发送一条请求到路由器上,然后路由器在自己的缓存中匹配查询记录
- ISP DNS缓存
如果本地路由器也没有匹配到,这个请求就会发送到ISP(互联网服务器提供商,简称宽带运行商),ISP也有对应的DNS服务器。
注意
如果万恶的运营商对你查询的DNS服务器进行修改的话,跳转他们指定的页面上,或对你的网页注入广告之类的,这样他们就到达营收的目的。这也就是常见的DNS劫持。
- 递归查询
如果连ISP的DNS服务器上都没有查询到的话,那么,你的ISP服务器会向根域名服务器进行搜索。根域名服务器是面向全球的顶级服务器(.com、.cn、.org等)。
例如:我们要查询:test.yangoogle.com
在ISP之前都没有查询到,在顶级DNS服务器上查询到
- 首先,匹配到
.com
一级域名服务器,请求转发到.com
域名服务器。 - 然后,匹配到
.yangoogle
的二级域名服务器,请求接着转发到.yangoogle
服务器。 - 最后,匹配到
test
这个三级域名的ip地址。
- 多ip域名DNS查询
有一种场景,就是一个域名对应多个ip地址(服务器地址)。
这种方式的目的是为了解决DNS查询时间的问题:
- 循环DNS 多个ip列表循环返回DNS查询
- 负载均衡 设计一个特点的ip负载均衡服务器,负责监听请求并转发给后面多个服务器集群,实现多个服务器负载均衡。
- 地理DNS 根据用户的地理位置,按距离返回最近的ip地址
2.TCP连接
在查询到域名对应的ip地址之后,,浏览器设置Remote Address,以及端口号port等信息。确保传输层的正常通信(即TCP正常连接)
- 生成TCP数据包
- 建立TCP连接
- 三次握手(HTTP)
- TSL握手(HTTPS)
ip.src == 175.45.4.110 or ip.dst == xx.xx.xx.xx
3.发送http请求
传输层通过已经建立完成,那么就准备发送http请求。
可以查询到http请求体的一些常见设置:
- Request URL 定位请求资源位置
- Request Method 请求方式
- Status Code 状态码:200(服务器已经成功处理)
- Remote Address (服务器ip地址)
- Referrer-Policy 用户代理行为
4.接收服务器响应
在服务器端接收来自浏览器的请求之后,服务端做出了相应的增删改查操作之后,返回相应的Content-Type。浏览器通过不同的Content-Type做出不同的解析。
当我们请求一个.html
类型的页面文件时,服务器端就将Content-Type设置为text/html。
同时,服务端也可以设置Cookie的过期时间(Expires),是否使用压缩(Content-Encoding)
Etag 是URL的Entity Tag,用于标示URL对象是否改变,区分不同语言和Session等等。具体内部含义是使服务器控制的,就像Cookie那样。
5.解析
当服务端返回.html
文件之后,接下来,浏览器就要对response
的内容进行渲染
let arr = []
for (let item in document){
arr.push(item)
}
console.log('dom',arr)
Webkit有三个C++的类对应这三类文档,解析这三种文件会产生一个DOM Tree。
-
CSS,解析CSS会产生CSS规则树。
-
Javascript,主要是通过DOM API和CSSOM API来操作DOM Tree和CSS Rule Tree.
-
解析HTML,生成DOM树
通过对response
进行词法分析,生成对应的AST。在解析HTML过程中,网页永远不会出现无效语法,因为浏览器本身具有一定的容错机制,会自动补全修复错误内容,然后继续解析。 -
解析CSS
根据css
的词法和语法分析,挂载到全局的样式表对象中(CSS StyleSheet)
- 解析JS
因为浏览器的UI线程是单线程的:主要执行js执行和渲染界面。
(js可以控制UI的绘制)
因此,当js处于加载中的状态时(padding),就会导致页面阻塞。阻塞期间,浏览器不会执行其他行为的,这就会出现一定时间的空白期。
6.渲染
解析完成后,浏览器引擎会通过DOM Tree 和 CSS Rule Tree 来构造 Rendering Tree。
基本步骤:
- 计算CSS样式
- 构建Render Tree
- Layout – 定位坐标和大小,是否换行,各种position, overflow, z-index属性
- 开画
上图流程中有很多连接线,这表示了Javascript动态修改了DOM属性或是CSS属会导致重新Layout,有些改变不会,就是那些指到天上的箭头,比如,修改后的CSS rule没有被匹配到,等。
DOM Tree里的每个结点都会有reflow方法,一个结点的reflow很有可能导致子结点,甚至父点以及同级结点的reflow。在一些高性能的电脑上也许还没什么,但是如果reflow发生在手机上,那么这个过程是非常痛苦和耗电的。 所以,下面这些动作有很大可能会是成本比较高的。
- 当你增加、删除、修改DOM结点时,会导致Reflow或Repaint
- 当你移动DOM的位置,或是搞个动画的时候。
- 当你修改CSS样式的时候。
- 当你Resize窗口的时候(移动端没有这个问题),或是滚动的时候。
- 当你修改网页的默认字体时。
注:display:none会触发reflow,而visibility:hidden只会触发repaint,因为没有发现位置变化。
注意:
- 这里重要要说两个概念,一个是Reflow,另一个是Repaint。这两个不是一回事。
Reflow的成本比Repaint的成本高得多的多。
-
Repaint —— 屏幕的一部分要重画,比如某个CSS的背景色变了。但是元素的几何尺寸没有变。
-
Reflow —— 意味着元件的几何尺寸变了,我们需要重新验证并计算Render Tree。是Render Tree的一部分或全部发生了变化。这就是Reflow,或是Layout。(HTML使用的是flow based layout,也就是流式布局,所以,如果某元件的几何尺寸发生了变化,需要重新布局,也就叫reflow)reflow 会从这个root frame开始递归往下,依次计算所有的结点几何尺寸和位置,在reflow过程中,可能会增加一些frame,比如一个文本字符串必需被包装起来。
减少reflow/repaint
- 不要一条一条地修改DOM的样式。与其这样,还不如预先定义好css的class,然后修改DOM的className。
- 把DOM离线后修改
// bad
el.style.left = left + "px";
el.style.top = top + "px";
// good
el.style.cssText += "; left: " + left + "px; top: " + top + "px;";
- 不要把DOM结点的属性值放在一个循环里当成循环里的变量。不然这会导致大量地读写这个结点的属性。
- 尽可能的修改层级比较低的DOM。当然,改变层级比较底的DOM有可能会造成大面积的reflow,但是也可能影响范围很小。
- 为动画的HTML元件使用fixed或absoult的position,那么修改他们的CSS是不会reflow的。
- 千万不要使用table布局。因为可能很小的一个小改动会造成整个table的重新布局。
- Rendering Tree 渲染树并不等同于DOM树,因为一些像Header或display:none的东西就没必要放在渲染树中了。
CSS 的 Rule Tree主要是为了完成匹配并把CSS Rule附加上Rendering Tree上的每个Element。也就是DOM结点。也就是所谓的Frame。 然后,计算每个Frame(也就是每个Element)的位置,这又叫layout和reflow过程。 - 通过调用操作系统Native GUI的API绘制。
服务器
断开TCP连接
- 根据Connection请求头,如果是keep-alive服务器就保持住tcp连接,如果没有或是close则服务器response传输完后主动关闭tcp连接。
- 当然现在浏览器都是http1.1都默认是keep-alive的,在浏览器tab关闭时,tcp连接关闭。
Tips
浏览器
查看版本号: chrome://version/
显示性能指标直方图: about:histograms
chrome://histograms/DNS
查看dns缓存: chrome://net-internals/#dns
参考文章
How Browsers Work
How Browsers Render Work
Google – Web Performance Best Practices
知乎文章 推荐
腾讯fex文章
伯乐在线文章
CSDN文章1
CSDN文章2
个人网站文章