我们以在Firefox浏览器中输入zhihu.com网址为例深入解剖这个过程。首先,为火狐浏览器添加一个插件Firebug后重启,按F12键得一控制台,输入地址zhihu.com,点击其中的网络栏发现从输入网址到页面加载完毕共用时间4.2s,如图1所示。那么这4.2s都发生了些什么?
1.浏览器发起DNS查询请求
在广域网中,我们是基于IP地址进行通信的,IP是形如 w.x.y.z的数字,四个数字都是0~255(比如192.168.0.1),也就是四个字节。在此,我们访问的是网址zhihu.com,为此,我们需要利用域名服务系统得到知乎网对应的IP地址。DNS查找过程如下:
* 浏览器缓存 – 浏览器会缓存DNS记录一段时间。 火狐的DNS缓存时间一般为一分钟。
* 系统缓存 – 如果在浏览器缓存里没有找到需要的记录,浏览器会做一个系统调用。这样便可获得系统缓存中的记录。
* 路由器缓存 – 系统缓存中若仍没有记录,查询请求将发向路由器,它一般会有自己的DNS缓存。
* ISP DNS 缓存 – 最后要检查的是ISP缓存DNS的服务器。在这一般都能找到相应的缓存记录。
* 递归搜索 – ISP的DNS服务器从跟域名服务器开始进行递归搜索,从.com顶级域名服务器到域名服务器。一般DNS服务器的缓存中会 有.com域名服务器中的域名,所以到顶级服务器的匹配过程不是那么必要了。域名服务器向客户端返回查询结果域名,从而完成域名到IP地址的转换。
由图2分析各事件的域名解析时间我们会发现打开知乎花费在域名解析上的时间大概为17ms。
2.连接服务器
在客户端获得知乎网对应的IP地址后,我们将采用TCP/IP的 三次握手协议与服务器建立连接,首先客户端将转换得到的IP地址发送一个连接建立的请求给服务器,服务器接收到请求返回一个确认,客户端得到确认后再次发送确认,由此连接建立成功。如果已经有了被保持的连接,则复用此连接。
分析各事件的建立连接时间我们会发现打开知乎花费在建立连接上的时间大概为35ms。
3.客户端向web服务器发送HTTP请求
建立连接以后,客户端便可以向有着知乎IP的web服务器发送HTTP请求了。HTTP请求是一个基于TCP协议之上的应用层协议——超文本传输协议。请求信息包含一个响应头信息和一个请求头信息,一般的web技术都会把请求进行封装然后交给服务器进行处理,比如servlet会把请求封装成httpservletrequest对象,把响应封装成httpsevletresponse对象。Node.js(简单来说就是运行在服务端的JavaScript)的http模块,当你创建服务器的时候会写一个回调函数,回调的参数用来接受http请求对象和响应对象,然后在回调函数中对请求进行处理。
GET 这个请求定义了要读取的URL。如图3所示,请求头信息主要包括了 浏览器自身定义 (User-Agent 头),和它希望接受什么类型的相应 (Accept and Accept-Encoding 头)。Connection头要求服务器为了后边的请求不要关闭TCP连接。请求头信息中也包含浏览器存储的该域名的cookies。在不同页面请求当中,cookies是与跟踪一个网站状态相匹配的键值。这样cookies会存储登录用户名,服务器分配的密码和一些用户设置等。Cookies会以文本文档形式存储在客户机里,每次请求时发送给服务器。
一般接触到的响应头信息就是设置content-type(内容类型,用于定义网络文件的类型和网页的编码,决定浏览器将以什么形式、什么编码读取这个文件)来决定MIME类型(服务器要将发送的多媒体数据类型告诉浏览器,而通知手段就是说明该多媒体数据的MIME类型,从而让浏览器知道接收到的信息哪些是MP3文件,哪些是Shockwave文件等。),设置Cache-Control,last-modify等缓存内容。一般来说返回给客户端的内容是一个html字符串,如上图所示,content-type被设定为text/html。当然也可能客户端请求的是一个image文件,那么就是读取image文件后,content-type可能设为image/png,image/jpg等,如图5中打开知乎网的六个图片请求然后把内容返回给客户端。这样一次对请求的处理就结束了。
图6中紫色部分代表等待响应的时间,由此可发现所有文件发送请求并得到响应这个过程花费了将近2.32s。
4.客户端渲染
客户端接收到服务器传来的响应对象,从中得到html字符串和MIME,根据MIME知道了要用页面渲染引擎来处理内容即html字符串,于是进入页面渲染阶段,这是一个很庞杂的体系。从浏览器的角度讲,浏览器包含几大组件,网络功能是其中之一,渲染引擎也是其中之一,还有其它的一些比如自己UI界面,javascript解释器,客户端数据存储等等,在这里我们主要关注渲染引擎和javascript解释器。我们能够在浏览器中看到一个页面,那么这个页面是怎么出现的呢?实际上就是调用底层绘图API给画出来的。不同的渲染引擎,Firefox的渲染引擎为Gecko。
整体上页面渲染的过程大致是这样的:
渲染引擎得到HTML字符串作为输入,然后对HTML进行转换,转化成能够被DOM处理的形式。DOM,文档对象模型,DOM可以以一种独立于平台和语言的方式访问和修改一个文档的内容和结构,是表示和处理一个HTML文档的常用方法。此外,DOM的设计是以对象管理组织(OMG)的规约为基础的,因此可以用于任何编程语言。最初人们把它认为是一种让JavaScript在浏览器间可移植的方法,不过DOM的应用已经远远超出这个范围。DOM技术使得用户页面可以动态地变化,如可以动态地显示或隐藏一个元素,改变它们的属性,增加一个元素等,DOM技术使得页面的交互性大大地增强。在HTML转换成能被DOM处理的形式后,接着转换成一个DOM树,在解析html的过程,解析到<link>,<script>,<img>等一些请求标签时,会发送请求把对应的内容获取到。这时又会同步进行CSS的解析,计算出最终的样式数据,对CSS代码中非法的语法她会直接忽略掉。解析CSS的时候会按照如下顺序来定义优先级:浏览器默认设置,用户设置,外链样式,内联样式,HTML中的style。构建出CSS样式规则应用到DOM树上,然后进行一定的布局处理,比如标记节点块在浏览器中的坐标等形成最终的渲染树,最后根据这棵渲染树在浏览器窗口中进行绘制,最终我们就看到了页面的样子。当然在页面渲染过程中还会同步进行javascript的解析,而且这两者是在同一个线程中的,所以一旦javascript死循环,页面的渲染也就进行进行不下去了。我们可以看到打开知乎网页需要处理HTML,CSS,JavaScript,XHR,图片,剩余的2s便是在进行页面的渲染。
点击Firebug控制台左上角的箭头会发现页面中的每一块内容在HTML中都有对应的语句,如图7所示。