访问网页流程

  1. 浏览器 发送请求到 dns服务器dns服务器 进行域名解析,解析完成之后 浏览器 拿到了 网页服务器 的IP地址
  2. 浏览器 根据拿回来的ip地址发送http请求给 网页服务器
  3. 网页服务器 接收到http请求并处理,从自己的硬盘目录里找到 浏览器 请求的网页文件并返回给 浏览器
  4. 浏览器 接收到 网页服务器 返回的网页文件,开始用自己的内核渲染网页,并最终展示到显示器上

这里的每一步都环环相扣,中间哪个步骤都不能掉链子。并且每个步骤的执行速度,都会影响到我们对某个网站打开速度的直观感受。我们再把影响这四个步骤的速度的因素来逐一分析:

浏览器 发送请求到 dns服务器dns服务器 进行域名解析,解析完成之后 浏览器 拿到了 网页服务器 的IP地址

  • 浏览器初步解析用户往地址栏输入的字符串。
  • 如果用户输入的字符串是合法的网址:浏览器会检查自带的 预加载HSTS列表(HTTP严格传输安全列表),这个列表里包含了那些请求浏览器只使用 https 进行连接的网站。如果网站在这个列表里,浏览器会使用 https 而不是 http 协议,否则,没有标明协议的url域名会默认使用 http 协议访问。(p.s. 一个网站哪怕不在 HSTS 列表里,也可以要求浏览器对自己使用HSTS政策进行访问。浏览器向网站发出第一个HTTP请求之后,网站会返回浏览器一个响应,请求浏览器只使用HTTPS发送请求。然而,就是这第一个HTTP请求,却可能会使用户受到黑客攻击,这也是为什么现代浏览器都预置了HSTS列表)
  • 如果用户输入的字符串不是网址,只是瞎几把打的词语:浏览器会将地址栏中输入的文字传给用户设置的默认搜索引擎,比如百度。大部分情况下,在把文字传递给搜索引擎的时候,URL会带有特定的一串字符,用来告诉搜索引擎这次搜索来自这个特定浏览器。这时用户想要访问的域名就是百度的域名baidu.com 加上在地址栏里瞎几把打的搜索关键字‘谁是世界第一帅’最终拼凑出来的 https://www.baidu.com/s?ie=UTF-8 & wd='谁是世界第一帅'
  • 在以上两种情况中,浏览器都会检查输入用户输入的字符串是否含有不是 a-z, A-Z,0-9之类的字符。如果用户输入的域名有非ASCII字符的话,浏览器会对域名部分使用 punycode 编码。例如 中国.cn,会被浏览器用Punycode转换为:xn--fiqs8s. cn。punycode编码是因为操作系统的核心都是英文组成,早期的dns服务器只支持英文域名的解析,并且解析也是由英文代码交换,所以dns服务器并不支持直接的中文域名解析,所有中文域名的解析都需要先转成punycode码。其实目前所说和各种浏览器都完美支持后来才出现的中文域名,只是浏览器给中文域名自动转码了,这样就不需要老旧的dns服务器再次安装中文域名转码控件来兼容中文域名的解析了。
  • 浏览器这时候得到了用户想要访问的网页服务器的域名,下一步是要根据这个域名拿到网页服务器的ip地址。
  • 浏览器会检查该域名是否在自己的缓存当中,因为浏览器默认会把用户访问过的网站缓存在本地。如果浏览器缓存中没有,浏览器就会去调用操作系统的getHostByName库函数进行查询。getHostByName函数会首先检查域名是否在本地的hosts文件里,host是一个记录着 域名 -> ip地址 映射关系表 的文件,hosts的位置不同的操作系统有所不同。如果 getHostByName没有在本地的 hosts文件 里找到域名对应的ip地址,它将会向 dns服务器 发送一条dns查询请求,去获取域名对应的IP地址。
  • dns服务器开始解析。dns服务器硬件配置差,机房温度太高,受到ddos攻击,不合理的解析协议算法等,都会导致dns服务器域名解析速度慢。
  • 另:好的网络架构应该会在局域网内设立一个缓存服务器,把那些局域网内大家都经常请求的网址的域名ip映射,甚至是网址内容都缓存下来,就不用跑去大老远的dns服务器那里先解析地址,之后再取网页文件了。就好比是全世界人民都喜欢吃麦当劳的薯条,但麦当劳不会只在美国开唯一一家全球总店,而是世界各地都开了分店,而且每家分店都有薯条卖,这样大家就不用跑去美国总店吃薯条了,出家门右转就能找到分店吃薯条了,在网络世界里,这个技术叫做CDN。

浏览器 根据拿回来的ip地址发送http/https请求给 网页服务器

  • 浏览器通过本机的操作系统的http模块发送http请求,和网页服务器通过经典的tcp三次握手之后建立可靠的连接(从硬件层面来说,用户的http请求数据流依次会经过用户的主机上的网卡 ->水晶头 -> 网线 ...),这一步的速度没啥好说的,主要就是 tcp 和 http 这些协议的算法的空间复杂度,时间复杂度的问题。
  • 这个步骤可能会掉链子,举个掉链子的栗子:中间人攻击。黑客截包之后篡改http请求头的 request host,让用户的请求最终发去了别的地方,而非用户的目标网页服务器。比如用户想去真·网页服务器请求一个修改密码的网页,结果这个请求发了去黑客服务器那里,黑客服务器给用户返回了一张高仿的修改密码页,用户不假思索就往这个网页上输入了自己的密码,然后就..........
  • 真实案例:邮件重置密码时,劫持了邮件的内容,将host替换掉,然后用户点击发起链接的事后,身份认证信息(这里一般指的是网站传给重置密码者的随机token)会自动传到恶意的host上面,从而导致攻击者可以劫持账户(已知身份认证信息)。
  • http请求头就好比是包裹上面贴着的快递单,上面写着这个包裹从哪来,要去哪等等信息,被随意篡改会出事的。

网页服务器 接收到http请求并处理,从自己的硬盘目录里找到 浏览器 请求的网页文件并返回给 浏览器

  • 以上这步和网页服务器的性能有关。
  • 网页服务器的http服务选择的是支持高并发的nginx,还是Apache,或者IIS都有影响,他们都属于http协议的实现,底层可能涉及到操作系统的io速度/线程的异步执行/内存管理等,我对此涉猎不深,略过不表。题外话:go有buffer ,python和node都没有buffer,后面俩加了buffer,io那项至少再快一半。所以服务端的http服务,我看好 go。
  • 有个疑问:如果网页服务器的硬盘用的都是ssd,可能 io 速度能快一些,对打开用户访问某个网站的速度有直观上的提升吗?

浏览器 接收到 网页服务器 返回的网页文件,开始用自己的内核渲染网页,并最终展示到显示器上

(这一步不考虑 服务端渲染 的情况,我们讨论最普通的 用户端渲染

  • 浏览器开始进行html解析
  • 浏览器内核有很多种,但都包含 js引擎css引擎html解析引擎
  • html解析引擎的主要工作是生成dom节点树。dom树是以dom元素以及其属性为节点的树。
  • 由于不能使用常用的解析技术,浏览器创造了专门用于解析HTML的解析器。解析算法在 HTML5 标准规范中有详细介绍,算法主要包含了两个阶段:标记化 dom树的构建
  • html不能使用常见的自顶向下或自底向上方法来进行解析,主要原因是以下几点 ① html本身可容错的特性 ② HTML本身可能是残缺的,对于常见的残缺,浏览器需要有传统的容错机制来支持它们,比如<input/>可能会写漏/写成了<input> ③ 解析过程需要反复。对于其他语言来说,源码不会在解析过程中发生变化,是静态的。但是对于HTML来说,例如js脚本中包含的 document.write() 方法会在源码中添加内容,也就是说,解析过程实际上会动态改变最初输入的html。
  • 注意,解析 HTML 网页时永远不会出现“语法错误”,浏览器会自动修复所有错误,然后继续解析。
  • html解析完成
  • 浏览器开始加载网页的外部资源(CSS,图像,Javascript 文件等)。这个说来话长,无数前端工程师为了能减少这里加载资源要发送的http请求数量,减小外部资源的体积,用了各种令人窒息的操作·····················比如把整个网站用到的小图标放在一张png的雪碧图,webpack的模块按需加载
  • 此时浏览器把文档标记为“可交互的”,浏览器开始 解析 处于 推迟 (defer)模式的脚本,也就是那些需要在文档 解析 完毕之后再 执行 的脚本。之后文档的状态会变为‘完成’,浏览器会执行 onload 事件。
  • js引擎开始执行同步的 js 代码
  • css引擎开始解析css代码,渲染页面(这个过程和传统工厂里报纸的印刷十分类似,先把元素框的大概位置布局固定好,再往上细致地喷颜色纹理之类
  • 根据css语法和句法去分析.CSS文件和 <style> 标签包含的内容
  • 每个CSS文件都被解析成一个样式表对象,这个对象里包含了带有选择器的CSS规则,和对应CSS语法的对象
  • CSS解析器可能是自顶向下的,也可能是使用解析器生成器生成的自底向上的解析器
  • 通过遍历DOM节点树创建一个“Frame 树”或“渲染树”,并计算每个节点的各个CSS样式值
  • 通过累加子节点的宽度,该节点的水平内边距(padding)、边框(border)和外边距(margin),自底向上的计算 Frame树 中每个节点首的选(preferred)宽度
  • 通过自顶向下的给每个节点的子节点分配可行宽度,计算每个节点的实际宽度
  • 通过应用文字折行、累加子节点的高度和此节点的 内边距 (padding)、边框 (border) 和外边距 (margin),自底向上的计算每个节点的高度
  • 使用上面的计算结果构建每个节点的(x,y)坐标
  • 当存在元素使用 floated,位置有 absolutely 或 relatively 属性的时候,会有更多复杂的计算。
  • 创建 layer (图层) 来表示页面中的哪些部分可以成组的被绘制,而不用被重新栅格化处理(就是像素化,可以简单理解为计算屏幕上每个像素格子的颜色值)。每个帧对象都被分配给一个层。
  • 页面上的每个层都被喷绘了纹理(?)
  • 每个层的帧对象都会被遍历,计算机执行绘图命令绘制各个层,此过程可能由CPU执行栅格化处理(就是像素化,计算屏幕上每个像素格子的颜色值),或者直接通过 D2D / SkiaGL 在GPU上绘制
  • 上面所有步骤都可能利用到最近一次页面渲染时计算出来的各个值,这样可以减少不少计算量
  • 计算出各个层的最终位置,一组命令由 Direct3D / OpenGL发出,GPU命令缓冲区清空,命令传至GPU并异步渲染,帧被送到 Window Server。
  • GPU开始把css引擎的计算结果渲染到用户的显示器上
  • 在渲染过程中,图形处理层可能使用通用用途的 CPU,也可能使用图形处理器 GPU
  • 当使用GPU用于图形渲染时,图形驱动软件会把任务分成多个部分,这样可以充分利用GPU 强大的并行计算能力,用于在渲染过程中进行大量的浮点计算。
  • 后期渲染与用户引发的处理:渲染结束后,浏览器根据某些时间机制运行JavaScript代码( 比如Google Doodle动画 ) 或与用户交互 ( 在搜索栏输入关键字获得搜索建议 ) 。类似Flash 和 Java的插件也会运行,尽管Google主页里没有。这些脚本可以触发网络请求,也可能改变网页的内容和布局,产生又一轮渲染与绘制(重排和重绘
posted @ 2020-08-17 17:14  spccoach  阅读(1152)  评论(0)    收藏  举报