阅读 437

让阿里面试官肯定的:从底层分析从输入URL到页面加载完成的过程

输入URL到页面加载完成

画一张鱼骨图方便大家理解

鱼骨图

image.png

输入URL到浏览器接收

从触屏到CPU

由于电压的变化,触摸屏控制器芯片计算出所触摸的位置,然后通过总线接口将信号传到CPU的引脚上

CPU内部处理

移动设备的CPU并不是一个单独的芯片,而是和GPU等芯片集成在一起,被称为片上系统。可以看一下时序图

CPU作用:

  1. 计算的原理: 在时钟的控制下,这些电流会进过mosfet晶体管,晶体管中包含N型半导体和P型半导体,通过电压就能控制线路开闭,然后这些mosfet构成了CMOS,接着再由CMOS实现与、或、非等逻辑电路门,最后由逻辑电路门实现加法、位移等计算。《计算机体系结构》
  2. 在CPU中还需要存储单元来加载和存储数据,这个存储单元一般通过触发器来实现,成为寄存器。

从 CPU 到操作系统内核

图像操作系统中都会包含GUI框架来方便应用程序开发

从操作系统GUI到浏览器

窗口管理服务会根据信息调用其中的监听函数

操作系统GUI将输入事件传递到浏览器中,这个过程中浏览器会做一些预处理:

举一个例子,比如Chrome会根据历史统计来预估所所输入字符对应的网站,比如输入了ba,那么根据之前的历史发现90%的概率会访问www.baidu.com,因此就会在输入回车前马上开始建立TCP链接甚至渲染了,当然还有很多其他的策略。

注:

一般情况下,我们的确是通过应用层http发送请求数据,然后再通过tcp传输层作为通道传送的。但是这
里我们讲的是chrome浏览器的一种优化策略:在没有发送请求前已经建立了tcp了。验证场景就是回车前
不会再network看到请求接口
复制代码

浏览器向网卡发送数据

从浏览器到浏览器内核

接着就是输入url后的回车,这是浏览器会对url进行检查,首先判断协议,如果是http那么就按照web来处理,另外还会对url进行安全检查,然后直接调用浏览器内核中的对应方法。比如Webview中的loadUrl方法。

在浏览器内核中会先查看缓存,然后设置UA等http信息,接着调用不同平台下的网络请求方法。

注意:浏览器和浏览器内核是不同的概念,浏览器指的是chrome/firefox,而浏览器内核则是Bink,Gecko,浏览器内核只负责渲染,浏览器实现GUI(图形用户界面: 应用计算机图形技术实现允许用户使用鼠标等输入设备操纵屏幕上的图标或者菜单选项,以选择命令、调用文件、启动程序或执行一些日常任务)及网络连接等跨平台工作。

HTTP请求的发送

因为网络的底层实现是与内核相关的,所有这一部分需要针对不同的平台进行处理,从应用层角度看主要做两件事情: 通过DNS查询IP、通过Socket发送数据。

  • DNS查询

    1. 什么是DNS查询?

    dns是一个域名系统,是万维网上作为域名和IP地址相互映射的一个分布式数据库,能够使用户更为方便的访问互联网而不用记住IP地址。

    1. DNS是基于UDP(无连接的传输协议)来实现的

    2. DNS的查找工具?

    linux下查询域名解析: nslookup或者dig 域名解析信息查询工具: www.renfei.net/kitbox/digt…

    1. DNS查找过程?

    可以看到这是一个范围逐步缩小的过程,首先由本机所配置的DNS服务器向DNS根节点查询负责.com区域的服务器,然后通过其中一个负责.com的服务器查询负责baidu.com的服务器,最后由一个baidu.com的域名服务器查询fex.baidu.com域名的地址

    1. 还要注意一些点:

    127.0.0.1走的是loopback(回环: 一种将电子信号、数据流等原样返回发送者的行为),和网卡设备没有关系;Chrome会在浏览器启动时预先查询你有可能访问的10个域名;Hosts文件、缓存时间TTL的影响(也就是浏览器缓存)等。

  • 通过Socket发送数据

  • 有了IP地址,就可以通过Socket API来发送数据了,这时可以选择TCP或者UDP协议, beej.us/guide/bgnet…

  • HTTP常用的是TCP协议。

谈一下TCP协议的Head-of-line blocking问题:

  • 假设客户端发送了3个TCP片段(segments)编号分别是1、2、3,如果编号1的包传输丢了,及时编号2、3已经到达也只能等待,因为TCP协议需要保证顺序,这个问题在HTTP pipelining下更严重,因为HTTP pipelining 可以让多个HTTP请求通过一个TCP发送,比如发送两张图片,可能第二张图片的数据已经全收到了,但是还要等第一张图片数据传到。

  • 为了解决TCP协议的性能问题,Chrome团队去年提出了QUIC协议,他是基于UDP实现的可靠传输,比起TCP,他能减少很多来回时间;还有前向纠错码等功能。

  • 但是现在只有谷歌的产品在用QUIC。但是是很有前景的,因为优化TCP需要升级系统内核

  • 浏览器对同一个域名有链接数限制,大部分是6,我以前认为将这个连接数该打后会提升性能,但实际上并不是,Chrome团队有做过实验,发现从6改成10性能反而下降了,造成这个现象的因素有很多,比如建立连接的开销、拥塞控制等问题,而像SPDY、HTTP2.0协议尽管使用一个TCP连接来传输数据,性能反而会更好,而且还能实现请求优先级。

  • 另外HTTP请求是纯文本格式的,所以在TCP的数据段中可以直接分析HTTP的文本。

Socket在内核中的实现

前面说道浏览器的跨平台库通过调用Socket API来发送数据,那么Socket API是如何实现的呢? 这个我还没有研究过,但是可以推荐:

以linux为例,实现:

lxr.linux.no/linux+v3.14…

学习资料: makelinux.github.io/kernel/map/

底层网络协议的具体例子:这个可以自己用抓包工具分析一下子

本机网卡发送数据到服务器

从内核到网络适配器

前面说到调用 Socket API 后内核会对数据进行底层协议栈的封装,接下来启动 DMA 控制器,它将从内存中读取数据写入网卡。

以 Nexus 5 为例,它使用的是博通 BCM4339 芯片通信,接口采用了 SD 卡一样的 SDIO,但这个芯片的细节并没有公开资料,所以这里就不讨论了。

连接WI-FI路由

Wi-Fi 网卡需要通过 Wi-Fi 路由来与外部通信,原理是基于无线电,通过电流变化来产生无线电,这个过程也叫「调制」,而反过来无线电可以引起电磁场变化,从而产生电流变化,利用这个原理就能将无线电中的信息解读出来就叫「解调」,其中单位时间内变化的次数就称为频率,目前在 Wi-Fi 中所采用的频率分为 2.4 GHz 和 5 GHz 两种。

在同一个 Wi-Fi 路由下,因为采用的频率相同,同时使用时会发生冲突,为了解决这个问题,Wi-Fi 采用了被称为 CSMA/CA 的方法,简单来说就是在传输前先确认信道是否已被使用,没有才发送数据。

而同样基于无线电原理的 2G/3G/LTE 也会遇到类似的问题,但它并没有采用 Wi-Fi 那样的独占方案,而是通过频分(FDMA)、时分(TDMA)和码分(CDMA)来进行复用,具体细节这里就不展开了。

以小米路由为例,它使用的芯片是 BCM 4709,这个芯片由 ARM Cortex-A9 处理器及流量(Flow)硬件加速组成,使用硬件芯片可以避免经过操作系统中断、上下文切换等操作,从而提升了性能。

因为内网设备的 IP 都是类似 192.168.1.x 这样的内网地址,外网无法直接向这个地址发送数据,所以网络数据在经过路由时,路由会修改相关地址和端口,这个操作称为 NAT 映射。

运营商网络内的路由

当数据传递到这些路由器后,路由器会取出包中目的地址的前缀,通过内部的转发表查找对应的输出链路。

转发表是通过选路算法来得到的。

主干网间的传输

IDC内网

数据通过光纤最终会来到服务器所在的 IDC 机房,进入 IDC 内网,这时可以先通过分光器将流量镜像一份出来方便进行安全检查等分析

接下来光纤中的数据将进入集群(Cluster)交换机,然后再转发到机架(Rack)顶部的交换机,最后通过这个交换机的端口将数据发往机架中的服务器

最后使用相关设备通过光电效应将光信号转换成电信号,然后进入服务器网卡。因为CPU处理的是电气信号。

服务器CPU

数据到了服务器网卡了,网卡将数据拷贝到内存中,然后通过中断来通知CPU。然后服务器CPU进行一些相关的计算。

服务器接收数据并处理

负载均衡(策略有很多)

请求在进入到真正的应用服务器前,可能还会经过负责负载均衡的机器,作用是将请求合理的分配到多个服务器上,同时防攻击等功能。

  • LVS

    作用: 对外看来只有一个IP,而实际上这个IP后面对应多台机器,因此也被称为Virtual IP。 前面提到的 NAT 也是一种 LVS 中的工作模式,除此之外还有 DR 和 TUNNEL,具体细节这里就不展开了,它们的缺点是无法跨网段,所以百度自己开发了 BVS 系统。

    网段:用来区分网路上的主机是否在同一网路区段内,在局域网中,每台电脑 只能和自己同一网段的电脑互相通讯. Gateway的出厂IP地址是 192.168.123.250,说明它处於192.168.123.X的网段(X代表1-255之间的任意 值).若您的路由器的IP地址是192.168.1.X或是其他位址,说明二者不处於同 一网段,则它们之间无法相互连接。

反向代理

反向代理是工作在HTTP上的,具体实现可以基于HAProxy或者Niginx,因为反向代理能理解HTTP协议,所以能做的非常多的事情:

  • 进行很多统一处理,比如防攻击策略、防抓取、SSL、gzip、自动性能优化等。
  • 应用层的分流策略都能在这里做,比如对/xx路径的请求分到a服务器,对/yy路径的请求分到b服务器,或者按照cookie进行小流量测试等。
  • 缓存,并在后端服务挂掉的时候显示友好的404页面
  • 监控后端服务是否异常
  • ......

Nginx的代码写得非常优秀,从中能学到很多,对高性能服务端开发感兴趣的读者一定要看看

  • 直接基于硬件的F5

Web Server中的处理

请求经过前面的负载均衡后,将进入到对应的服务器上的Web Server, 比如Apache、Tomcat、Node.JS等。

以Apache为例,在接收到请求后会交给一个独立的进程来处理,我们可以通过编写Apache扩展来处理,一般会调用PHP等脚本语言来处理,比如CGI(通用网关接口)下就是将HTTP中的参数放到环境变量中,然后启动PHP进程来执行。

进入后端语言

前面说到 Web Server 会调用后端语言进程来处理 HTTP 请求(这个说法不完全正确,有很多其它可能),那么接下来就是后端语言的处理了,目前大部分后端语言都是基于虚拟机的,如 PHP、Java、JavaScript、Python 等,当然这就是一个领域了,不细说 了。

Web框架

如果你的 PHP 只是用来做简单的个人主页「Personal Home Page」,倒没必要使用 Web 框架,但如果随着代码的增加会变得越来越难以管理,所以一般网站都会会基于某个 Web 框架来开发,因此在后端语言执行时首先进入 Web 框架的代码,然后由框架再去调用应用的实现代码。

读取数据

服务器将数据返回到浏览器

从01到字符

HTTP 请求返回的 HTML 传递到浏览器后,如果有 gzip 会先解压,然后接下来最重要的问题是要知道它的编码是什么,比如同样一个「中」字,在 UTF-8 编码下它的内容其实是「11100100 10111000 10101101」也就是「E4 B8 AD」,而在 GBK 下则是「11010110 11010000」,也就是「D6 D0」,如何才能知道文件的编码?可以有很多判断方法:

  • 用户设置,在浏览器中可以指定页面编码
  • HTTP 协议中
  • 中的 charset 属性值
  • 对于 JS 和 CSS
  • 对于 iframe

如果在这些地方都没指明,浏览器就很难处理,在它看来就是一堆「0」和「1」,比如「中文」,它在 UTF-8 下有 6 个字节,如果按照 GBK 可以当成「涓枃」这 3 个汉字来解释,浏览器怎么知道到底是「中文」还是「涓枃」呢?

不过正常人一眼就能认出「涓枃」是错的,因为这 3 个字太不常见了,所以有人就想到通过判断常见字的方法来检测编码,典型的比如 Mozilla 的 UniversalCharsetDetection,不过这东东误判率也很高,所以还是指明编码的好。

这样后续对文本的操作就是基于「字符」(Character)的了,一个汉字就是一个字符,不用再关心它究竟是 2 个字节还是 3 个字节。

外链资源的加载

javascript的执行

从字符到图片

跨平台2D绘制图

在不同操作系统中都提供了自己的图形绘制 API,比如 Mac OS X 下的 Quartz,Windows 下的 GDI 以及 Linux 下的 Xlib,但它们相互不兼容,所以为了方便支持跨平台绘图,在 Chrome 中使用了 Skia 库。

GPU合成

浏览器将页面展示出来

Framebuffer

从内存到LCD

LCD显示

忽略内容

内存相关

  • 堆:这里的分配策略有很多,比如malloc的实现
  • 栈:函数调用,已经有很多优秀的文章了
  • 内存映射:动态库加载等
  • 队列:队列无处不在,但是这些细节和原理关系不大

各种缓存

  • CPU的缓存
  • 操作系统的缓存
  • HTTP缓存
  • 后端缓存

各种监控:很多日志保存下来以便后续分析

Basic Info

Project Name

  • 从输入URL到页面加载完成的过程

Date

  • 2021/04/10

Prepared By

  • Forrest

XMind - Trial Version

文章分类
前端
文章标签