服务端性能优化
pr1mavera 2019/9/10
# 浏览器渲染过程
# Navigation Timing
w3c 制定的浏览器标准处理流程,将各个阶段拆分成一个个步骤:
prompt for funload
提示准备卸载上一个页面- 事件 :
navigationStart
该流程真正开始的时间
- 事件 :
redirect
本地跳转重定向,检查本地缓存(客户端)是否存在、是否过期- 事件 :
redirectStart
重定向开始,开始查找本地资源 - 事件 :
redirectEnd
查找到数据库中对应的资源位置,此时还未获取到资源
- 事件 :
unload
卸载上一个页面(与上一步同时进行,并发)- 事件 :
unloadStart
- 事件 :
unloadEnd
- 事件 :
APP cache
读取本地缓存(前提是步骤二中检查到有本地缓存,否则跳过,但是该步骤伴随的事件依旧存在)- 事件 :
fetchStart
读取缓存的起始 - 若获取到该缓存资源,则直接跳过一下 5、6、7、8 步,直接开始处理 HTML
- 事件 :
DNS
解析- 事件 :
domainLookupStart
DNS 查询起始时间 - 事件 :
domainLookupEnd
DNS 查询结束时间
- 事件 :
TCP
从 TCP 层面上与远程服务器建立连接、握手过程- 事件 :
connectStart
TCP 建立请求时间,三次握手开始 - 事件 :
(srcureConnectionStart)
HTTPS 安全协议连接、交换证书 - 事件 :
connectStart
- 事件 :
Request
- 事件 :
requestStart
开始发送请求 - 等待服务器处理
- 事件 :
Response
- 事件 :
responseStart
客户端收到服务器发来的响应开始 - 事件 :
responseEnd
客户端收到服务器发来的响应结束
- 事件 :
Processing
处理 HTML- 事件 :
domLoading
收到响应文本之后,放入内存,开始解析 - 处理成 DOM 树
- 事件 :
domInteractive
解析完成 - 待解析完毕,将 DOM 对象载入可用内存中去
- 事件 :
domContentLoaded
开始计算 DOM 结构的位置和大小 - 事件 :
domComplete
计算完成
- 事件 :
onLoad
展示- 事件 :
loadEventStart
- 事件 :
loadEventEnd
- 事件 :
- 本地缓存阶段 2, 3, 4
- 网络请求阶段 5, 6, 7, 8
- 渲染阶段 9, 10
这些节点的时间可以通过浏览器的 performance.timing
API 获取,目前主流的浏览器都支持
可优化的点:
- 缓存阶段
- DNS
- TCP
- 请求 & 响应(传输数据的多少)
# DNS解析
Domain Name System,用于将 域名 转换成 IP
- 正向解析 域名 --> IP
- 反向解析 IP --> 域名
域名数量级达到上亿,因此需要分级解析
# 名词
顶级域名
google.com二级域名
www.google.com后缀
.com资源记录
关联 域名 与 IP
# 解析过程
DNS 服务器:
- 内置所有 根服务器 的 IP
- 内部有缓存机制,几分钟更新一次
几个资源服务器:
- Root Server 根服务器
- 维护后缀(.com)
- 全球仅13台(大部分分布于欧美日,且都是集群分布)
- TLD Server TLD服务器
- 维护顶级域名(google.com)
- Name Server 名称服务器
- 维护剩余的域名(二级、三级、... ,如:www.google.com / images.google.com / map.a.qq.com)
由于请求压力非常的大,这些资源服务器都不是单独的一台或者单独的集群,都是存在相应的镜像服务器的
- 受制于主服务器
- 镜像服务器只负责查询,不起真正作用
- 真正的 主服务器 更新之后,需要马上同步至镜像服务器
- 镜像同步是需要时间的,几分钟至几十分钟
过程:
- PC 向 DNS 服务器查询,在本地系统中设置的 DNS 服务器(8.8.8.8 / 114.114.114.114)
- DNS 服务器检查存在缓存并且未过期,则直接将缓存的 IP 返回给 PC
- 若以上步骤不通,则需要启动远程 DNS 查询(甚至跨域半个地球)
- 远程 DNS 查询
- 与 Root Server 通信,查询后缀,返回下一个要查询的TLD服务器的 IP
- 与 TLD Server 通信,查询顶级域名,返回下一个要查询的名称服务器的 IP
- 与 Name Server 通信,解析整个域名,返回最终的 IP 给 DNF 服务器
- DNF 服务器将 IP 缓存住,返回给 PC
# DNS 记录类型
- SOA: (StartOf Authority, 起始授权记录),一个区域解析库有且只能有一个SOA记录,⽽而且必须放在第一条
- A记录: (主机记录),用于名称解析的重要记录,将特定的主机名映射到对应主机的 IPv4 IP地址上
- CNAME记录: (别名记录),用于返回另一个域名,即当前查询的域名是另一个域名的跳转,主要用于域名的内部跳转,为服务器配置提供灵活性
- NS记录: (域名服务器记录),用于返回保存下一级域名信息的服务器地址。该记录只能设置为域名,不能设置为IP地址。
- MX: (邮件记录),用于返回接收电子邮件的服务器地址
- IPv6主机记录: (AAAA记录),与A记录对应,用于将特定的主机名映射到一个主机的 IPv6 地址
# TCP 三次握手与四次挥手
通过 TCP 协议传输的数据内容,是经过层层包装的从原始的 HTTP 包装请求报文开始,一层层的向下传输至物理层(电信号),每经过一层,数据就被多包装一次。数据到了接收方那边,又一层层的拆开包装,拿到里面的数据内容
# TCP 的协议头结构
被 TCP 协议包装过后,会给数据包装上这样的协议头:
其中:
- 左手边的数字为数据流偏移量(字节位置)
Source Port
- 源端口号、发送方端口号(本机的一个高于 1024 随机端口号)
- 占两个字节(短整型)
Destination Port
- 目标端口号、接收方端口号(80 端口)
Sequence Number
- 顺序号,发送方发送数据需携带
- 每发送一个数据包,顺序号 +1
- 目的:大数据是要拆成多个小包发送的,数据的顺序就依靠顺序号,到接收方那边若发现某个数据包丢失需要重发,只需要求发送方重发该顺序号对应的数据包
Acknowledgment Number
- 应答编号,与顺序号对应,接收方收到数据响应回去需携带
Offet
- 整个协议头长度,即偏移量
- 当拿到数据包的时候需要拆开层层包装的数据时,根据该字段拆分
- 后续为一系列用于校验与验证的数据属性
# 握手与挥手
为了创建一条稳定的链路,TCP 连接(建立链路)需要三次握手过程,断开连接(断开链路)需要四次挥手过程
名词:
SYN
: 请求指令ACK
: 应答指令FIN
: 结束指令
握手过程:
- 客户端 ----发送请求指令,携带顺序号(Seq=x)---> 服务端
- 客户端 <---发送应答指令,携带应答号(Ack=x+1),同时为减少通信次数,发送请求指令,携带顺序号(Seq=y)---- 服务端
- 客户端 ----发送应答指令,携带应答号(Ack=y+1)---> 服务端
挥手过程:
- 客户端 ----发送结束指令,携带顺序号(Seq=x+2)、应答号(Ack=y+1)---> 服务端
- 客户端 <---服务端应答该结束指令,携带应答号(Ack=x+1)---- 服务端
- 客户端 <---服务端做好断开准备,发送结束指令,携带顺序号(Seq=y+1)---- 服务端
- 客户端 ----客户端应答该结束指令,携带应答号(Ack=y+2)---> 服务端
为什么挥手有四次?
因为挥手过程中,服务端接收到客户端的结束指令时,服务端可能还存在事务未处理完,需先响应客户端的断开,处理完事务,再发送给客户端结束指令,告诉客户端自己这边也处理完毕,可以结束
# 半连接
半连接状态:即握手或挥手过程中,客户端发送请求指令,服务端发送应答指令,之后客户端未响应应答指令。这时,服务端维持着网络连接状态。
造成的后果:
- 网络连接资源无法释放不掉
- 为连接申请的资源释放不掉,会造成 TCP 堆的崩溃
网络安全中存在一种 DOS 攻击(半连接攻击、拒绝服务攻击),目前应对的策略只能是堆硬件
# CDN 与 集群
CDN 的原理:
- 一台主服务器映射多台 镜像服务器,镜像服务器的资源需从主服务器同步
- 智能 DNS ,将域名映射到 多个 IP 地址,并将对应的解析规则,放入到客户端对应的 区域 DNS 服务器 中。比如当某客户端访问 www.baidu.com,则由该客户端的区域 DNS 服务器解析出对于客户端最快的镜像服务器 IP 地址