# 从输入url到页面展示到底发生了什么
# 主要过程整理
- 输入地址
- 浏览器查找域名的 IP 地址(DNS 查询:具体过程包括浏览器搜索自身的 DNS 缓存、搜索操作系统的 DNS 缓存、读取本地的 Host 文件和向本地 DNS 服务器进行查询等)
- 浏览器获得域名对应的 IP 地址以后,浏览器向服务器请求建立链接,发起三次握手
- TCP/IP 链接建立起来后,浏览器向服务器发送 HTTP 请求
- 服务器的永久重定向响应
- 浏览器跟踪重定向地址
- 服务器处理请求
- 服务器返回一个 HTTP 响应
- 浏览器获取 HTML 并解析,同时发送请求获取嵌入在 HTML 中的资源(如图片、音频、视频、CSS、JS等等)
# 1. 输入地址
当我们开始在浏览器中输入网址的时候,浏览器其实就已经在智能的匹配可能得 url 了,他会从历史记录,书签等地方,找到已经输入的字符串可能对应的 url,然后给出智能提示,让你可以补全url地址。对于 chrome 浏览器,他甚至会直接从缓存中把网页展示出来。
# 2. 浏览器查找域名的 IP 地址
请求一旦发起,浏览器首先要做的事情就是解析这个域名,一般来说,整体查找步骤如下:
- 浏览器缓存:当用户通过浏览器访问某域名时,浏览器首先会在自己的缓存中查找是否有该域名对应的IP地址(若曾经访问过该域名且没有清空缓存便存在);
- 系统缓存(host): 当浏览器缓存中无域名对应IP则会自动检查用户计算机系统Hosts文件DNS缓存是否有该域名对应IP;
- 路由器缓存(局域网): 当浏览器及系统缓存中均无域名对应IP则进入路由器缓存中检查,以上三步均为客户端的DNS缓存;
- ISP(互联网服务提供商)DNS缓存(电信、移动): 当在用户客服端查找不到域名对应IP地址,则将进入ISP DNS缓存中进行查询。比如你用的是电信的网络,则会进入电信的DNS缓存服务器中进行查找;(或者向网络设置中指定的local DNS进行查询,如果在PC指定了DNS的话,如果没有设置比如DNS动态获取,则向ISP DNS发起查询请求)
- 根域名服务器: 当以上均未完成,则进入根服务器进行查询。全球仅有13台根域名服务器,1个主根域名服务器,其余12为辅根域名服务器。根域名收到请求后会查看区域文件记录,若无则将其管辖范围内顶级域名(如.com)服务器IP告诉本地DNS服务器;
- 顶级域名服务器: 顶级域名服务器收到请求后查看区域文件记录,若无则将其管辖范围内主域名服务器的IP地址告诉本地DNS服务器;
- 主域名服务器: 主域名服务器接受到请求后查询自己的缓存,如果没有则进入下一级域名服务器进行查找,并重复该步骤直至找到正确记录;
- 保存结果至缓存: 本地域名服务器把返回的结果保存到缓存,以备下一次使用,同时将该结果反馈给客户端,客户端通过这个IP地址与web服务器建立链接。
# 知识扩展
什么是 DNS?
DNS(Domain Name System,域名系统),因特网上作为域名和 IP 地址相互映射的一个分布式数据库,能够使用户更方便的访问互联网,而不用去记住能够被机器直接读取的 IP 数串。通过主机名,最终得到该主机名对应的 IP 地址的过程叫做域名解析(或主机名解析)。
通俗的讲,我们更习惯于记住一个网站的名字,比如 www.baidu.com,而不是记住它的 ip 地址,比如:167.23.10.2。而计算机更擅长记住网站的 ip 地址,而不是像 www.baidu.com 等链接。因为,DNS 就相当于一个电话本,比如你要找 www.baidu.com 这个域名,那我翻一翻我的电话本,我就知道,哦,它的电话(ip)是167.23.10.2。
DNS查询的两种方式:递归查询和迭代查询
当局部 DNS 服务器(局域网的 DNS 服务器)自己不能回答客户机的 DNS 查询时,它就需要向其他 DNS 服务器进行查询。此时有两种方式:
递归方式:局部 DNS 服务器自己负责向其他 DNS 服务器进行查询,一般是先向该域名的根域服务器查询,再由根域名服务器一级级向下查询。最后得到的查询结果返回给局部 DNS 服务器,再由局部 DNS 服务器返回给客户端。
迭代查询:局部 DNS 服务器不是自己向其他 DNS 服务器进行查询,而是把能解析该域名的其他 DNS 服务器的 IP 地址返回给客户端 DNS 程序,客户端 DNS 程序再继续向这些 DNS 服务器进行查询,直到得到查询结果为止。也就是说,迭代解析只是帮你找到相关的服务器而已,而不会帮你去查。比如说: baidu.com 的服务器ip地址在 192.168.4.5 这里,你自己去查吧,本人比较忙,只能帮你到这里了。
DNS 域名称空间的组织方式
按其功能命名空间中用来描述 DNS 域名称的五个类别的介绍详见下表中,以及与每个名称类型的示例:
说明 | 示例 | |
---|---|---|
根域 | 这是表示未命名的等级; 目录树的顶部它有时显示为两个空引号 (""),表示空值。 DNS 域名中使用时,它规定由尾部句点 (.) 来指定名称位于根或更高级别的域层次结构。 在此情况下,DNS 域名被认为是完整和点到准确的位置在树中的名称。 名称声明这种方式的 Fqdn。 | 单个句点 (.) 或句点用于末尾的名称,如"example.microsoft.com"。 |
顶级域 | 用来指示某个国家/地区或组织使用的名称的类型名称。 | ".com",表示一个名称注册为在 Internet 上的商业使用的业务。 |
第二层域 | 可变长度的个体或组织,以便在 Internet 上使用的注册名称。 这些名称始终基于相应的顶级域,具体取决于组织或地理位置名称使用的位置的类型。 | "microsoft.com。 ",这是由 Internet DNS 域的名称注册向 Microsoft 注册的二级域名称。 |
子域 | 其他名称的组织可以创建从已注册的二级域名派生的。 这些功能包括添加到组织中的名称的 DNS 树的增长,并将其分为部门或地理位置的名称。 | "example.microsoft.com"。 ",这是由 Microsoft 指定文档名称示例中用于虚构子域。 |
主机或资源名称 | 代表名称的 DNS 树的叶节点并标识特定的资源的名称。 通常情况下,DNS 域名的最左侧的标签标识网络上的特定计算机。 例如,如果主机 (A) 资源记录中使用此级别的名称,则它用于查找基于其主机名的计算机的 IP 地址。 | "主机-a.example.microsoft.com。",其中第一个标签 ("主机-a") 是网络上的特定计算机的 DNS 主机名。 |
DNS 负载均衡
当一个网站有足够多的用户的时候,假如每次请求的资源都位于同一台机器上面,那么这台机器随时可能会蹦掉。处理办法就是用 DNS 负载均衡技术,它的原理是在 DNS 服务器中为同一个主机名配置多个 IP 地址,在应答 DNS 查询时,DNS 服务器对每个查询将以 DNS 文件中主机记录的 IP 地址按顺序返回不同的解析结果,将客户端的访问引导到不同的机器上去,使得不同的客户端访问不同的服务器,从而达到负载均衡的目的。例如可以根据每台机器的负载量,该机器离用户地理位置的距离等等。
# 3. 浏览器获得域名对应的 IP 地址以后,浏览器向服务器请求建立链接,发起三次握手
拿到域名对应的 IP 地址之后,浏览器会以一个随机端口(1024<端口<65535)向服务器的 WEB 程序(常用的有 httpd,nginx 等)80 端口发起 TCP 的连接请求。这个连接请求到达服务器端后(这中间通过各种路由设备,局域网内除外),进入到网卡,然后是进入到内核的 TCP/IP 协议栈(用于识别该连接请求,解封包,一层一层的剥开),还有可能要经过 Netfilter 防火墙(属于内核的模块)的过滤,最终到达 WEB 程序,最终建立了 TCP/IP 的连接。(如果是 HTTPS 还需要建立 TLS 连接)
# 知识扩展
TCP三次握手和四次挥手
关于 TCP 三次握手和四次挥手请查看 TCP三次握手和四次挥手 (opens new window)
为什么需要三次握手呢?两次不行吗?
为什么连接的时候是三次握手,关闭的时候却是四次握手?
为什么 TIME_WAIT 状态需要经过 2MSL(最大报文段生存时间)才能返回到CLOSE状态?
以上三个问题请查看上一篇文章 HTTP精选面试题 (opens new window)
# 4. TCP/IP 链接建立起来后,浏览器向服务器发送 HTTP 请求
建立了 TCP 连接之后,发起一个 HTTP 请求。一个典型的 http request header 一般需要包括请求的方法,例如 GET 或者 POST 等,不常用的还有 PUT 和 DELETE 、HEAD、OPTION 以及 TRACE 方法。
发送请求时会发送请求报文,其由请求行(含请求方法、请求URI、协议版本)、报文首部和报文实体构成的,且报文首部和报文实体以一空行(CR+LF)分隔。
更多内容请查看HTTP知识整合-HTTP报文 (opens new window)
# 5. 服务器的永久重定向响应
服务器给浏览器响应一个 301 永久重定向响应,这样浏览器就会访问 “http://www.google.com/” 而非 “http://google.com/”。
为什么服务器一定要重定向而不是直接发送用户想看的网页内容呢?其中一个原因跟搜索引擎排名有关。如果一个页面有两个地址,就像 http://www.yy.com /和 http://yy.com/,搜索引擎会认为它们是两个网站,结果造成每个搜索链接都减少从而降低排名。而搜索引擎知道 301 永久重定向是什么意思,这样就会把访问带 www 的和不带 www 的地址归到同一个网站排名下。还有就是用不同的地址会造成缓存友好性变差,当一个页面有好几个名字时,它可能会在缓存里出现好几次。
# 知识扩展
301和302的区别
301和302状态码都表示重定向,就是说浏览器在拿到服务器返回的这个状态码后会自动跳转到一个新的 URL 地址,这个地址可以从响应的 Location 首部中获取(用户看到的效果就是他输入的地址A瞬间变成了另一个地址B)——这是它们的共同点。
他们的不同在于。301 表示旧地址A的资源已经被永久地移除了(这个资源不可访问了),搜索引擎在抓取新内容的同时也将旧的网址交换为重定向之后的网址;
SEO 301 好于 302,如果要将内容永久移动到新位置,请使用 301 重定向。如果要暂时移动它,请使用 302 重定向。
重定向原因
- 网站调整(如改变网页目录结构);
- 网页被移到一个新地址;
- 网页扩展名改变(如应用需要把.php改成.Html或.shtml)。
这种情况下,如果不做重定向,则用户收藏夹或搜索引擎数据库中旧地址只能让访问客户得到一个 404 页面错误信息,访问流量白白丧失;再者某些注册了多个域名的网站,也需要通过重定向让访问这些域名的用户自动跳转到主站点等。
# 6. 浏览器跟踪重定向地址
现在浏览器知道了 "http://www.google.com/" 才是要访问的正确地址,所以它会发送另一个 http 请求。
# 7. 服务器处理请求
经过前面的重重步骤,我们终于将我们的 http 请求发送到了服务器这里,其实前面的重定向已经是到达服务器了,那么,服务器是如何处理我们的请求的呢?
后端从在固定的端口接收到 TCP 报文开始,它会对 TCP 连接进行处理,对 HTTP 协议进行解析,并按照报文格式进一步封装成 HTTP Request 对象,供上层使用。
一些大一点的网站会将你的请求到反向代理服务器中,因为当网站访问量非常大,网站越来越慢,一台服务器已经不够用了。于是将同一个应用部署在多台服务器上,将大量用户的请求分配给多台机器处理。此时,客户端不是直接通过 HTTP 协议访问某网站应用服务器,而是先请求到 Nginx 代理服务器,Nginx 再请求应用服务器,然后将结果返回给客户端,这里 Nginx 的作用是提供反向代理功能。同时也带来了一个好处,其中一台服务器万一挂了,只要还有其他服务器正常运行,就不会影响用户使用。
# 知识拓展
关于 Nginx 的使用和功能,请看 nginx 最全操作总结 (opens new window)
# 8. 服务器返回一个 HTTP 响应
经过前面的7个步骤,服务器收到了我们的请求,也处理我们的请求,到这一步,它会把它的处理结果返回,也就是返回一个 HTTP 响应。
HTTP 响应与 HTTP 请求相似,HTTP 响应也由3个部分构成,分别是:
- 状态行
- 响应头(Response Header)
- 响应正文
HTTP 请求需要的内容就放在响应正文,即响应体中
更多内容请查看HTTP知识整合-HTTP报文 (opens new window)
# 9. 浏览器获取 HTML 并解析,解析过程中同时发送请求获取嵌入在 HTML 中的资源(如图片、音频、视频、CSS、JS等等)
# 解析 HTML
在浏览器没有完整接受全部 HTML 文档时,它就已经开始显示这个页面了,浏览器是如何把页面呈现在屏幕上的呢?
解析 html 以构建 dom 树(同时解析 CSS 构建 style Rules) -> 构建render 树(2016删除) -> 布局 render 树 -> 构建图层树 -> 绘制渲染
浏览器在解析html文件时,会”自上而下“加载,并在加载过程中进行解析渲染。在解析过程中,如果遇到请求外部资源时,如图片、外链的CSS、iconfont等,请求过程是异步的,并不会影响html文档进行加载。
解析过程中,浏览器首先会解析HTML文件构建DOM树,然后解析CSS文件构建渲染树,等到渲染树构建完成后,浏览器开始布局渲染树并将其绘制到屏幕上。这个过程比较复杂,涉及到两个概念: reflow(回流)和repain(重绘)。
绘制渲染过程:
- 建立图层树
- 生成绘制列表
- 生产图块并栅格化
- 显示器显示内容
DOM 节点中的各个元素都是以盒模型的形式存在,这些都需要浏览器去计算其位置和大小等,这个过程称为 relow;当盒模型的位置,大小以及其他属性,如颜色,字体,等确定下来之后,浏览器便开始绘制内容,这个过程称为 repain。
页面在首次加载时必然会经历 reflow 和 repain。reflow 和 repain过程是非常消耗性能的,尤其是在移动设备上,它会破坏用户体验,有时会造成页面卡顿。所以我们应该尽可能少的减少 reflow 和 repain。
当文档加载过程中遇到 js 文件,html 文档会挂起渲染(加载解析渲染同步)的线程,不仅要等待文档中 js 文件加载完毕,还要等待解析执行完毕,才可以恢复 html 文档的渲染线程。因为 JS 有可能会修改 DOM,最为经典的document.write,这意味着,在 JS 执行完成前,后续所有资源的下载可能是没有必要的,这是 js 阻塞后续资源下载的根本原因。所以我明平时的代码中,js 是放在 html 文档末尾的。
JS的解析是由浏览器中的 JS 解析引擎完成的,比如谷歌的是 V8。JS 是单线程运行,也就是说,在同一个时间内只能做一件事,所有的任务都需要排队,前一个任务结束,后一个任务才能开始。但是又存在某些任务比较耗时,如 IO 读写等,所以需要一种机制可以先执行排在后面的任务,这就是:同步任务(synchronous)和异步任务(asynchronous)。
JS的执行机制就可以看做是一个主线程加上一个任务队列(task queue)。同步任务就是放在主线程上执行的任务,异步任务是放在任务队列中的任务。所有的同步任务在主线程上执行,形成一个执行栈;异步任务有了运行结果就会在任务队列中放置一个事件;脚本运行时先依次运行执行栈,然后会从任务队列里提取事件,运行任务队列中的任务,这个过程是不断重复的,所以又叫做事件循环(Event loop)。
# 获取其它资源并解析
在浏览器显示 HTML 时,它会注意到需要获取其他地址内容的标签。这时,浏览器会发送一个获取请求来重新获得这些文件。比如我要获取外图片,CSS,JS文件等。
这些地址都要经历一个和 HTML 读取类似的过程。所以浏览器会在 DNS 中查找这些域名,发送请求,重定向等等...
不像动态页面,静态文件会允许浏览器对其进行缓存。有的文件可能会不需要与服务器通讯,而从缓存中直接读取,或者可以放到 CDN 中。
本文主要对 老生常谈-从输入url到页面展示到底发生了什么 (opens new window) 进行的修改完善,记录在这里也方便查阅和分享给大家。