未完待续
性能优化是大家熟的不能再熟的概念、命题、目标。更多的大家只关注How(how很重要),而不太关注Why(为什么要做,为什么这个方案work或者不work), 也不太关注When(何时做以及要不要做)。而我始终认为性能优化是一个系统工程,和其它工程一样是有一套底层的理论体系做支撑的。今天我尝试做一个总结,权当抛转。
在开始之前我抛一些问题:
- 为什么网页二次打开会很快?
- 为什么手淘跟统一接入之间要维持长链接?
- 为什么说接口包大小会影响响应速度?
- 为什么绝大部分情况下SSR比CSR快?
- 为什么机房要做单元化改造?
- 监控系统 上哪些指标都啥意思 ?
- 监控系统上指标都是怎么来的 ?
- 如何正确的开始一次性能优化?
- 等等...
1. 从一道经典面试题开始
这道题目就是「从浏览器输入网址按回车开始到网页展示发生了什么?」。 这个题目可以一句话说完,也可以「罄竹难书」,今天我们把主要目光关注在和性能相关的过程上。 首先这是发生在B/S 架构下的一个问题。 从抽象的角度看,上面已经是非常抽象的存在,对于理解架构有利,但是性能优化是一件需要「抽丝剥茧」的事情。这个架构还是需要进一步细化。但大致可以分位三大块:
- 浏览器及运行在浏览器里的应用程序(这里特指前端的页面)
- 网络及运行在网络内的应用程序
- 运行在服务端的设备、服务和程序
1.1 先说说这个面试题
B/S 架构的特点是:请求-响应。理解这一点很关键,后面会说。
如无特殊说明这里特指chrome为代表的的浏览器
预备知识:
- 当代浏览器都是多进程模型,在chrome浏览器下一般会有4个进程
- 浏览器主进程:负责页面展示,用户交互,子进程管理等功能
- 渲染进程:每个选项卡都有自己的渲染进程,无关乎是否为 same-site 站点,SandBox 运行环境,处理 HTML、CSS、JavaScript。同时 V8 和 Blink 也都运行在该进程中。
- 插件进程:负责插件运行,根据插件的功能决定是否运行在 Sandbox 环境
- GPU 进程:处理一些特殊的 CSS 效果
- NetWork Service:处理网络资源加载,请求响应,校验 CORS。
- Storage Service:处理对 localStorage、sessionStorage、cookie、Indexed DB 存储的控制。
- Audio Service:处理音视频 Buffer 的音量播放等操作
- RTT: **round-trip time **
RTT(Round-Trip Time): 往返时延。在计算机网络中它是一个重要的性能指标,表示从发送端发送数据开始,到发送端收到来自接收端的确认(接收端收到数据后便立即发送确认),总共经历的时延。 一般认为单向时延=传输时延t1+传播时延t2+排队时延t3 t1是数据从进入节点到传输媒体所需要的时间,通常等于数据块长度/信道带宽 t2是信号在信道中需要传播一定距离而花费的时间,等于信道长度/传播速率(光纤中电磁波的传播速率约为210^5 km/s,铜缆中2.310^5 km/s) t3可笼统归纳为随机噪声,由途径的每一跳设备及收发两端负荷情况及吞吐排队情况决定(包含互联网设备和传输设备时延) 综上:时延并无标准值只有经验值。某运营商规定网内路由器间时延1000公里之内<=40ms,2000公里之内<=60ms,3000公里之内<=80ms
按完回车后,浏览器内主要发生以下几步:
- 检验输入,是搜索词跳搜索,是链接或者域名继续
- 这里有执行beforeunload的时机(你经常遇到的“你确定要退出吗”,就发生在这里。
- 浏览器是否有缓存,有直接返回,否则继续
- NetWork Service 会委托 Storage Service 依次在 service worker cache、memory cache、disk cache、push cache(HTTP2 Stream)中寻找对应的 URI 是否有可用的强缓存,如果存在强缓存,则直接使用缓存进入浏览器解析环节,否则进入 DNS 解析.
- 这里面给我们有几个启示:
- service worker 应用
- http cache 应用
- pre-fetch 应用等
- DNS解析(这是一个知识点,也是一个优化点我们后面讲)
- DNS 的概念及分类
- CDN 与DNS的关系
- 为什么有HTTPDNS
- DNS怎么影响了性能
- 怎么优化
- 排队:TCP队列(也就是传说中浏览器针对单个域名有6个并发请求限制)
- 发送网络请求:,
- TCP 建立链接 :三次握手、慢启动、流量控制、队首拥塞、(知识点,也是网络优化的重点)
- 如果是https 协议,建立TLS链接
- 最多需要两次握手
- 构建HTTP请求信息(Request Header + Request Line ),发送请求
- 请求行: 方法(比如GET,POST) + PATH (比如/index.html ) + 协议版本(这个版本很重要,也是我们性能优化的重点,后面会讲)
- http 1.0 协议
- http 1.1 协议
- http 2.0 协议
- http 3.0 协议
- 请求header (包含该域名下的cookie等信息)
- 请求行: 方法(比如GET,POST) + PATH (比如/index.html ) + 协议版本(这个版本很重要,也是我们性能优化的重点,后面会讲)
- 中间数据包的传输过程
- 操作系统,驱动程序,网卡,光电信息号
- WIFI 、蜂窝网络、有线网(属于用户端网络的最后一公里范畴,延迟的重灾区)
- 核心网、骨干网、ISP
- 数据中心及其内部网络
- 本部分的主要问题其实是TCP协议本身的限制导致的网络延迟问题(敦促了http 3.0 协议的升级)
- 3次握手
- 拥塞窗口
- 队首拥塞
- 慢启动
- 丢包
- 服务端接受请求后,解析请求信息,内部运算后产生响应数据(Response Header, Response status, Body) , 发送响应请求(服务端这一块,后面我会讲,是很大的一块)(从分工上来讲,这块需要服务端去优化)。
- 服务端其实是一个非常抽象的概念
- 服务端既是服务端也是客户端 ,尤其是当前微服务架构模式占比比较多的当下
- 服务端既是单个数据中心,也可以跨(地域)数据中心 (为什么公司要做单元化改造?)
- 集群
- DB
- 缓存
- 队列
- 配置中心
- 负载均衡
- 单机
- 程序本身(java、node、c、c++等)
- 操作系统
- 硬件
- 网卡
- CPU
- 内存
- 存储类型
- 浏览器网络进程解析请求返回头及返回行(这里面主要看状态码,以及Content-Type)(TTFB )
- 状态码的知识大家应该都比较清楚
- content-type 需要细说下,比如浏览器怎么知道什么时候渲染html, 什么时候是调用下载,什么时候调用播放器播放视频呢,就是这个东西,类型很多,常见的 (text/html、application/json等)
- 如果这里content-type 不是类似 application/octet-stream 这种下载类型(调用下载器,流程结束),比如我们这里指html这种类型,则会继续通知主进程 准备渲染进程进一步进行渲染
- 如果这个时候遇到重定向则会重新执行(DNS解析-> 发起请求的过程)(现实中的例子:微信里面商品详情分享去掉一次302跳转,优化100ms+)
- 创建渲染进程并从网络进程中读取响应体数据 开始解析过程 (这里面的知识点是,现代浏览器都是基于流式渲染的,并不是需要完整的下载完才开始(这里可以参见关于使用ER做流式渲染的文章))
- 遇到css文件会跳过(阻塞渲染,但不阻塞解析)
- 遇到没有添加async 或者defer属性的script 则会阻碍解析
- 解析完成后就形成了DOM 树
- 处理CSS构建CSSOM树
- 遇到 link 的css 要下载来、style 标签里面的,inline 的这些 转换成浏览器认识的数据结构- styleSheets.
- 属性值标准化(比如em 转成px,blod 转成700)
- 计算DOM树中每个节点的样式(请问啥叫css的继承与层叠哇?)
- 继承就是从父元素等继承下来的
- 层叠就是多个来源都作用于一个元素的时候,优先级咋算,也就是到底谁生效
- 最后生成Computed Style
- JavaScript 编译 (这一层优化可以说比较底层了)
- 源代码 到 AST
- AST 到字节码
- 字节码到机器码
- 构建辅助功能树(无障碍相关)
- 合并 DOM 树 和CSSOM 到render 树 (忽略了那些隐藏的元素
- 渲染(前端同学需要开始关注了)
- 样式计算
- 遇到 link 的css 要下载来(阻塞的哦)、style 标签里面的,inline 的这些 转换成浏览器认识的数据结构- styleSheets.
- 属性值标准化(比如em 转成px,blod 转成700)
- 计算DOM树中每个节点的样式(请问啥叫css的继承与层叠哇?)
- 继承就是从父元素等继承下来的
- 层叠就是多个来源都作用于一个元素的时候,优先级咋算,也就是到底谁生效
- 最后生成Computed Style
- 样式计算
- 布局阶段(Layout) (计算DOM树中可见元素(宽度,高度,位置)的过程)
- 创建(可见元素)布局树
- 布局计算 (计算节点的坐标位置)
- 这里面一个知识点: **第一次确定节点的大小和位置称为布局。随后对节点大小和位置的重新计算称为回流
- 分层阶段
- 一些3D, transform, z-index 过的这些节点生成专用的图层,并生成图层树(LayerTree)
- 什么情况下会被提升成一个单独的层呢 (下列二选一)?
- 具有层叠上下文**的元素 **
- 因为超过元素容器大小(比如overflow: hidden, auto 后)被截断,出现滚动条
- 分层确实可以提升性能,一般会到GPU中加速
- 图层绘制: 生成记录绘制顺序和绘制指令的列表
- 栅格化(raster)操作
- 合成线程会按照视口附近的图块来优先生成位图,实际生成位图的操作是由栅格化来执行的。所谓栅格化,是指将图块转换为位图
- 一般这一步操作在GPU中进行,也叫做快速栅格化
- 合成与显示 (将上一步光栅化的内容存到内存,显示到显示器的过程)
- 这里的知识点是,首次绘制的节点:FMP( first meaningful paint) (不过这个被FCPFirst Contentful Paint(en-US)替代了)
- 随着 不同层的绘制,会相互重叠,则必须进行合成。
- 交互
一旦主线程绘制页面完成,你会认为我们已经“准备好了”,但事实并非如此。如果加载包含 JavaScript(并且延迟到 onload 事件激发后执行),则主线程可能很忙,无法用于滚动、触摸和其他交互。 Time to Interactive(en-US)(TTI)是测量从第一个请求导致 DNS 查询和 SSL 连接到页面可交互时所用的时间——可交互是 First Contentful Paint(en-US) 之后的时间点,页面在 50ms 内响应用户的交互。如果主线程正在解析、编译和执行 JavaScript,则它不可用,因此无法及时(小于 50ms)响应用户交互。
1.2 当代浏览器及前端页面
1.3 网络及协议
1.4 现代后端服务设计
2. 性能优化的数理基础
本质是为了如何度量
2.1 概率与统计
2.2 支撑法则
2.3 如何AB
3. 如何开启一次正确性能优化之旅
知道why后,how也很重要,很多同学感兴趣的可能是这一部分 ^_^