面试题:输入一条URL到页面展示中间发生什么(DNS解析,3握4挥,http/https,渲染)

用户输入URL:

用户输入URL,浏览器先会判断用户输入的是搜索内容还是一个网址,如果是内容,就将搜索内容+搜索引擎合成一个新的URL;如果用户输入的URL符合规范,浏览器就会根据URL协议在这段内容上加上协议合成合法URL。

以下是5个步骤:

  • DNS解析(CDN加速)

  • TCP连接(网络模型,三握四挥)

  • 发送HTTP请求

  • 服务器处理请求并返回HTTP报文

  • 浏览器解析渲染页面

1.DNS解析

输入url后,首先需要找到这个url域名的服务器ip,为了找到这个ip,会有一个递归查找的过程,从浏览器缓存中查找->本地的hosts文件查找->找本地DNS解析器缓存查找->本地DNS服务器查找,这个过程中任何一步找到了都会结束查找流程。如果本地DNS服务器无法查询到,然后在去向本地DNS服务器发请求查找,如果本地域名服务器没查找到,会从根域名服务器查找,该过程属于迭代查找,根域名会告诉你从哪个与服务器查找,最后查找到对应的ip地址后把对应规则保存到本地的hosts文件中。这个过程是迭代查找
如下图所示,先横着查找缓存递归,然后往下迭代查找每个服务器
在这里插入图片描述

1.1 CDN加速:

CDN是什么
一组分布在多个不同地理位置的 Web 服务器。我们都知道,当服务器离用户越远时,延迟越高。CDN 就是为了解决这一问题,在多个位置部署服务器,让用户离服务器更近,从而缩短请求时间。

过程如下:
用户输入url地址后,本地DNS会解析url地址,不过会把最终解析权交给CNAME指向的CDN的DNS服务器
CDN的DNS服务器会返回给浏览器一个全局负载均衡IP
用户会根据全局负载均衡IP去请求全局负载均衡服务器
全局负载均衡服务器会根据用户的IP地址,url地址,会告诉用户一个区域负载均衡设备,让用户去请求它。
区域负载均衡服务器会为用户选择一个离用户较近的最优的缓存服务器,并把ip地址给到用户
用户想缓存服务器发送请求,如果请求不到想要的资源的话,会一层层向上一级查找,直到查找到为止。

总的来说,DNS可以返回一个根据每台机器的负载量,该机器离用户地理位置的距离较近的机器的IP给用户,这种过程就是DNS负载均衡,又叫做DNS重定向。DNS服务器会返回一个跟用户最接近的点的IP地址给用户,CDN节点的服务器负责响应用户的请求,提供所需的内容,加快速度。
得到服务器ip地址后,浏览器根据这个ip的端口号,构造一个http请求,这个请求报文会包括这次请求的信息

2.建立TCP连接

这里首先讲一下为什么要进行TCP连接的网络层模型:

TCP/IP协议族按层次分别分为以下4层:应用层、传输层、网络层和数据链路层

好处:把各层之间的接口部分规划好之后,每个层次内部的设计就能够自由改动了

2.1 应用层

应用层决定了向用户提供应用服务时通信的活动

TCP/IP协议族内预存了各类通用的应用服务

比如,FTP(File Transfer Protocol,文件传输协议)和DNS(Domain Name System,域名系统)服务就是其中两类。

HTTP协议也处于该层

2.2 传输层

传输层对上层应用层,提供处于网络连接中的两台计算机之间的数据传输

在传输层有两个性质不同的协议:TCP(Transmission Control Protocol,传输控制协议)和UDP(User Data Protocol,用户数据报协议)

2.3 网络层(网络互连层)

网络层用来处理在网络上流动的数据包

数据包是网络传输的最小数据单位。该层规定了通过怎样的路径(所谓的传输路线)到达对方计算机,并把数据包传送给对方

与对方计算机之间通过多台计算机或网络设备进行传输时,网络层所起的作用就是在众多的选项内选择一条传输路线

2.4 链路层(数据链路层、网络接口层)

用来处理连接网络的硬件部分

包括控制操作系统、硬件的设备驱动、NIC(Network Interface Card,网络适配器,即网卡),及光纤等物理可见部分(还包括连接器等一切传输媒介)

硬件上的范畴均在链路层的作用范围之内
在这里插入图片描述
用HTTP举例来说明

  • 首先作为发送端的客户端在应用层(HTTP协议)发出一个想看某个Web页面的HTTP请求
  • 为了传输方便,在传输层(TCP协议)把从应用层处收到的数据(HTTP请求报文)进行分割,并在各个报文上打上标记序号及端口号后转发给网络层。
  • 在网络层(IP协议),增加作为通信目的地的MAC地址后转发给链路层
  • 接收端的服务器在链路层接收到数据,按序往上层发送,一直到应用层
    当传输到应用层,才能算真正接收到由客户端发送过来的HTTP请求

发送端在层与层之间传输数据时,每经过一层时必定会被打上一个该层所属的首部信息
反之,接收端在层与层传输数据时,每经过一层时会把对应的首部消去

2.5 负责传输的 IP(Internet Protocol) 协议

按层次分,IP(Internet Protocol)网际协议位于网络层

IP协议的作用是把各种数据包传送给对方。而要保证确实传送到对方那里,则需要满足各类条件。其中两个重要的条件是IP地址和MAC地址(Media Access Control Address)

image

  • IP与MAC
    1.IP地址指明了节点被分配到的地址,MAC地址是指网卡所属的固定地址
    2.IP地址可以和MAC地址进行配对
    3.IP地址可变换,但MAC地址基本上不会更改
    4.使用ARP协议凭借MAC地址进行通信

  • IP间的通信依赖MAC地址
    1.在网络上进行中转时,会利用下一站中转设备的MAC地址来搜索下一个中转目标
    2.会采用ARP协议(Address Resolution Protocol)
    3.ARP是一种用以解析地址的协议,根据通信方的IP地址就可以反查出对应的MAC地址
    4.没有人能够全面掌握互联网中的传输状况

在到达通信目标前的中转过程中,那些计算机和路由器等网络设备只能获悉很粗略的传输路线。这种机制称为路由选择(routing)

2.6 确保可靠性的 TCP 协议

按层次分,TCP位于传输层,提供可靠的字节流服务

字节流服务(Byte Stream Service)是指为了方便传输,将大块数据分割成以报文段(segment)为单位的数据包进行管理。而可靠的传输服务是指,能够把数据准确可靠地传给对方。

TCP协议为了更容易传送大数据才把数据分割,而且TCP协议能够确认数据最终是否送达到对方

确保数据能到达目标——三次握手

为了准确无误地将数据送达目标处,TCP协议采用了三次握手(three-way handshaking)策略

首先,判断是不是https的,如果是,则HTTPS其实是HTTP + SSL / TLS 两部分组成,也就是在HTTP上又加了一层处理加密信息的模块。服务端和客户端的信息传输都会通过TLS进行加密,所以传输的数据都是加密后的数据。

进行三次握手,建立TCP连接。

三次握手,客户端先向服务端发起一个SYN包,进入SYN_SENT状态,服务端收到SYN后,给客户端返回一个ACK+SYN包,表示已收到SYN,并进入SYN_RECEIVE状态,最后客户端再向服务端发送一个ACK包表示确认,双方进入establish状态。

在这里插入图片描述
参考YK菌内容:YK博客地址
之所以是三次握手而不是两次,是因为如果只有两次,在服务端收到SYN后,向客户端返回一个ACK确认就进入establish状态,万一这个请求中间遇到网络情况而没有传给客户端,客户端一直是等待状态,后面服务端发送的信息客户端也接受不到了。
步骤如下图所示:

在这里插入图片描述

如果是https,还需要进行SSL加密
加密过程如下图:
在这里插入图片描述

6.断开TCP连接

四次挥手

断开进行4次数握手

  1. 客户端服务端发送释放连接的请求
  2. 服务端收到客户端的请求后,告知应用层释放连接
  3. 服务端将数据发送完毕后,再向客户端发送释放连接请求
  4. 客户端收到释放请求后,并向服务端发送确认释放的应答,同意释放

在这里插入图片描述
之所以等待两个周期是因为最后客户端发送的ACK包可能会丢失,如果不等待2个周期的话,服务端在没收收到ACK包之前,会不停的重复发送FIN包而不关闭,所以得等待两个周期

3.发送HTTP请求

TCP连接建立后,浏览器就可以利用HTTP/HTTPS协议向服务器发送请求了。
这里有一个HTTP请求时间的问题如下:
在这里插入图片描述
所以真正接收数据时间只有13.05ms,大部分都浪费在了连接方面,这就是请求合并原因。

4.服务器处理请求,返回响应结果

服务器接受到请求,就解析请求头,如果头部有缓存相关信息如if-none-match与if-modified-since,则验证缓存是否有效,若有效则返回状态码为304,直接使用客户端缓存即可,该过程属于协商缓存,若无效则重新返回资源,状态码为200.

这里有发生的一个过程是HTTP缓存(强缓存和协商缓存)可以了解一下如下
强缓存

在客户端发起请求之前,先检查强缓存,查看强缓存的cache-control里的max-age,判断数据有没有过期,如果没有直接使用该缓存 ,有些用户可能会在没有过期的时候就点了刷新按钮,这个时候浏览器就回去请求服务端,要想避免这样做,可以在cache-control里面加一个immutable.这样用户再怎么刷新,只要 max-age 没有过期就不会去请求资源。
public
因为服务端返回数据的过程中可能会经过很多虚拟代理服务器,他们可以进行缓存,使用 public 就是允许它们缓存
private
不允许代理服务器缓存,允许客户端缓存
no-cache
?- 不允许强缓存,可以协商缓存
no-store
不允许缓存

协商缓存

浏览器加载资源时,没有命中强缓存,这时候就去请求服务器,去请求服务器的时候,会带着两个参数,一个是If-None-Match,也就是响应头中的etag属性,每个文件对应一个etag;另一个参数是If-Modified-Since,也就是响应头中的Last-Modified属性,带着这两个参数去检验缓存是否真的过期,如果没有过期,则服务器会给浏览器返回一个304状态码,表示缓存没有过期,可以使用旧缓存。
etag的作用
有时候编辑了文件,但是没有修改,但是last-modified属性的时间就会改变,导致服务器会重新发送资源,但是etag的出现就完美的避免了这个问题,他是文件的唯一标识
last-modified和etag各有各自的优点和缺点:
每个文件都有一个 etag 和 last-modified 属性,etag 要优先于 last-modified,两个属性各有优缺点,比如 last-modified 很多时候不能感知文件是否改变,但 etag 能;last-modified 仅仅记录了时间点,性能肯定要高于etag,etag 记录的是哈希值

5.浏览器解析渲染页面

浏览器是一个边解析边渲染的过程,

  • 服务器解析这个请求返回html给浏览器->浏览器构建DOM树->遇到js脚本停止构建,先加载js->没有js脚本则构建css,构建完成后与DOM合并为render Tree->然后进行布局渲染

客户端自上而下执行代码
其中遇到CSS加载的时候,CSS不会阻塞DOM树的解析,但是会阻塞DOM树的渲染,并且CSS会阻塞下面的JS的执行
然后是JS加载,JS加载会影响DOM的解析,之所以会影响,是因为JS可能会删除添加节点,如果先解析后加载的话,DOM树还得重新解析,性能比较差。如果不想阻塞DOM树的解析的话,可以给script添加一个defer或者async的标签。
defer:不会阻塞DOM解析,等DOM解析完之后在运行,在DOMContentloaed之前
async: 不会阻塞DOM解析,等该资源下载完成之后立刻运行
进行DOM渲染和Render树渲染
获取html并解析为Dom树
解析css并形成一个cssom(css树)
将cssom和dom合并成渲染树(render树)
进行布局(layout)
进行绘制(painting)
回流重绘
回流必将引起重绘,重绘不一定引起回流
当改变 width、height 等影响布局的属性时会引起回流,或者当获取 scroll、client、offset 一族时,浏览器为获取这些值也会进行回流,getComputedStyle 也会引起回流

细一点来讲如下:

(1)构建DOM树

浏览器从网络或硬盘中获得HTML字节数据后会经过一个流程将字节解析为DOM树

具体步骤:

  1. 转码(Bytes -> Characters)—— 读取接收到的 HTML 二进制数据,按指定编码格式将字节转换为 HTML 字符串在这里插入图片描述

  2. Tokens 化(Characters -> Tokens)—— 解析 HTML,将 HTML 字符串转换为结构清晰的 Tokens,每个 Token 都有特殊的含义同时有自己的一套规则 在这里插入图片描述

  3. 构建 Nodes(Tokens -> Nodes)—— 每个 Node 都添加特定的属性(或属性访问器),通过指针能够确定 Node 的父、子、兄弟关系和所属 treeScope(例如:iframe 的 treeScope 与外层页面的 treeScope 不同)

  4. 构建 DOM 树(Nodes -> DOM Tree)—— 最重要的工作是建立起每个结点的父子兄弟关系

浏览器解析HTML文件构建DOM树,在构建DOM树时,如果遇到JS脚本或外部的JS连接,则会停止构建DOM,来加载相应的代码,这会造成阻塞,如果不想阻塞DOM树的解析的话,可以给script添加一个defer或者async的标签。

defer:

  • 给 script 标签添加 defer 属性,就不会阻塞 dom 的解析了,等 dom 渲染完之后才会运行该 js 代码,如果是多个 script 标签都添加了 defer 属性的话,那么它们是按照顺序执行

async:

  • 给 script 添加 async 属性之后,会异步下载 js 代码,等下载完成之后会立即运行js 代码。多个 script 标签同时设置了 async 是没有先后顺序的,谁先加载完谁就先运行。
    如果 script 标签没有操作任何 dom 信息,且不彼此依赖的话,可以使用 async

这就是为什么推荐JS代码应该放在html代码的后边,之后根据外部样式和内部样式以及内联样式构建一个CSSOM规则树,构建完成之后与DOM合并为渲染树;之后进行布局然后渲染页面。因为html文件中会含有图片,视频,音频等资源,在解析DOM的过程中,遇到这些都会进行并行下载,浏览器对每个域的并行下载数量有一定的限制,一般是4-6个。

JS是单线程运行,也就是说,在同一个时间内只能做一件事,所有的任务都需要排队,前一个任务结束,后一个任务才能开始。但是又存在某些任务比较耗时,如IO读写等,所以需要一种机制可以先执行排在后面的任务,这就是:同步任务(synchronous)和异步任务(asynchronous)。

JS的执行机制就可以看做是一个主线程加上一个任务队列(task queue)。同步任务就是放在主线程上执行的任务,异步任务是放在任务队列中的任务。所有的同步任务在主线程上执行,形成一个执行栈;异步任务有了运行结果就会在任务队列中放置一个事件;脚本运行时先依次运行执行栈,然后会从任务队列里提取事件,运行任务队列中的任务,这个过程是不断重复的,所以又叫做事件循环(Event loop)。

(2)样式计算

渲染引擎将 CSS 样式表转化为浏览器可以理解的 styleSheets,计算出 DOM 节点的样式。

CSS 样式来源主要有 3 种,分别是通过 link 引用的外部 CSS 文件、style标签内的 CSS、元素的 style 属性内嵌的 CSS。,其样式计算过程主要为:

图片

可以看到上面的 CSS 文本中有很多属性值,如 2em、blue、bold,这些类型数值不容易被渲染引擎理解,所以需要将所有值转换为渲染引擎容易理解的、标准化的计算值,这个过程就是属性值标准化。处理完成后再处理样式的继承和层叠,有些文章将这个过程称为CSSOM的构建过程。

(3)布局树

布局过程,即排除 script、meta 等功能化、非视觉节点,排除 display: none 的节点,计算元素的位置信息,确定元素的位置,构建一棵只包含可见元素布局树。

布局完成过程中,如果有js操作或者其他操作,对元素的颜色,背景等作出改变就会引起重绘,如果有对元素的大小、定位等有改变则会引起回流

常见的会导致回流的元素:
  • 常见的几何属性有 width、height、padding、margin、left、top、border 等等。
  • 最容易被忽略的操作:获取一些需要通过即时计算得到的属性,当你要用到像这样的属性:offsetTop、offsetLeft、 offsetWidth、offsetHeight、scrollTop、scrollLeft、scrollWidth、scrollHeight、clientTop、clientLeft、clientWidth、clientHeight 时,浏览器为了获取这些值,也会进行回流。
避免方式:
  1. 避免逐条改变样式,使用类名去合并样式
  2. 将 DOM “离线”,使用DocumentFragment
  3. 提升为合成层,如使用will-change

will-change属性可以提前通知浏览器我们要对元素做什么动画,这样浏览器可以提前准备合适的优化设置。这样可以避免对页面响应速度有重要影响的昂贵成本。元素可以更快的被改变,渲染的也更快,这样页面可以快速更新,表现的更加流畅。

#divId {
  will-change: transform;
}

优点

  • 合成层的位图,会交由 GPU 合成,比 CPU 处理要快
  • 当需要 repaint 重绘时,只需要 repaint 本身,不会影响到其他的层
  • 对于 transform 和 opacity 效果,不会触发 layout 和 paint
(4)生成分层树

页面中有很多复杂的效果,如一些复杂的 3D 变换、页面滚动,或者使用 z-indexing 做 z 轴排序等,为了更加方便地实现这些效果,渲染引擎还需要为特定的节点生成专用的图层,并生成一棵对应的图层树(LayerTree),如图:图片

(5)栅格化

为每个图层生成绘制列表,并将其提交到合成线程。合成线程将图层分图块,并栅格化将图块转换成位图。

合成线程会按照视口附近的图块来优先生成位图,实际生成位图的操作是由栅格化来执行的。所谓栅格化,是指将图块转换为位图。如图:

图片

通常一个页面可能很大,但是用户只能看到其中的一部分,我们把用户可以看到的这个部分叫做视口(viewport)。在有些情况下,有的图层可以很大,比如有的页面你使用滚动条要滚动好久才能滚动到底部,但是通过视口,用户只能看到页面的很小一部分,所以在这种情况下,要绘制出所有图层内容的话,就会产生太大的开销,而且也没有必要。

(6)显示

最后,合成线程发送绘制图块命令给浏览器进程。浏览器进程根据指令生成页面,并显示到显示器上,渲染过程完成。

结束End········

当然在这些所有的请求中我们还需要关注的就是缓存,缓存一般通过Cache-Control、Last-Modify、Expires等首部字段控制。

  • Cache-Control和Expires的区别在于Cache-Control使用相对时间,
  • Expires使用的是基于服务器 端的绝对时间,因为存在时差问题,一般采用Cache-Control,在请求这些有设置了缓存的数据时,会先 查看是否过期,如果没有过期则直接使用本地缓存,过期则请求并在服务器校验文件是否修改,如果上一次 响应设置了ETag值会在这次请求的时候作为If-None-Match的值交给服务器校验,如果一致,继续校验 Last-Modified,没有设置ETag则直接验证Last-Modified,再决定是否返回304。
  • 2
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值