从输入url到看到页面经历了些什么(一)

前端界面是用户与计算机交互的一个媒介,为用户提供更好的交互体验,首先需要从明白计算机和网络都干了什么,然后才能针对性的作出一些优化。

本文将从web页面请求历程为例,聊聊输入url到用户看到页面整个过程背后都发生了什么。文章主要参考了《大型网站性能优化实战》以及《高性能网站建设指南》这两本书,同时也参考了网上很多优秀的文章,下面就让我们开始吧~

1.总体概述
从用户体验出发

从用户打开浏览器,浏览网页的感知出发,以下几点因素是衡量用户性能方面对用户体验造成比较重要影响的

  • 白屏(白屏时间过长,用户打开的一直是没有内容的白色画面,这体验。。。)
  • 首屏(首屏是用户进来看到的第一个页面,用户好不容易看到的不是白色页面了,如果这个时候看着资源一顿一顿的加载,或者很久都出不来完整的页面,这体验。。。)
  • 页面整体加载(就是用户能看到完整界面,还能用的时间,这个时间也是比较重要,前两步都完成了,要是用户只能看,还不能用,这体验。。。)
  • 页面可交互(这块就是用户开始用页面的体验,要是用户想实现注册功能,连注册按钮都还没加载出来,这体验。。。)
  • 功能交互响应(如果用户点击一个按钮,浏览器很久都没回应,这体验。。。)

在这些因素中,最基础的就是白屏时间。先明确一个问题,在用户输入url之后,浏览器发送请求到服务器,从服务器获取一些资源,将资源渲染完成后,用户就能在浏览器里看到完整的web界面。在渲染完成之前,页面展示出来的结果就是白屏(有人可能会说有的界面会出现loading或者骨架屏之类的,那些资源也是从服务器获取的,这只是一个界面优化的方式,在界面完全渲染出来之前,让用户不至于一直看着空白的屏幕)。

浏览器与服务器的交互过程

从输入url到看到页面经历了些什么(一)_第1张图片

(图片取自《大型网站性能优化实战一书》)

大概的步骤如下:

  1. 浏览器从DNS服务器中进行域名查询,浏览器解析域名拿到对应的IP地址
  2. 通过IP地址建立TCP请求连接(三次握手)
  3. 浏览器向服务器发送http请求包,服务器请求处理响应
  4. 服务器返回HTTP Response后,浏览器开始接收数据,进行资源下载,解析,页面渲染

对于前端而言,第四个步骤可发挥的余地比较大。前端性能有个黄金法则:只有10%-20%的最终用户响应时间花在了下载HTML文档上,其余的80%-90%时间花在了下载页面中的所有组件上,在进行前端性能优化的时候,可以根据这个黄金法则进行梳理。

接下来让我们仔细的从这几个步骤出发聊聊吧~

2.DNS Lookup

DNS是一个分布式数据库,用于维护URL到其IP地址的映射关系,DNS Lookup是浏览器从DNS服务器中进行域名查询的过程,在一次请求中,首先会进行页面本身的域名查询,在浏览器在解析HTML代码过程中,需要加载JS,CCS,Image等资源的时候也需要进行域名解析。

  1. 域名查询的时候首先会查找DNS缓存,DNS缓存包括浏览器缓存,操作系统本地缓存,路由器缓存以及ISP(本地通信服务商)缓存,如果不存在缓存,这时候就会发起DNS查询
  2. DNS是分布式域名服务器,因此当本地DNS(ISP分配给用户的DNS服务器,具有缓存的能力,对应ISP缓存)没办法提供正确的IP地址的时候,就需要在互联网上搜索多个DNS服务器,以此来找到正确的IP地址,服务器大致有三种——根域名服务器,顶级域名服务器,权威域名服务器。
  3. DNS查找一般有三种类型(递归,迭代,非递归),优化的DNS解析过程可以缩短传输距离,DNS查询报文会经过许多路由器和设备才会到达响应的服务器,经过每个服务器的时候都会使用路由表来确定最优的路由路径(有兴趣可以看看路由选择算法)

在DNS查询阶段可进行以下优化:

  • DNS缓存优化
  • DNS预加载策略(DNS Prefetching 是让具有此属性的域名不需要用户点击链接就在后台解析,而域名解析和内容载入是串行的网络操作,所以这个方式能 减少用户的等待时间,提升用户体验
  • 页面中资源的域名合理分配
  • 稳定可靠的DNS服务器等
3.ARP请求

DNS查询获得IP地址之后,还需要进行ARP(地址解析协议)请求,从而获得对应的MAC地址,之后才能完成通信,ARP以IP地址为线索,定位下一个应该接收数据分包的主机MAC地址。
ARP会维护每个主机和路由器上的APR缓存(或表),缓存中维护着的是每个IP到MAC地址的映射关系,当一个映射关系被缓存过后,下次再向这个地址发送数据时就不需要再次发起ARP请求了,ARP请求通常使用UDP协议(因为需要广播)。

Q:为什么有了IP地址还要加上MAC地址呢?
A:首先TCP/IP是网络层的协议,根据网络的不同同一台机器的ip地址也会发生改变,因此并不能通过IP地址实现与计算机的一一映射,而MAC地址在链路层,与计算机是一一绑定的(出厂的时候就焊死了,是唯一的标识),因此能通过MAC地址实现与计算机的一一映射。其次,仅通过IP地址必须通过在局域网内广播才能与主机匹配,如果只采用IP定位,就会造成网络上大量的广播包,形成广播风暴,大大浪费网络带宽和资源。若采用IP和MAC地址则能确定主机位置,只需要第一次通信广播将IP和MAC相互映射,之后都是通过单播通信。

建立TCP请求

浏览器获取到目标地址的IP地址之后,就能获取到对应的端口号(http默认80,https默认443),接着进行对应的数据包封装,包封装好之后会进行TCP请求,这就是很熟悉的TCP/IP三次握手,简单的描述一下三次握手如下(只是便于记忆,详细的可以参考专门的博文):

  1. 服务端进程准备好接收来自外部的TCP连接,等待客户端发送请求
  2. 客户端打开连接,发送信息给服务器端
  3. 服务器端接收到信息,将接收到信息这个信息回传给客户端,并且为这次通信分配资源
  4. 客户端收到回传信息,说明通道已经联通了,就将这个信息回传给服务端,并分配资源
  5. 开始进行通信
    三次握手的目的:是同步连接双方的序列号和确认号并交换 TCP 窗口大小信息
4.TCP传输

建立好连接之后,就开始进行TCP传输,在服务器端,可以通过优化来降低响应响应给客户端的网络耗时。TCP传输是分段的,一个HTTP响应报文会被操作系统切成多个MSS大小(一般为1460B),发送端每次只会按顺序发送若干段,通过拥塞窗口和接收端窗口,知道接收端接收到完整的报文为止。报文越大,受拥塞控制算法的影响也越大。

接收端流量控制用的是滑动窗口机制,本质上是描述接收方的TCP数据报缓冲区大小的数据,通过这个窗口大小,TCP可以慢慢从数据的左边移到数据的左边,从而实现按顺序进行分段数据的发送。(知道一次能接收多少所以这边才能一次传多少过去)

发送端流量控制用的是拥塞窗口机制,拥塞窗口的原理:TCP发送方先发送少量的数据报文段,然后等待对方的回应,ACK回应后就把这个窗口的大小加倍,然后连续发送两个数据包,对方回应后,继续加倍,知道发生错误或丢包,这样就能检测到网络的承载能力,从而按这个大小发送数据,这个阶段的优化可以通过减小HTTP报文大小和HTTP头的大小来实现,因为报文越小,传输所需要的RTT次数越少,耗时越短(AJax异步化可以减少这个问题)

在传输阶段可进行以下优化:

  • 基于不同网络环境,优化数据包的大小,以减少数据因传输丢失或者被破坏产生的重传,从而提高传输效率
  • 对网络传输链路进行优化(通常需要很大的投入)
TCP连接关闭的四次挥手

传输结束之后要记得关闭TCP连接,这里简单描述一下(具体的看专业的分析博):

  1. 客户端发送给服务器端FIN包(告诉它要关闭了)
  2. 服务器端告诉客户端已经收到准备关闭的消息(这时候可能还有数据没处理完,服务端会继续处理数据)
  3. 服务器告诉客户端信息已处理完
  4. 客户端收到回传信息,关闭通信
5.浏览器向服务器发送请求包

浏览器会向服务器发起请求,在HTTP中,请求方法包括简单请求和非简单请求:

  • 简单请求:不能触发CORS预检请求,Content-Type 的值仅限于下列三者之一:text/plain,multipart/form-data,application/x-www-form-urlencoded。包括GET请求(参数从url中带走)以及POST请求(参数通过body带走)
  • 非简单请求:先发送预请求options,预请求成功之后才会发送真实请求,OPTIONS的请求是由Web服务器处理跨域访问引发的,包括PUT请求(一般用于文件上传),DELETE请求(删除操作)等

除请求内容之外,HTTP请求还包括很多信息,这些信息都被放在头部,一起带给服务器
HTTP是基于TCP协议的,除了HTTP请求之外,根据业务需求,还会有ws请求,ftp请求等。
这里插播一个http协议的比较
http1.0,http1.1,https以及http2.0

http协议最初的目的只是将HTML文档从web服务器传送到客户端的浏览器,但是我们的页面月来越复杂,网络情况也更加复杂,所以HTTP也在不断的优化中

  1. http1.0和http1.1

    1. http1.0在1996年开始使用,只是使用在一些简单的网页上,进行简单的网络请求,http1.1在1999年才开始广泛使用,http1.0是http1.1是在http1.0的基础上在缓存处理,带宽优化,错误通知的处理以及host头处理上进行了优化
    2. http协议运行在TCP之上,默认端口是80,传输的内容都是明文,这个时候就会有安全风险
  2. https

    https需要到CA申请证书,运行在SSL/TLS上,SSL/TLS运行在TCP之上,所有传输的内容都经过加密的,默认的端口是443,它可以有效的防止运营商劫持,解决了防劫持的一个大问题

    https的流程:

    1. 客户端发起请求,连接到服务器的443端口
    2. 服务器把自己的信息以数字证书的形式返回给客户端(包括密钥公钥,网站地址,证书颁发机构,失效日期等),数字证书是非对称加密,就是公钥和私钥不一致
    3. 客户端收到服务器端的响应之后会先验证证书的合法性(看有没有失效,还有网址对不对)
    4. 浏览器生成随机密码(RSA签名),生成的是一个对称密钥,用服务器给的公钥对这个密钥进行加密,发送给服务器端,如果客户端解密成功,说明服务端确实是私钥的持有者
    5. 验证完身份之后,客户端生成一个对称加密的算法和对应密钥,以公钥加密之后发送给服务端。之后客户端与服务端可以用这个对称加密算法来加密和解密通信内容了

Q:为何使用对称加密和非对称加密混合的方式?
A:在这个流程中可以看出,https对数据加密使用的是对称加密,对称加密的密钥是通过非对称加密传输的,这样保证了密钥传输过程中的安全,理论上而言非对称加密会比对称加密安全,但是由于非对称加密会比较耗时,所以为了加快传输速度,在真正数据传输的时候用的对称加密

  1. http2.0

    http2.0与http1.x相比的新特性:

    1. 新的二进制格式(1.x基于文本,2.0基于二进制,实现方便且健壮)
    2. 多路复用:连接共享,每一个request都是是用作连接共享机制的。一个request对应一个id,这样一个连接上可以有多个request,每个连接的request可以随机的混杂在一起,接收方可以根据request的 id将request再归属到各自不同的服务端请求里面
    3. header压缩:使用encoder来减少需要传输的header大小,通讯双方各自cache一份header fields表,既避免了重复header的传输,又减小了需要传输的大小
    4. 服务端推送
6.服务器处理请求并返回响应

这个过程主要工作是服务器端处理数据,返回数据,主要的性能优化点在降低服务端RT(Response Time)上。服务器端返回的响应包含请求的网页以及状态码,压缩类型,如何缓存页面,还有需要设置的cookie信息,隐私信息等待。

服务端请求处理响应主要包括以下几个步骤:

  1. Web服务器根据请求类型,将请求转发给已经注册该请求的应用服务器来处理
  2. 应用服务器接收请求,分配给对应的代码单元处理
  3. 从缓存,数据库,文件系统等获取数据
  4. 基于业务进行数据逻辑处理
  5. 基于模板进行数据格式化渲染
  6. 应用服务器将格式化渲染的数据返回web服务器
  7. web服务器将最终响应内容经过Gzip压缩后,返回客户端

对于HTTP请求而言,HTTP状态码是请求状态的体现,常见的状态码如下:

  • 1**:服务器收到请求,需要请求者继续执行操作
    101:需要切换协议,表示服务器应客户端升级协议的请求对协议进行切换,就像http访问https的时候

  • 2**:操作被成功接受并处理
    200:成功
    204:操作成功但是没有任何的返回

  • 3**:重定向,需要进一步的操作以完成请求
    301:请求的资源被永久移动到新url里面,在nginx上配置rewrite ^/(.*)$ http://**** permanent;可实现
    302:暂时的重定向,跳转后会保留旧地址,在nginx上配置rewrite ^/(.*)$ http://**** redirect;可实现
    304:客户端的缓存,比如之前的图片,二次加载的时候可能就会有304,避免重新加载

插播一个知识点:浏览器缓存
因为304的出现和缓存密不可分,所以在这里讲一讲浏览器的缓存。
浏览器的缓存是指在上一次访问的时候本地备份保存,下一次使用浏览器就可以直接加载本地资源,这样降低请求次数,减少网络延迟,加快页面打开速度。
浏览器缓存分为强缓存和协商缓存

  1. 强缓存(Expires和Cache-control)

    强缓存是利用http头的Expires(http1.0)和Cache-control(http1.1)两个字段进行控制的

    • Expires设置一个绝对的失效时间,在那个时间之前发送的请求都读取本地缓存,这个方法的致命缺点在于服务器端和客户端如果时间不一致,特别是时间相差比较大的前提下,就会导致缓存混乱
    • Cache-Control吸取了这个经验,它一般通过max-age设置一个时间间隔(3600表示3600秒),表示在这个间隔内的请求都读取本地缓存,优先级比Expires高
      • Cache-Control除了max-age之外还有几个常用值:
        1. no-cache:不用强缓存,但是可以用协商缓存(协商缓存开启要设置它)
        2. no-store:直接啥缓存都不让用
        3. public:可被所有用户缓存,包括CDN之类的中间代理服务器
        4. private:只允许终端用户缓存
  2. 协商缓存

    协商缓存会先发送请求到服务器,和服务器商量商量是用本地还是服务器返回,如果本地生效,返回304状态码

    • Last-Modified,If-Modified-Since(http1.0)
      • 本地最后一次修改时间,在请求头带上If-Modified-Since,里面包含Last-Modified,服务器校验这个时间和资源修改时间不一致,就会重新返回,否则返回304
      • 这个的弊端在于资源修改频率可能很高,可能会导致无法进行判断;有的修改并非有效修改(可能只改修改时间不改内容),此时其实不希望重新请求;有的服务器并不能精确的得到文件的最后修改时间
    • ETag,If-None-Match(http1.1)
      • ETag是实体标签的缩写,是资源的唯一标识,在第一次请求资源时服务端会返回,再次请求时通过If-None-Match返回给服务器进行比对

4**:客户端错误,因为某种原因导致无法完成请求
401:请求要求用户的身份认证,一般是账号密码不对
403:服务器理解请求客户端的请求,但是拒绝执行此请求,一般是账号密码对了但是这个账号密码没有对应权限
405:请求方法错了,比如前端写的get,但是后端接口写的是post

429:请求次数过多,比如某个请求在几秒内触发了上百次

5**:服务器错误,服务器在处理请求过程中发生了某些错误
500:服务器内部错误
502:充当网关或代理的服务器,从远端服务器接收到了一个无效的请求
504:网关超时

在服务器端,每一步都有可优化的地方,对中小型网站而言,重心可能会放在数据获取过程的优化上,对大型网站而言,每一步的优化都可能带来质的提升。

7.浏览器进行资源下载,解析,渲染

最后一步是浏览器的渲染,这部分内容比较多,大体的过程如下:

  1. 浏览器解析HTTP的头部代码(如果是Gzip那会先解压),下载头部中引用的css文件或js文件
  2. 解析html代码和css代码,解析出DOM树和CSSDOM树
  3. 通过上面两棵树构造Rendering Tree
  4. 根据Rendering Tree完成绘制过程

在此,输入url之后到页面渲染的全部过程就大概聊完了,浏览器渲染过程比较复杂,将在单独开一篇进行阐述~

你可能感兴趣的:(前端发展历史,网络,javascript)