图片来源:《图解HTTP》
浏览器工作过程_服务器
总结一下分为以下几个过程:
DNS解析
TCP连接
发送HTTP请求
服务器处理请求并返回HTTP报文
浏览器解析渲染页面
连接结束

下面针对每个过程进行简述:
DNS解析:
当我们输入一个网址 实际上是要将其翻译成服务器IP地址的,而这种翻译就是需要DNS解析,解析过程为:首先在本地域名服务器上查找此IP地址,如果没有找到的话 本地域名服务器会向根域名服务器发送一个请求,如果根域名服务器也不存在,那么本地域名服务器会再向com顶级域名服务器发送请求。直到最后 本地域名服务器得到此网址的IP地址,并将其缓存到本地,攻下一次使用。总结一下,所有网址真正的解析过程为: . -> .com -> google.com. -> www.google.com.。注意到了最前面的(.),这个就是本地的域名服务器。因此本地域名服务器是从顶级域名服务器开始查找,然后逐级具体化直到www.google.com.
在DNS请求的过程中 同时存在TCP和UDP请求,为什么?
首先我们要知道 DNS规范规定了两种服务器:主DNS服务器和辅助DNS服务器。如果我们直接向主DNS请求 主DNS服务器会从本机的数据中查找数据。而如果我们是向辅助DNS请求,而辅助服务器此时会从主DNS中请求读取数据 这叫做区传送(zone transfer),因为区域传送是DNS内部事务 对准确性要求较高(TCP优于UDP),而且会产生大于512字节的数据包 (UDP报文最大512字节)因此使用TCP协议。
而当DNS服务器(不管是主还是辅助DNS服务器)与客户端联系的时候,一般返回的字节不超过512字节(因为就是IP地址及其他的东西),因此 为了使DNS服务器负载更低 相应更快,所以我们会使用UDP。但是TCP当然也是可以用的。
现在我们知道了DNS请求原理,那么如何做优化?
第一种是DNS缓存。DNS存在多级缓存,从离浏览器的距离排序 可以有以下几种:浏览器缓存,系统缓存,路由器缓存,IPS(intrusion prevention system)服务器缓存,根域名服务器缓存,顶级域名服务器缓存,主域名服务器缓存。
第二种是DNS负载均衡 load balancing:由于现代大公司需要成千上万台服务器来满足人们的需求 因此每次用户键入同一个网址 DNS返回的IP地址不同。但是在用户眼中 它只需要其请求被处理就可以,至于最终是哪台服务器响应了他的请求并不重要。因此DNS会从服务器池中返回一个合适的机器IP返回给用户(如何选择?选择负载量底的,或者用round robin法,或者hash一下,或者选择离用户地理位置近的)。这个过程叫DNS负载均衡 或者叫重定向。之前了解的CDN(content dilivery network)就是利用这种技术。

TCP连接

HTTP协议利用TCP作为其传输层协议,因此TCP出现瓶颈,HTTP也会受到影响,因此TCP优化也很重要。
而至于TCP利用三次握手进行连接 有专门的文章 在此不加赘述。
这里插一个:HTTP协议和HTTPS协议的区别:
我们知道 HTTP报文是包裹在TCP报文中进行发送的,服务器段收到TCP报文的时候会解包提取出HTTP报文,由于HTTP报文是明文 因此有风险,所以在进入TCP报文之前对HTTP做一次加密就可以解决这个问题了。用什么做加密呢?SSL或TLS。因此可以说是讲HTTP变成HTTPS+SSL/TLS.
HTTPS过程:HTTPS在传输数据之前需要客户端与服务器进行一个握手(TLS/SSL握手),在握手过程中将确立双方加密传输数据的密码信息。TLS/SSL使用了非对称加密,对称加密以及hash等。HTTPS相比于HTTP,虽然提供了安全保证,但是势必会带来一些时间上的损耗,如握手和加密等过程,是否使用HTTPS需要根据具体情况在安全和性能方面做出权衡。(refer:http://www.ruanyifeng.com/blog/2014/09/illustration-ssl.html)

HTTP请求
简单的讲:发送HTTP请求的过程就是构建HTTP请求报文并通过TCP协议中发送到服务器指定端口(HTTP协议80/8080, HTTPS协议443)。HTTP请求报文是由三部分组成: 请求行, 请求报头和请求正文。下面说一下这三部分的格式:

  • 请求行:GET index.html HTTP/1.1 //Method Request-URL HTTP-Version CRLF, 常用的方法有: GET, POST, PUT, DELETE, OPTIONS, HEAD。(经典问题:GET和POST有什么区别)
  • 请求报头: 包括客户端向服务器传递请求的附加信息和客户端自身的信息。常见的请求报头有: Accept, Accept-Charset, Accept-Encoding, Accept-Language, Content-Type, Authorization, Cookie, User-Agent等。
  • 请求正文: 当使用POST, PUT等方法时,通常需要客户端向服务器传递数据。这些数据就储存在请求正文中。例如: 现在的Web应用通常采用Rest架构,请求的数据格式一般为json。这时就需要设置Content-Type: application/json。

服务器处理请求并返回HTTP报文
web server会对TCP连接进行处理,对HTTP协议进行解析,并按照报文格式进一步封装成http request对象 供上层使用(?什么上层)
HTTP响应报文也由三部分组成:状态码,响应报头和响应报文

  • 状态码:此部分请见其他文章
  • 响应报头:常见的响应报头字段有: Server, Connection…。
  • 响应报文:服务器返回给浏览器的文本信息,通常HTML, CSS, JS, 图片等文件就放在这一部分。

浏览器解析渲染页面
浏览器工作过程_加载_02
总的来说 是边解析边渲染 大致分为下面三步:

  1. 首先浏览器解析HTML文件构建DOM树,
  2. 然后解析CSS文件构建渲染树,
  3. 等到渲染树构建完成后,浏览器开始布局渲染树并将其绘制到屏幕上。
    有一点重要的要说明:这个过程比较复杂,涉及到两个概念: reflow(回流)和repain(重绘)。DOM节点中的各个元素都是以盒模型的形式存在,这些都需要浏览器去计算其位置和大小等,这个过程称为relow;当盒模型的位置,大小以及其他属性,如颜色,字体,等确定下来之后,浏览器便开始绘制内容,这个过程称为repain。页面在首次加载时必然会经历reflow和repain。reflow和repain过程是非常消耗性能的,尤其是在移动设备上,它会破坏用户体验,有时会造成页面卡顿。所以我们应该尽可能少的减少reflow和repain。
    那么,网页中的javasript如何解析呢?
    JS的解析是由浏览器中的JS解析引擎完成的。
    **Verbose Warning!!!**
    JS是单线程运行,也就是说,在同一个时间内只能做一件事,所有的任务都需要排队,前一个任务结束,后一个任务才能开始。但是又存在某些任务比较耗时,如IO读写等,所以需要一种机制可以先执行排在后面的任务,这就是:同步任务(synchronous)和异步任务(asynchronous)。JS的执行机制就可以看做是一个主线程加上一个任务队列(task queue)。同步任务就是放在主线程上执行的任务,异步任务是放在任务队列中的任务。所有的同步任务在主线程上执行,形成一个执行栈;异步任务有了运行结果就会在任务队列中放置一个事件;脚本运行时先依次运行执行栈,然后会从任务队列里提取事件,运行任务队列中的任务,这个过程是不断重复的,所以又叫做事件循环(Event loop)。
    浏览器在解析过程中,如果遇到请求外部资源时,如图像,iconfont,JS等。浏览器将重复1-6过程下载该资源。请求过程是异步的,并不会影响HTML文档进行加载,但是当文档加载过程中遇到JS文件,HTML文档会挂起渲染过程,不仅要等到文档中JS文件加载完毕还要等待解析执行完毕,才会继续HTML的渲染过程。原因是因为JS有可能修改DOM结构,这就意味着JS执行完成前,后续所有资源的下载是没有必要的,这就是JS阻塞后续资源下载的根本原因。CSS文件的加载不影响JS文件的加载,但是却影响JS文件的执行。JS代码执行前浏览器必须保证CSS文件已经下载并加载完毕。
    **==================**

Web Optimization
宏观的想一下,我们为什么要了解一次客户端到服务器的完整的请求过程?无非是为了web优化,优化什么呢?无非是将信息快速并友好的展示给用户并能够与用户进行交互。
基于之前提过的原理 优化方式可以从以下来着手:
如何尽快的加载资源?答案就是能不从网络中加载的资源就不从网络中加载,当我们合理使用缓存,将资源放在浏览器端,这是最快的方式。
如果资源必须从网络中加载呢? 则要考虑缩短连接时间,即DNS优化部分;减少响应内容大小,即对内容进行压缩。另一方面,如果加载的资源数比较少的话,也可以快速的响应用户。
当资源到达浏览器之后什么会限制性能呢?,浏览器开始进行解析渲染,浏览器中最耗时的部分就是reflow,所以围绕这一部分就是考虑如何减少reflow的次数。