专栏名称: SegmentFault思否
SegmentFault (www.sf.gg)开发者社区,是中国年轻开发者喜爱的极客社区,我们为开发者提供最纯粹的技术交流和分享平台。
目录
相关文章推荐
OSC开源社区  ·  Bun ... ·  昨天  
程序员的那些事  ·  OpenAI ... ·  昨天  
程序员的那些事  ·  印度把 DeepSeek ... ·  3 天前  
程序猿  ·  “我真的受够了Ubuntu!” ·  3 天前  
51好读  ›  专栏  ›  SegmentFault思否

从输入url开始能做哪些优化

SegmentFault思否  · 公众号  · 程序员  · 2018-04-20 08:00

正文

此文主要讲的事情是如何让用户快点看到首屏页面,其主要影响因素是延迟和解析渲染耗时。有关安全部分其实也是优化的一部分。我们着重说下网络部分。

大致过程:DNS域名解析、建立TCP连接、下载资源、解析页面。文章描述的优化会尽量限制在当时的分析的过程下。

参考

  • 《计算机网络自顶向下方法》

  • 《Web性能权威指南》

  • 老生常谈-从输入url到页面展示到底发生了什么

  • what happens when you type in a URL in browser

  • 从浏览器多进程到JS单线程,JS运行机制最全面的一次梳理

  • DNS解析的过程是什么,求详细的?

  • 前端性能优化最佳实践

  • 前端性能优化-justjavac

  • 浏览器的渲染:过程与原理

  • 浏览器渲染过程与性能优化

1.DNS域名解析

一般来讲,我们输入的url是域名,而为了识别一个实体,TCP/IP使用IP地址来唯一确定一台主机到因特网的连接,DNS会帮助我们完成域名到IP地址映射的工作。以 www . aaa . com 为例,解析过程大致如下:

过程

浏览器

浏览器查询浏览器缓存,没有。

本机层

浏览器客户端向系统询问服务器IP地址,调用本机内的DNS解析程序,检查自己本地的hosts文件是否有这个域名映射关系,没有。

查找本机的DNS解析器缓存,没有。

路由器缓存

可能还存在路由器缓存这一层

本地DNS服务器

本机的DNS解析程序向本地的DNS服务器发起请求,一般为TCP/IP参数中设置的首选DNS服务器,是知道IP地址的,一般会UDP协议。

本地DNS服务器查询是否在本地区域文件中,没有。

本地DNS服务器查询DNS缓存中是否存在,没有。

本地DNS服务器会根据是否设置转发器判断是向上一级DNS服务器(其解析规则同理)还是直接向根DNS服务器(知道根DNS服务器的IP地址)发送请求。

与DNS服务器

收到请求后,根DNS服务器并不直接解析地址,但是知道每个顶级域中的一台服务器的地址(如 com 域名服务器)。如果为迭代查询方式,此顶级域DNS服务器的ip被返回给本地DNS服务器。

本地DNS服务器提取到顶级域DNS服务器信息后,会再向其发出请求。顶级域DNS服务器收到请求后,会先查询自己的缓存,没有,则将负责的二级域名服务器(如 aaa . com 域名服务器)返回给本地DNS服务器,以此类推直到查到目标域名的映射信息或查询失败。

查到映射信息后返回到本机,中间各层会进行缓存。

查询方式

  • 递归方式:一路查下去中间不返回,得到最终结果才返回信息。

  • 迭代方式:就是上面的本地DNS服务器与其他域名服务器直接的查询方式,查到一个可能知道的服务器地址,将此地址返回,重新发送解析请求。

一般默认的方式从本机到本地DNS服务器是递归,DNS服务器之间是迭代查询。

优化

当然针对DNS的优化就是减少DNS解析的时间,由于浏览器缓存机制的存在,我们只需要对首次访问进行优化(虽然我们现在只是请求了一个html文件,但是html文件里还会有我们后续要请求的css/js/img等),即适当减少要解析的域名个数,考虑到 其他优化机制 可以将页面及页面内资源发布到2-4个域名上。

2.建立连接

TCP连接

好了,浏览器终于拿到服务器IP了,客户端想要与服务器间通信并传递消息需要开启TCP(一种传输层协议)连接。

过程:

  • 客户端创建socket,向服务器目标端口发送连接建立请求,数据段包含位码SYN(建立联机标志位) = 1,随机数seq(顺序号码)= x,和其他TCP标志和选项。

  • 服务器有一个专门处理连接请求的welcome socket,接收到连接建立请求,置位码SYN和ACK(确认标志位)为1,ack(确认号码)= x + 1,随机数seq = y,并返回。

  • 客户端检查ack是否等于x + 1,等于时,将ACK置为1,SYN置为0,将ack置为y + 1发送至服务器端。

  • welcome socket检查ack等于y + 1和ACK等于1后,创建新的socket,此socket由源IP/源端口、目标IP/目标端口标识,之后客户端发送的数据都被引导向此新的socket,至此,TCP连接建立。

简单来讲:

  1. // client:

  2. send({SYN : 1, seq: x, ...others})

  3.                    |

  4.                    

  5. //server:

  6. send({SYN: 1, ACK: 1, ack: x + 1, seq: y, ...others})

  7.                    |

  8.                    

  9. //client:

  10. ack === x + 1 ? send({ACK: 1, SYN: 0, ack: y + 1, ...others}) : 'hehe'

  11.                    |

  12.                    

  13. //server:

  14. ack === y + 1 && ACK === 1 ? new Socket() : ''

SSL/TLS

如果启用了HTTPS进行加密,在使用TLS前还需要协商建立加密信道。

过程:

客户端

TCP连接建立之后,再以纯文本形式发送一些规格说明,随机数 Random1 ,TLS协议版本,支持的加密套件列表,支持或希望使用的其他TLS选项。

服务器

  1. 取得TLS协议版本以备将来通信使用,从客户端提供的加密套件列表中选择一个,生成随机数 Random2 发送给客户端;

  2. 附上自己的证书,将响应发送给客户端;

  3. 同时,也可发送一个请求,要求客户端提供证书以及其他TLS扩展参数。

客户端

  1. 同上,可能会向服务器发送自己的证书。

  2. 客户端收到服务器的证书后,通过证书链关系从根CA(证书的签发机构)验证证书的合法性,验证通过后取出证书中的服务器公钥,生成随机数Random3,再用服务器公钥加密 Random3 (pre master key),发送给服务器;

  3. 告诉服务器可以开始加密透明信了;

  4. 客户端用 三个随机数 约定的加密方法 生成 对话密钥 。将前面的握手信息生成完成摘要,使用 对话密钥 加密,发送告诉服务器我已完成握手。

除了服务器公钥加密的新对称密钥外,所有的数据都是明文形式发送。

服务器

用私钥解密出客户端发来的随机数,通过验证消息的MAC检测消息完整性,用相同的方式生成 对话密钥

解密客户端发送的完成报文,验证 对话密钥 是否正确。

  1. 告诉客户端,要开始加密了;

  2. 同样再返回给客户端一个加密的完成消息。

客户端用它之前生成的 对话密钥 解密这条消息,确定 对话密钥是否正确 ,正确则建立信道并且开始发送应用数据。

其中:

  • 对话密钥 又可称为 协商密钥

  • 对话密钥 对称密钥 ,对称加解密速度很快。

  • 服务器公钥和私钥是非对称密钥,非对称加解密速度很慢。

  • 使用非对称加密生成可靠的 对称密钥 ,使用对称密钥进行后续数据的加密。

  • 上述带序号报文可能一次发送,也可能分次连续发送。

  • SSL和TLS可以当作一个东西。

  • 服务器也可以不使用CA颁发的证书,而使用自己的证书。

优化

我们要对TCP和SSL/TLS握手耗时进行优化。有以下几个因素:

  • 数据往返延迟:主要受地理位置影响,使用较近的服务器进行数据传输会减少数据往返的时间,我们可以通过在不同的地区部署服务器(如:CDN,其也会用到DNS解析,可能在DNS解析阶段就完成了对客户端访问域名到距离最近的服务器的映射),将数据放到接近客户端的地方,可以减少网络往返时间。

  • 证书链:其实数据往返延迟优化不只是针对TCP握手阶段的,后续基于TCP的数据传输都会收益,如SSL/TLS握手和后续的请求响应。那么证书链是影响SSL/TLS握手的一个重要因素,证书链是服务器向客户端发送的证书内的信息,由站点证书、中间证书颁发机构的证书、~~根证书~~组成(比较类似DNS域名解析服务器之间的关系)。

原因:

  1. TCP慢启动:由于TCP慢启动(为避免拥塞,TCP连接初始只能发送较少的分组,然后等待客户端确认,然后翻倍,经过几次往返直至到达阈值)和TLS/SSL握手数据发送一般位于TCP连接慢启动阶段的关系,证书数据过多会超过TCP连接的初始值,会造成数据往返次数的成倍增加。

  2. 证书链验证过长:由于客户端浏览器在验证证书可靠性时,会递归验证链条中的每个节点至根证书,也会增加握手时间。

方法:

  1. 减少中间证书颁发机构的数量,优化至只有站点证书和一个中间证书颁发机构。

  2. 不要添加根证书信息,浏览器内置信任名单中有根证书。

握手次数:前两点优化都是针对的握手时间的优化,握手次数也是影响延迟的重要因素。我们在后面谈到大量请求的时候再说这一点。

初始拥塞窗口:适当增大初始拥塞窗口大小,即增大TCP连接初始可发送的分组大小。

3.获得页面响应

重定向响应

如果服务器返回了跳转重定向(非缓存重定向),那么浏览器端就会向新的URL地址重新走一遍 DNS解析 建立连接

所以应该避免不必要的重定向。

页面资源响应

在获得了html响应之后,浏览器开始解析页面,进入准备渲染的阶段。下载优化同样放在后面谈到大量请求的时候再说这一点。

4.解析渲染页面

我们需要将这个过程先分为两个部分来看, 页面资源加载 渲染

页面资源加载

浏览器在解析页面的过程中会去请求页面中诸如js、css、img等外联资源。

建立连接

同样这些资源的加载也是需要建立TCP连接的,直接使用也需要进行DNS解析和握手。

优化

此处的请求次数与频率相对于第一次请求页面资源时要高很多,所以这里着重阐述下成批量的请求的优化。

浏览器目前使用的HTTP协议版本大多是1.1和2,二者有些不同,但是底层都是使用TCP进行数据传输。由于TCP握手耗时,和SSL/TLS更加耗时,我们需要减少整个加载过程中需要建立的连接的次数和耗时。

复用:针对HTTP1.1的最好方法是启用长连接:HTTP 1.1提供了默认开启长连接功能,相对于短连接(每请求一个资源建立然后断开一次TCP连接),同一客户端socket(浏览器可能会开多个端口并行请求)针对同一socket(域名+端口)后续请求都会复用一个TCP连接进行传输,直到关闭这个TCP连接。

加速:针对SSL/TLS握手有会话恢复机制,验证通过后,可以直接使用之前的对话密钥,减少握手往返。

加载之前

在服务器返回响应时,又存在几种情况,如:服务器负载大,服务器宕机,无法及时或较快响应请求,服务器地理位置过远或跨运营商导致延迟很高。

优化

这里跟 建立连接 部分的优化其实是公用的,但是单纯的正常建立连接消耗资源较少,所以我们在这个再较完成的阐述一下。

  • 增加带宽:但是大部分情况下服务器带宽并不是影响延迟的主要因素。

  • 智能DNS解析:根据客户端的IP地址,将域名解析为最近的或不跨运营商的服务器的IP地址,解决地理位置和跨运营商的延迟问题。

  • CDN:使用某种分析方式根据节点服务器的地理位置、负载情况、资源匹配情况从遍布各地的节点服务器中找出最合适的静态资源服务器。

  • 负载均衡:使用DNS负载均衡、IP负载均衡、反向代理负载均衡等方式从一堆服务器(集群相同职责)或一组服务器(分布式职责区分)中选择最合适的服务器处理请求。

  • 这几种技术可能是相互结合的,比如CDN会用到DNS智能解析和负载均衡等。

  • 其中使用了跳转重定向方式的会重新进行DNS解析和握手,其中一部分优化实际是在域名的DNS解析部分完成的。

开始加载

好了,服务器终于可以愉快的返回数据了。

HTTP 1.1

过程:

  • 虽然HTTP 1.1有长连接,一个TCP连接可以用来请求多个资源,但是这些资源的下载是串行的,比如使用这个TCP通道请求1.css、2.css、1.js,只有在前者传输 成功完整完成 后才会进行下一个的传输。







请到「今天看啥」查看全文