经典问题总结--从输入url到显示出页面发生了什么

Posted by liveipool on August 24, 2017

经典问题总结–从输入url到显示出页面发生了什么

  1. DNS解析
  2. 服务器的永久重定向响应
  3. TCP连接
  4. HTTP请求和响应
  5. 浏览器解析渲染页面

DNS解析

人们习惯记忆域名,但机器间互相只认IP地址,一个ip地址不一定只对应一个域名,且一个域名只可以对应一个ip地址,它们之间的转换工作称为域名解析,域名解析需要由专门的域名解析服务器(Domain Name Server–DNS)来完成。

DNS解析过程.png

解析过程:本地DNS服务器(一般是指ISP提供的服务器,如我们的中国电信之类的)–》根域名服务器(全球13套或更多,是套不是个)–》顶级域名服务器–》权威域名服务器–》本地域名服务器–》用户主机

事实上,真正的网址是www.google.com.,并不是多打了一个.,这个.对应的就是根域名服务器,默认情况下所有的网址的最后一位都是.,既然是默认情况下,为了方便用户,通常都会省略,浏览器在请求DNS的时候会自动加上,所有网址真正的解析过程为: . -> .com. -> google.com. -> www.google.com.。

如果想要对用户的DNS解析进行优化,需要进行DNS缓存设置:DNS存在着多级缓存,从离浏览器的距离排序的话,有以下几种: 浏览器缓存,系统缓存(如本地的hosts文件),路由器缓存,ISP服务器缓存,根域名服务器缓存,顶级域名服务器缓存,主域名服务器缓存。

在你的chrome浏览器中输入:chrome://dns/ ,你可以看到chrome浏览器的DNS缓存。 系统缓存主要存在本地的某些文件,如/etc/hosts(Linux系统)中。

DNS负载均衡

DNS返回的IP地址并非每次都一样:如果每次都一样是否说明你请求的资源都位于同一台机器上面,那么这台机器需要多高的性能和储存才能满足亿万请求呢?其实真实的互联网世界背后存在成千上百台服务器,大型的网站甚至更多。但是在用户的眼中,它需要的只是处理他的请求,哪台机器处理请求并不重要。DNS可以返回一个合适的机器的IP给用户,例如可以根据每台机器的负载量,该机器离用户地理位置的距离等等,尽可能保证你所访问的入口是所有入口中可能较快的一个,这种过程就是DNS负载均衡,又叫做DNS重定向。

服务器的永久重定向响应

服务器给浏览器响应一个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的资源已经被永久地移除了(这个资源不可访问了),搜索引擎在抓取新内容的同时也将旧的网址交换为重定向之后的网址

302表示旧地址A的资源还在(仍然可以访问),这个重定向只是临时地从旧地址A跳转到地址B,搜索引擎会抓取新的内容而保存旧的网址

当一个网站或者网页24—48小时内临时移动到一个新的位置,这时候就要进行302跳转,而使用301跳转的场景就是之前的网站因为某种原因需要移除掉,然后要到新的地址访问,是永久性的。

浏览器跟踪重定向地址

现在浏览器知道了 “http://www.google.com/”才是要访问的正确地址,所以它会发送另一个http请求。

TCP连接

三次握手,四次挥手…

HTTP请求和响应

发送HTTP请求的过程就是构建HTTP请求报文并通过TCP协议中发送到服务器指定端口(HTTP协议80, HTTPS协议443)。

HTTP请求报文

这部分又可以称为前端工程师眼中的HTTP。 HTTP请求报文是由三部分组成: 请求行, 首部行和实体

// 请求行
Method Request-URL HTTP-Version
GET index.html HTTP/1.1
// 首部行
首部行允许客户端向服务器传递请求的附加信息和客户端自身的信息。(客户端不一定特指浏览器,有时候也可使用Linux下的CURL命令以及HTTP客户端测试工具等。)
// 实体
当使用POST, PUT等方法时,通常需要客户端向服务器传递数据。这些数据就储存在实体中。在首部行中有一些与实体相关的信息,例如: 现在的Web应用通常采用Rest架构,请求的数据格式一般为json。这时就需要设置Content-Type: application/json。
// 例
GET index.html HTTP/1.1   
Accept-Language:zh-cn   
Connection:Keep-Alive   
Host:localhost   
User-Agent:Mozila/4.0(compatible;MSIE5.01;Window NT5.0)   
Accept-Encoding:gzip,deflate   

username=jinqiao&password=1234

注意:最后一个请求头之后是一个空行,发送回车符和换行符,通知服务器以下不再有请求头。

HTTP响应报文

这部分又可以称为后端工程师眼中的HTTP。后端从在固定的端口接收到TCP报文开始,这一部分对应于编程语言中的socket。它会对TCP连接进行处理,对HTTP协议进行解析,并按照报文格式进一步封装成HTTP Request对象,供上层使用。这一部分工作一般是由Web服务器去进行。 HTTP响应报文也是由三部分组成: 状态行, 首部行和实体

状态码:

  • 1xx:Information
  • 2xx:Success
  • 3xx:Redirection
  • 4xx:You fucked up
  • 5xx:I fucked up

响应报头:一些附加信息(待补充) 响应报文:服务器返回给浏览器的文本信息,通常HTML, CSS, JS, 图片等文件就放在这一部分。

一些常见的状态码总结可参考:易忘的点

服务器处理请求

在服务器发送回客户端HTTP响应报文之前,服务器是进行了很多的处理的,如:

一些大一点的网站会将你的请求到反向代理服务器中,因为当网站访问量非常大,网站越来越慢,一台服务器已经不够用了。于是将同一个应用部署在多台服务器上,将大量用户的请求分配给多台机器处理。此时,客户端不是直接通过HTTP协议访问某网站应用服务器,而是先请求到Nginx,Nginx再请求应用服务器,然后将结果返回给客户端,这里Nginx的作用是反向代理服务器。同时也带来了一个好处,其中一台服务器万一挂了,只要还有其他服务器正常运行,就不会影响用户使用。

image.png

将资源分布在多个域名下

下一步浏览器会去加载页面中用到的css、js、图片等样式、脚本和资源文件。但是可能相对较少的同学才会知道,你的浏览器在同一个域名下并发加载的资源数量是有限制的,例如ie6-7是两个,ie8是6个,chrome各版本不大一样,一般是4-6个。我刚刚看了一下,我访问淘宝网首页需要加载126个资源,那么如此小的并发连接数自然会加载很久。所以前端开发人员往往会将上述这些资源文件分布在好多个域名下,变相的绕过浏览器的这个限制,这也为后面提到的CDN打下了基础。

CDN,Content Delivery Network,即内容分发网络的作用。淘宝在全国各地建立了数十上百个CDN节点,利用一些手段保证你访问的(这里主要指js、css、图片等)地方是离你最近的CDN节点,这样便保证了大流量分散已经在各地访问的加速。这便出现了一个问题,那就是假若一个卖家发布了一个新的宝贝,上传了几张新的宝贝图片,那么淘宝网如何保证全国各地的CDN节点中都会同步的存在这几张图片供用户使用呢?这里边就涉及到了大量的内容分发与同步的相关技术。淘宝开发了分布式文件系统TFS(taobao file system)来处理这类问题。

浏览器解析渲染页面

浏览器解析渲染页面过程.png

在讨论页面重绘、回流之前。需要对页面的呈现流程有些了解,页面是怎么把html结合css等显示到浏览器上的,下面的流程图显示了浏览器对页面的呈现的处理流程。可能不同的浏览器略微会有些不同。但基本上都是类似的。

  1. 浏览器把获取到的HTML代码解析成1个DOM树,HTML中的每个tag都是DOM树中的1个节点,根节点就是我们常用的document对象。DOM树里包含了所有HTML标签,包括display:none隐藏,还有用JS动态添加的元素等。

  2. 浏览器把所有样式(用户定义的CSS和用户代理)解析成样式结构体,在解析的过程中会去掉浏览器不能识别的样式,比如IE会去掉-moz开头的样式,而FF会去掉_开头的样式。

  3. DOM Tree 和样式结构体组合后构建render tree, render tree类似于DOM tree,但区别很大,render tree能识别样式,render tree中每个NODE都有自己的style,而且 render tree不包含隐藏的节点 (比如display:none的节点,还有head节点),因为这些节点不会用于呈现,而且不会影响呈现的,所以就不会包含到 render tree中。注意 visibility:hidden隐藏的元素还是会包含到 render tree中的,因为visibility:hidden 会影响布局(layout),会占有空间。根据CSS2的标准,render tree中的每个节点都称为Box (Box dimensions),理解页面元素为一个具有填充、边距、边框和位置的盒子。

  4. 一旦render tree构建完毕后,浏览器就可以根据render tree来绘制页面了。

回流(reflow、重排)

当render tree中的一部分(或全部)因为元素的规模尺寸,布局,隐藏等改变而需要重新构建。这就称为回流(reflow)。每个页面至少需要一次回流,就是在页面第一次加载的时候。在回流的时候,浏览器会使渲染树中受到影响的部分失效,并重新构造这部分渲染树,完成回流后,浏览器会重新绘制受影响的部分到屏幕中,该过程成为重绘。

重绘(repaint)

当render tree中的一些元素需要更新属性,而这些属性只是影响元素的外观,风格,而不会影响布局的,比如background-color。则就叫称为重绘。

回流必将引起重绘,而重绘不一定会引起回流。

回流和重绘的优化

如果每句与样式相关的JS操作都去回流重绘的话,浏览器可能就会受不了。所以很多浏览器都会优化这些操作,浏览器会维护1个队列,把所有会引起回流、重绘的操作放入这个队列,等队列中的操作到了一定的数量或者到了一定的时间间隔,浏览器就会flush队列,进行一个批处理。这样就会让多次的回流、重绘变成一次回流重绘。

虽然有了浏览器的优化,但有时候我们写的一些代码可能会强制浏览器提前flush队列,这样浏览器的优化可能就起不到作用了。当你请求向浏览器请求一些 style信息的时候,就会让浏览器flush队列,比如:

  1. offsetTop, offsetLeft, offsetWidth, offsetHeight

  2. scrollTop/Left/Width/Height

  3. clientTop/Left/Width/Height

  4. width,height

  5. 请求了getComputedStyle(), 或者 IE的 currentStyle

当你请求上面的一些属性的时候,浏览器为了给你最精确的值,需要flush队列,因为队列中可能会有影响到这些值的操作。即使你获取元素的布局和样式信息跟最近发生或改变的布局信息无关,浏览器都会强行刷新渲染队列。

因此需要减少访问会引起浏览器flush队列的属性,如果你确实要访问,利用缓存(用变量将这些属性存下来再操作)

js解析

js解析.png JS的解析是由浏览器中的JS解析引擎完成的。JS是单线程运行,也就是说,在同一个时间内只能做一件事,所有的任务都需要排队,前一个任务结束,后一个任务才能开始。但是又存在某些任务比较耗时,如IO读写等,所以需要一种机制可以先执行排在后面的任务,这就是:同步任务(synchronous)和异步任务(asynchronous)。JS的执行机制就可以看做是一个主线程加上一个任务队列(task queue)。同步任务就是放在主线程上执行的任务,异步任务是放在任务队列中的任务。所有的同步任务在主线程上执行,形成一个执行栈;异步任务有了运行结果就会在任务队列中放置一个事件;脚本运行时先依次运行执行栈,然后会从任务队列里提取事件,运行任务队列中的任务,这个过程是不断重复的,所以又叫做事件循环(Event loop)。

参考

前端经典面试题: 从输入URL到页面加载发生了什么?

页面重绘和回流以及优化

从输入 URL 到页面展示到底发生了什么

赞赏码.jpeg