总体来说分为以下几个过程:
-
DNS
解析 -
TCP
连接 - 发送
HTTP
请求 - 服务器处理请求并返回
HTTP
报文 - 浏览器解析渲染页面
- 连接结束
具体过程
DNS解析
DNS
解析的过程就是寻找哪台机器上有你需要资源的过程。当你在浏览器中输入一个地址时,例如www.baidu.com
,其实不是百度网站真正意义上的地址。互联网上每一台计算机的唯一标识是它的IP
地址,但是IP
地址并不方便记忆。用户更喜欢用方便记忆的网址去寻找互联网上的其它计算机,也就是上面提到的百度的网址。
所以互联网设计者需要在用户的方便性与可用性方面做一个权衡,这个权衡就是一个网址到IP地址的转换,这个过程就是DNS解析。它实际上充当了一个翻译的角色,实现了网址到IP
地址的转换。网址到IP
地址转换的过程是如何进行的?
解析过程
DNS
解析是一个递归查询的过程。
首先在本地域名服务器中查询IP
地址,如果没有找到的情况下,本地域名服务器会向根域名服务器发送一个请求,如果根域名服务器也不存在该域名时,本地域名会向com
顶级域名服务器发送一个请求,依次类推下去。直到最后本地域名服务器得到google
的IP地址并把它缓存到本地,供下次查询使用。
从上述过程中,可以看出网址的解析是一个从右向左的过程: com
-> google.com
-> www.google.com
。但是你是否发现少了点什么,根域名服务器的解析过程呢?
事实上,真正的网址是www.google.com.
,并不是我多打了一个.
,这个.
对应的就是根域名服务器,默认情况下所有的网址的最后一位都是.
,既然是默认情况下,为了方便用户,通常都会省略,浏览器在请求DNS
的时候会自动加上,所有网址真正的解析过程为: .
-> .com
-> google.com.
-> www.google.com.
。
DNS优化
了解了DNS
的过程,可以为我们带来哪些?
上文中请求到google
的IP地址时,经历了8个步骤,这个过程中存在多个请求(同时存在UDP
和TCP
请求,为什么有两种请求方式,请自行查找)。如果每次都经过这么多步骤,是否太耗时间?如何减少该过程的步骤呢?那就是DNS缓存。
DNS缓存
DNS
存在着多级缓存,从离浏览器的距离排序的话,有以下几种: 浏览器缓存,系统缓存,路由器缓存,IPS服务器缓存,根域名服务器缓存,顶级域名服务器缓存,主域名服务器缓存**。
在你的chrome
浏览器中输入:chrome://dns/
,你可以看到chrome
浏览器的DNS
缓存。
系统缓存主要存在/etc/hosts
(Linux
系统)中。
DNS负载均衡
不知道大家有没有思考过一个问题: DNS
返回的IP地址是否每次都一样?
如果每次都一样是否说明你请求的资源都位于同一台机器上面,那么这台机器需要多高的性能和储存才能满足亿万请求呢?其实真实的互联网世界背后存在成千上百台服务器,大型的网站甚至更多。但是在用户的眼中,它需要的只是处理他的请求,哪台机器处理请求并不重要。
DNS
可以返回一个合适的机器的IP
给用户,例如可以根据每台机器的负载量,该机器离用户地理位置的距离等等,这种过程就是DNS负载均衡,又叫做DNS重定向。
大家耳熟能详的CDN
(Content Delivery Network
)就是利用DNS
的重定向技术,DNS
服务器会返回一个跟用户最接近的点的IP
地址给用户,CDN
节点的服务器负责响应用户的请求,提供所需的内容。
TCP连接
HTTP
协议是使用TCP
作为其传输层协议的,当TCP
出现瓶颈时,HTTP
也会受到影响。但由于TCP
优化这一块我平常接触的并不是很多,再加上大学时的计算机网络的基础基本上忘完,所以这一部分我也就不在这里分析了。
HTTPS协议
我不知道把HTTPS
放在这个部分是否合适,但是放在这里好像又说的过去。HTTP
报文是包裹在TCP
报文中发送的,服务器端收到TCP
报文时会解包提取出HTTP
报文。但是这个过程中存在一定的风险,HTTP
报文是明文,如果中间被截取的话会存在一些信息泄露的风险。那么在进入TCP
报文之前对HTTP
做一次加密就可以解决这个问题了。
HTTPS
协议的本质就是HTTP
+ SSL
(or TLS
)。在HTTP
报文进入TCP
报文之前,先使用SSL
对HTTP
报文进行加密。从网络的层级结构看它位于HTTP
协议与TCP
协议之间。
HTTPS过程
HTTPS
在传输数据之前需要客户端与服务器进行一个握手(TLS/SSL
握手),在握手过程中将确立双方加密传输数据的密码信息。TLS/SSL
使用了非对称加密,对称加密以及hash
等。
HTTPS
相比于HTTP
,虽然提供了安全保证,但是势必会带来一些时间上的损耗,如握手和加密等过程,是否使用HTTPS
需要根据具体情况在安全和性能方面做出权衡。
HTTP请求
其实这部分又可以称为前端工程师眼中的HTTP
,它主要发生在客户端。发送HTTP
请求的过程就是构建HTTP
请求报文并通过TCP
协议中发送到服务器指定端口(HTTP
协议80/8080
, HTTPS
协议443
)。
HTTP
请求报文是由三部分组成: 请求行, 请求报头和请求正文。
请求行格式如下:
Method Request-URL HTTP-Version CRLF
例:
GET index.html HTTP/1.1
常用的方法有: GET
, POST
, PUT
,DELETE
, OPTIONS
, HEAD
。
请求报头
请求报头允许客户端向服务器传递请求的附加信息和客户端自身的信息。PS: 客户端不一定特指浏览器,有时候也可使用Linux
下的CURL
命令以及HTTP
客户端测试工具等。
常见的请求报头有: Accept
, Accept-Charset
, Accept-Encoding
, Accept-Language
, Content-Type
, Authorization
, Cookie
, User-Agent
等。
请求正文
当使用POST
,PUT
等方法时,通常需要客户端向服务器传递数据。这些数据就储存在请求正文中。在请求包头中有一些与请求正文相关的信息。例如: 现在的Web
应用通常采用Rest
架构,请求的数据格式一般为json
。这时就需要设置Content-Type: application/json
。
服务器处理请求并返回HTTP
报文
自然而然这部分对应的就是后端工程师眼中的HTTP
。后端从在固定的端口接收到TCP
报文开始,这一部分对应于编程语言中的socket
。它会对TCP
连接进行处理,对HTTP
协议进行解析,并按照报文格式进一步封装成HTTP Request
对象,供上层使用。这一部分工作一般是由Web
服务器去进行,我使用过的Web
服务器有Tomcat
, Jetty
和Netty
等等。
HTTP
响应报文也是由三部分组成: 状态码, 响应报头和响应报文。
状态码
状态码是由3位数组成,第一个数字定义了响应的类别,且有五种可能取值:
1xx
:指示信息–表示请求已接收,继续处理。
2xx
:成功–表示请求已被成功接收、理解、接受。
3xx
:重定向–要完成请求必须进行更进一步的操作。
4xx
:客户端错误–请求有语法错误或请求无法实现。
5xx
:服务器端错误–服务器未能实现合法的请求。
响应报头
常见的响应报头字段有: Server
,Connection
...。
响应报文
服务器返回给浏览器的文本信息,通常HTML
,CSS
, JS
, 图片等文件就放在这一部分。
浏览器解析渲染页面
浏览器在收到HTML
,CSS
,JS
文件后,它是如何把页面呈现到屏幕上的?
浏览器是一个边解析边渲染的过程。首先浏览器解析HTML
文件构建DOM
树,然后解析CSS
文件构建Style Rules
,通过Attachment
过程之后生成了渲染树,浏览器开始布局渲染树并将其绘制到屏幕上。这个过程比较复杂,涉及到两个概念: reflow
(回流)和repain
(重绘)。
DOM
节点中的各个元素都是以盒模型的形式存在,这些都需要浏览器去计算其位置和大小等,这个过程称为relow
;当盒模型的位置,大小以及其他属性确定下来之后,浏览器便开始绘制内容,这个过程称为repain
。页面在首次加载时必然会经历reflow
和repain
。
reflow
和repain
过程是非常消耗性能的,尤其是在移动设备上,它会破坏用户体验,有时会造成页面卡顿。所以我们应该尽可能少的减少reflow
和repain
。
JS
的解析是由浏览器中的JS
解析引擎完成的。JS
是单线程运行,也就是说,在同一个时间内只能做一件事,所有的任务都需要排队,前一个任务结束,后一个任务才能开始。
但是又存在某些任务比较耗时,如IO
读写等,所以需要一种机制可以先执行排在后面的任务,这就是:同步任务(synchronous
)和异步任务(asynchronous
)。
JS
的执行机制就可以看做是一个主线程加上一个任务队列(task queue
)。同步任务就是放在主线程上执行的任务,异步任务是放在任务队列中的任务。所有的同步任务在主线程上执行,形成一个执行栈;异步任务有了运行结果就会在任务队列中放置一个事件;脚本运行时先依次运行执行栈,然后会从任务队列里提取事件,运行任务队列中的任务,这个过程是不断重复的,所以又叫做事件循环(Event loop
)。
浏览器在解析过程中,如果遇到请求外部资源时,如图像,iconfont
,JS
等。浏览器将需要下载该资源。请求过程是异步的,并不会影响HTML
文档进行加载,但是当文档加载过程中遇到JS
文件,HTML
文档会挂起渲染过程,不仅要等到文档中JS
文件加载完毕还要等待解析执行完毕,才会继续HTML
的渲染过程。原因是因为JS
有可能修改DOM
结构,这就意味着JS
执行完成前,后续所有资源的下载是没有必要的,这就是JS
阻塞后续资源下载的根本原因。CSS
文件的加载不影响JS
文件的加载,但是却影响JS
文件的执行。JS
代码执行前浏览器必须保证CSS
文件已经下载并加载完毕。
Web优化
上面部分主要介绍了一次完整的请求对应的过程,了解该过程的目的无非就是为了Web
优化。在谈到Web
优化之前,我们回到一个更原始的问题,Web
前端的本质是什么。
我的理解是: 将信息快速并友好的展示给用户并能够与用户进行交互。快速的意思就是在尽可能短的时间内完成页面的加载,试想一下当你在淘宝购买东西的时候,淘宝页面加载了10
几秒才显示出物品,这个时候你还有心情去购买吗?怎么快速的完成页面的加载呢?
如何尽快的加载资源?答案就是能不从网络中加载的资源就不从网络中加载,当我们合理使用缓存,将资源放在浏览器端,这是最快的方式。如果资源必须从网络中加载,则要考虑缩短连接时间,即DNS
优化部分;减少响应内容大小,即对内容进行压缩。另一方面,如果加载的资源数比较少的话,也可以快速的响应用户。当资源到达浏览器之后,浏览器开始进行解析渲染,浏览器中最耗时的部分就是reflow
,所以围绕这一部分就是考虑如何减少reflow
的次数。