mirror of
https://github.com/cheetahlou/CategoryResourceRepost.git
synced 2025-10-19 16:33:46 +08:00
mod
This commit is contained in:
485
极客时间专栏/高楼的性能工程实战课/性能工程的实践关键点/05 | 性能方案:你的方案是否还停留在形式上?.md
Normal file
485
极客时间专栏/高楼的性能工程实战课/性能工程的实践关键点/05 | 性能方案:你的方案是否还停留在形式上?.md
Normal file
@@ -0,0 +1,485 @@
|
||||
<audio id="audio" title="05 | 性能方案:你的方案是否还停留在形式上?" controls="" preload="none"><source id="mp3" src="https://static001.geekbang.org/resource/audio/bd/72/bd6c018e3f71d1b2058bcf28898e7572.mp3"></audio>
|
||||
|
||||
你好,我是高楼。
|
||||
|
||||
性能方案在性能项目中是非常重要的文档之一,它指导着整个项目的执行过程,同时也约束着项目的边界,定义相关人员的职能。但令人痛心的是,如今它变得“微不足道”。
|
||||
|
||||
在很多常见的性能项目中,性能方案就是一个文档,并且是一个静态的文档。里面写的东西是什么,项目后续会不会按这个内容去做,基本上没有人关心。它就成了一个形式,只有在评审方案的时候才会被拿出来看看。甚至在一些第三方测试项目中,我看到有些甲方连方案的内容都不看,直接问有没有。如果有就过去了。你看,一个必需的交付物却无人关心。
|
||||
|
||||
**在我的性能工程理念中,性能方案是一个重量级的文档。**在性能项目中,它被叫成是“性能测试方案”。在我这里,我要把“测试”二字拿掉。为什么要拿掉?因为这取决于我在前面课程中提到的性能工程理念,我希望把整个项目的过程都描述在方案中。
|
||||
|
||||
我讲的性能方案和那些常见的性能方案究竟有什么区别呢?我们不妨先来看看,后者普遍都是什么样的。
|
||||
|
||||
这些目录相信你并不陌生,我们经常能看到有这样目录的性能测试方案。
|
||||
|
||||
<img src="https://static001.geekbang.org/resource/image/72/80/724a358cc2fyy85e6e80168baceec180.jpg" alt="">
|
||||
|
||||
这里我就不一一列举了,再看更多的目录,其实也是类似的。这样的目录大纲,在我看来分为这么几个部分。
|
||||
|
||||
- **常规项目信息**:比如说测试背景、测试范围、测试准则、测试环境、实施准备、组织结构、项目风险、里程碑。
|
||||
- **性能实施信息**:比如说测试模型、测试策略、监控策略。
|
||||
- **项目输出**:比如说测试脚本、测试用例/测试场景、监控采集数据,测试报告、调优报告。
|
||||
|
||||
从性能测试方案的角度来说,这些内容似乎够了。但是,如果抛弃掉“测试”这个视角,从一个完整的性能项目的角度来看,这些内容其实还不够。
|
||||
|
||||
以前经常有人问我要一个性能项目方案模板,我一直不太理解,就这么一个目录,为什么还非得要呢?自己一个字一个字也照样写得出来吧。后来我慢慢理解了,他们要的其实不是大纲目录,而是一个完整的性能方案内容。
|
||||
|
||||
不过,我们知道,项目实施的性能方案基本上都不太可能直接发出来,即便做了脱敏,一些内容也可以看出是属于某些企业的。所以,出于职场的素养,这些内容不得不放在硬盘里,直到过时,直到烂掉。这也就是为什么我们在网上看不到非常完整的性能方案。
|
||||
|
||||
可是,尽管网上的方案不完整,在性能市场上,我们还是看到有太多的性能方案是抄来抄去的,总体的结构大同小异。这也就导致了在性能项目中,大量的方案都只有形式上的意义。
|
||||
|
||||
因为我们这个课程需要基于一个完整的项目来编写,所以,我把这个项目整体的方案写在这里。你将看到,我认为的真正完整并且有意义的性能方案是什么样子,希望能给你一些启发。
|
||||
|
||||
由于性能方案的内容比较多,并且相对琐碎,我给你整理了一张性能方案的目录表格,你可以对应这张表格,来学习具体的内容。
|
||||
|
||||
<img src="https://static001.geekbang.org/resource/image/23/80/23f4edc62fbe347019198152bc2c9780.jpg" alt="">
|
||||
|
||||
# 性能项目实施方案
|
||||
|
||||
## 背景
|
||||
|
||||
### 项目背景
|
||||
|
||||
我们刚才提到,这个课程需要搭建一个比较完整的性能项目。但由于各企业的商业软件有限制,我们只能选择一个开源的项目,并且这个项目最好可以覆盖常见的技术栈,以便能给你提供更多可借鉴的内容。
|
||||
|
||||
基于上述原因,我们搭建了一套电商项目。对此,我要说明两点:第一,这个项目是较为完整的;第二,当前电商的系统比较典型,并且这个项目完全开源,便于我们改造。
|
||||
|
||||
不过,也因为这是一个开源的项目,功能和性能都不知道会有什么样的问题,我们只有在性能实施的过程中一步步去发掘,所以这是一个非常符合我们当前目标的项目。
|
||||
|
||||
### 性能目标
|
||||
|
||||
1. 根据经典的电商下单流程,测试当前系统的单接口最大容量。
|
||||
1. 根据业务比例设计容量场景,充分利用当前资源,找到当前系统的性能瓶颈,并优化,以达到系统的最佳运行状态。
|
||||
1. 根据稳定性场景,判断当前系统可支持的系统最大累加容量。
|
||||
1. 根据异常场景,判断当前系统中的异常对性能产生的影响。
|
||||
|
||||
**在每一个性能项目中,性能目标都会影响项目的整个过程。因此,对目标的把握将决定一个性能项目的走向。**
|
||||
|
||||
记得我在之前的一个项目中,客户方要求做到支持1000万人在线,项目不算小,开发团队有300人左右。到那里后,我一看只有两个性能测试人员,而且其中一个还是刚毕业,还处于打野练级的状态。于是,我就过去找他们科技部的老大说,这个项目我做不了。因为根据这个目标和这样的人员配置,我清楚这个坑根本不是我能填得上的,所以得赶紧认怂。
|
||||
|
||||
后来,那个科技部的老大问,需要什么样的资源才能做下去呢?于是我提了几个必需的条件,直到这些条件都满足了,我才敢接这个项目。
|
||||
|
||||
我举这个例子是想让你明白,性能目标在上下级眼中根本是不一样的,而我这样的处理,是希望把性能目标在上下级的脑袋中变得一致。这一点很重要。
|
||||
|
||||
## 测试范围
|
||||
|
||||
### 需要测试的特性
|
||||
|
||||
电商主流程,如下所示:
|
||||
|
||||
<img src="https://static001.geekbang.org/resource/image/d0/57/d04fd451655e345cca2355d5c0a1d657.jpg" alt="">
|
||||
|
||||
### 不需要测试的特性
|
||||
|
||||
批量业务。
|
||||
|
||||
## 准则
|
||||
|
||||
### 启动准则
|
||||
|
||||
1. 确定系统逻辑架构和部署架构和生产一致。
|
||||
1. 确定基础数据和生产一致或按模型缩放。
|
||||
1. 确定业务模型可以模拟生产真实业务。
|
||||
<li>环境准备完毕,包括:<br>
|
||||
4.1. 功能验证通过。<br>
|
||||
4.2. 各组件基础参数梳理并配置正确。<br>
|
||||
4.3. 压力机到位,并部署完毕。<br>
|
||||
4.4. 网络配置正确,连接通畅,可以满足压力测试需求。</li>
|
||||
1. 测试计划、方案评审完毕。
|
||||
1. 架构组、运维组、开发组、测试组及相关专家人员到位。
|
||||
|
||||
### 结束准则
|
||||
|
||||
1. 达到项目要求的性能需求指标。
|
||||
1. 关键性能瓶颈已解决。
|
||||
1. 完成性能测试报告和性能调优报告。
|
||||
|
||||
### 暂停/再启动准则
|
||||
|
||||
**1. 暂停准则**
|
||||
|
||||
- 系统环境变化:举例:系统主机硬件损坏、网络传输时间超长、压力发生器出现损坏、系统主机因别的原因需升级暂停等。
|
||||
- 测试环境受到干扰,比如服务器被临时征用,或服务器的其他使用会对测试结果造成干扰。
|
||||
- 需要调整测试环境资源,如操作系统、数据库参数等。
|
||||
- 该测试机型无法达到规划指标要求。
|
||||
- 出现测试风险中列出的问题。
|
||||
|
||||
**2. 再启动准则**
|
||||
|
||||
- 测试中发现问题得以解决。
|
||||
- 测试环境恢复正常。
|
||||
- 测试风险中出现的问题已解决。
|
||||
- 环境调整完毕。
|
||||
|
||||
## 业务模型和性能指标
|
||||
|
||||
### 业务模型/测试模型
|
||||
|
||||
<img src="https://static001.geekbang.org/resource/image/7d/b4/7d25892ee40bbdfa8074fb50c59634b4.jpg" alt="">
|
||||
|
||||
请你注意,这个模型并不是随便填写的,而是直接从生产环境中取得的业务比例。关于如何从生产中取出这样的业务比例,有很多种手段。这个并不复杂,通过统计日志就可以做到。
|
||||
|
||||
不过,在有些企业中,生产数据都在运维手里,性能团队怎么也得不到,因为没有权限,就连做业务模型的数据都没有。如果是这样的话,那性能项目是可以直接终止的,因为做了也没有多大的意义,最多也就是找那些瞎吹牛的架构师和乱写代码的开发人员,犯的一些错而已。
|
||||
|
||||
### 业务指标/性能指标
|
||||
|
||||
<img src="https://static001.geekbang.org/resource/image/76/99/76567b1ef8cec388c2698bb50b936499.jpg" alt="">
|
||||
|
||||
在不清楚项目目标TPS的情况下,我们暂定目标TPS为1000。为什么暂定1000呢?因为根据经验来说,在这样的硬件环境下,定为1000并不算高,除非是没有合理的软件架构。
|
||||
|
||||
## 系统架构图
|
||||
|
||||
### 系统技术栈
|
||||
|
||||
系统技术栈是让我们知道整个架构中用了哪些技术组件。而这些技术组件中有哪些常见的性能瓶颈点,有哪些性能参数,我们都可以在查看技术栈时得到一些相关信息。而在后续的工作中,我们也要整理出相应的关键性能参数配置。
|
||||
|
||||
下面这张表格,就是我们在后续课程的案例分析中,会用到的技术栈。我在搭建这个系统时,考虑的是尽量覆盖当前技术市场中的主流技术组件。
|
||||
|
||||
<img src="https://static001.geekbang.org/resource/image/b1/99/b1691631357554701bfc4bd54cd8ae99.jpg" alt="">
|
||||
|
||||
### 系统逻辑架构图
|
||||
|
||||
画系统的逻辑架构图是为了后续性能分析的时候,脑子里能有一个业务路径。我们在做性能分析时,要做响应时间的拆分,而只有了解了逻辑架构图才可以知道从哪里拆到哪里。
|
||||
|
||||
<img src="https://static001.geekbang.org/resource/image/9b/8b/9b5730bbfb2a191a00ab3b569acbff8b.jpg" alt="">
|
||||
|
||||
### 系统部署架构图
|
||||
|
||||
画部署架构图是为了让我们知道有多少节点、多少机器。在执行容量场景时,你的脑子里要有一个概念,就是这样的部署架构最大应该可以支持多少的容量上限。
|
||||
|
||||
此外,对一些无理的性能需求,你看了部署架构之后,其实就可以拒绝。比如说前段时间有个人跟我说,他们有一个CRM系统,在做性能的时候,说要达到1万的并发用户。而实际上,那个系统就算是上线了,总用户数可能都不到1万。
|
||||
|
||||
<img src="https://static001.geekbang.org/resource/image/f2/2f/f2294e162a078c7053ef0887f86b762f.png" alt="">
|
||||
|
||||
## 性能实施前提条件
|
||||
|
||||
### 硬件环境
|
||||
|
||||
通过对整体硬件资源的整理,我们可以根据经验知道容量大概能支持多少的业务量级,而不至于随便定无理的指标。比如说,当看到下面表格中这样的硬件配置,我想没有人会把指标定为10000TPS。因为即使是对于最基础的接口层来说,这样的硬件也支持不了这么大的TPS。
|
||||
|
||||
<img src="https://static001.geekbang.org/resource/image/a3/6a/a309ab88d76b436241856126a7305a6a.jpg" alt="">
|
||||
|
||||
我们可以看到,当前服务器总共使用在应用中的资源是:64C的CPU资源,128G的内存资源。NFS服务器不用在应用中,故不计算在内。因为单台机器的硬件资源相对较多,所以在后续的工作中,我们可以将这些物理机化为虚拟机使用,以方便应用的管理。
|
||||
|
||||
在成本上,所有物理机加在一起大概8万左右的费用,这其中还包括交换机、机柜、网线等各类杂七杂八的费用。
|
||||
|
||||
我之所以会对硬件的成本进行一个说明,主要是因为在当前的性能行业中,很少有性能工程师去做成本的计算。我们说性能项目的目标是让线上的系统运行得更好,与此同时,我们也要知道使用了多少成本在运行业务系统。
|
||||
|
||||
在当前的性能行业中,有大量的线上主机处于高成本低使用率的状态当中,这是极大的资源浪费和成本消耗。我经常在性能项目中,看到一台256C512G的硬件服务器里,只运行了一个4G JVM的Tomcat,性能工程的价值完全没有在这样的项目中应用起来。
|
||||
|
||||
因此,我时常会痛心疾首地感慨性能行业的不景气:
|
||||
|
||||
<li>
|
||||
企业中没有意识到性能的价值,觉得摆个高配置的硬件服务器,业务系统的性能就能好起来。其实完全不是这样。
|
||||
</li>
|
||||
<li>
|
||||
性能市场从业人员完全没有把性能的价值,透明化地体现出来。并且很多性能人员自身的技术能力不足,这也让一个企业完全看不到性能本该有的价值。
|
||||
</li>
|
||||
|
||||
鉴于此,**作为性能从业人员,我们必须要了解硬件配置和整体业务容量之间的关系。**
|
||||
|
||||
### 工具准备
|
||||
|
||||
#### 测试工具
|
||||
|
||||
<img src="https://static001.geekbang.org/resource/image/d0/89/d00a8d9947301974dbb9c8ed2bf2ea89.jpg" alt="">
|
||||
|
||||
在测试过程中,我们将使用JMeter的backend Listener把数据直接发到InfluxDB中,然后再由Grafana来展现。我们不使用JMeter的分布式执行功能或本地收集数据的功能,因为这样会消耗本地的IO。
|
||||
|
||||
然而,现在还是有很多性能人员,仍然在项目中频繁地使用那些性能工具的低性能操作手段,同时还在不断抱怨性能这么差。对于这种现状,我希望你可以明白一点:我们要理智地使用工具,不要觉得一个性能测试工具拿起来就可以用。
|
||||
|
||||
#### 监控工具
|
||||
|
||||
<img src="https://static001.geekbang.org/resource/image/e9/e1/e93f194c5b8be2089ee368yy85a8cee1.jpg" alt="">
|
||||
|
||||
根据RESAR性能工程中的全局-定向的监控思路,我们在选择第一层监控工具时,要采集全量的全局计数器,采集的计数器包括各个层级,这里请参考前面的架构图。
|
||||
|
||||
但是,请你注意,在全局监控中,我们要尽量避免使用定向的监控手段,比如说java应用中的方法级监控、数据库中的SQL监控等。因为在项目开始之初,我们不能确定到底在哪个层面会出现问题,所以不适合使用定向监控思路。
|
||||
|
||||
那全局监控怎么来做才最合理呢?这里我们可以参考线上运维的监控手段。注意,我们在性能监控过程中,尽量不要自己臆想,随意搭建监控工具。
|
||||
|
||||
有时我们可能为了能监控得更多,会在测试环境中用很多监控手段。但实际上,线上运维时并不用那些手段,这就导致了监控对资源的消耗大于生产环境的资源消耗,我们也就得不到正常有效的结果。
|
||||
|
||||
前面我们提到在选择第一层监控工具时,需要采集全量的全局计数器。在我们采集好全局的计数器后,还需要分析并发现性能问题,然后再通过查找证据链的思路,来找性能瓶颈的根本原因。
|
||||
|
||||
### 数据准备
|
||||
|
||||
#### 基础数据
|
||||
|
||||
<img src="https://static001.geekbang.org/resource/image/39/ab/397yycb462ac6ff675251130a359b3ab.jpg" alt="">
|
||||
|
||||
在性能工程中,我们一直强调基础数据要满足两个特性:
|
||||
|
||||
<li>
|
||||
**满足生产环境的真实数据分布**:要想做到这一点,最合理的方式是脱敏生产数据。如果你要自己造数据的话,也一定要先分析业务逻辑。在我们这个系统中,我造了243万条用户数据和250万条地址数据。
|
||||
</li>
|
||||
<li>
|
||||
**参数化数据一定要使用基础数据来覆盖真实用户**:一直以来,很多人都在使用少量数据做大压力,这种逻辑是完全不对的。在性能脚本中一定要用基础数据来做参数化,而用多少数据取决于性能场景的设计。
|
||||
</li>
|
||||
|
||||
## 性能设计
|
||||
|
||||
### 场景执行策略
|
||||
|
||||
#### 场景递增策略
|
||||
|
||||
对于性能场景,我一直在强调一个观点,那就是性能的场景必须满足两个条件:
|
||||
|
||||
- 连续
|
||||
- 递增
|
||||
|
||||
所以在这次的执行过程中,我也会把这两点应用到下面的业务场景中。
|
||||
|
||||
你也许会问,如果不连续递增的话会有什么问题呢?比如说下面这样的图:
|
||||
|
||||
<img src="https://static001.geekbang.org/resource/image/46/27/46319a3b6200f22516a51d9166026427.png" alt="">
|
||||
|
||||
在图中画红框的地方,其实就是递增带来的性能问题表现。因为在递增过程中,被测系统的资源要动态分配。系统会不会在这个时候抖动,我们完全可以从这样的图中看出来,而这样的场景才是真实的线上场景。
|
||||
|
||||
如果不连续递增,就不会有图中红框这样的部分。当然了,要是不连续递增,也就不能模拟出线上的真实场景。
|
||||
|
||||
高老师画重点!敲黑板了!**要模拟生产场景,连续递增一定要做到的,不容迟疑。**
|
||||
|
||||
而在不同工具中,设置连续递增的方式是不同的。
|
||||
|
||||
LoadRunner设计如下:
|
||||
|
||||
<img src="https://static001.geekbang.org/resource/image/1b/06/1b739eaa3eb3f9152b0e753fa6ff1b06.png" alt="">
|
||||
|
||||
JMeter设计如下:
|
||||
|
||||
<img src="https://static001.geekbang.org/resource/image/74/b4/74bc6a59yy4e6bbce713fdc71ef84cb4.png" alt="">
|
||||
|
||||
总之,请你记住,在设计场景中,我们一定要做到上面这种连续递增的样子。
|
||||
|
||||
#### 业务场景
|
||||
|
||||
在RESAR性能工程中,性能场景只需要这四类即可:
|
||||
|
||||
<img src="https://static001.geekbang.org/resource/image/7d/03/7df293f3b57eed500d95acf156c05303.jpg" alt="">
|
||||
|
||||
执行顺序先后为:基准场景、容量场景、稳定性场景、异常场景。
|
||||
|
||||
请你注意,除了这四类性能场景外,再没有其他类型的场景了。在每一个场景分类中,我们都可以设计多个具体的场景来覆盖完整的业务。
|
||||
|
||||
下面我给你一一解释一下。
|
||||
|
||||
**1. 基准场景**
|
||||
|
||||
我经常看到有人说,用脚本加上三五个线程跑上多少次迭代,就算是基准场景了。你可以想想这样的场景意义何在?它仅能验证一下脚本和场景是正确的而已。所以,我不把这样的步骤称为基准场景。
|
||||
|
||||
在我的RESAR性能工程理念中,**基准场景必须是容量场景的前奏**。具体怎么做呢?那就是在基准场景中,我们也要通过递增连续的场景做到最大TPS。也就是说在基准场景中,我们要把单接口或单业务压到最大TPS,然后来分析单接口或单业务的瓶颈点在哪里。
|
||||
|
||||
可能你会问,在基准场景中有没有必要做调优的动作呢?
|
||||
|
||||
根据我的经验,应该先判断当前单接口或单业务的最大TPS,有没有超过目标TPS。如果超过,并且响应时间也在业务可接受的范围之内,那就不用调优。如果没有超过,那必须要做调优。
|
||||
|
||||
另外,根据RESAR性能工程理论,性能执行的第一阶段目标就是把资源用光,第二阶段的目标是将系统优化到满足业务容量。要知道,任何一个系统要调优都是无止境的,而我们的目标是要保证系统的正常运行。
|
||||
|
||||
因为在我们这个课程的示例项目中只有一个系统,所以,我们先做接口级的,然后把接口拼装成完整的业务量,并实现业务模型,然后再在容量场景中执行。在这里,我们将执行测试范围中接口的基准场景。
|
||||
|
||||
**2. 容量场景**
|
||||
|
||||
有了基准场景的结果之后,我们就进入了容量场景的阶段。在容量场景中,我们还是要继续秉承“连续、递增”的执行思路,最重要的是,要实现我们前面提到的业务模型,来真实模拟线上的业务场景。
|
||||
|
||||
我们可以经常看到,现在很多的性能项目里,大部分性能需求都提得不是很具体,从而导致性能场景的模型和生产场景不一致,这是一个严重的问题。
|
||||
|
||||
还有一个严重的问题是,即便业务模型和生产一致了,也会由于性能工程师在执行过程中没有严格模拟业务模型中的比例,性能场景的结果变得毫无意义。要知道,在执行过程中,响应时间会随着压力的增加而增加,我们仅用线程数来控制比例是非常不理智的,因为在执行的过程中会出现业务比例失衡。
|
||||
|
||||
那应该如何控制这个比例关系呢?如果你是用JMeter的话,可以使用Throughput Controller来控制业务比例,如下所示:
|
||||
|
||||
<img src="https://static001.geekbang.org/resource/image/fa/09/fa700cbb38e65500c211f38f4d11c809.png" alt="">
|
||||
|
||||
当然,你也可以用其他方式来实现。总之,在场景执行结束之后,我们要把业务比例做统计,并且要和业务比例对比,当比例一致时,才算是合理的场景。
|
||||
|
||||
在容量场景中,我们还有一个要确定的事情,就是什么是最大的TPS。
|
||||
|
||||
我想请你看一下这张图,你觉得最大的TPS是多少呢?
|
||||
|
||||
<img src="https://static001.geekbang.org/resource/image/1e/32/1e9945005c788bcbef8e15d97ca8e332.png" alt="">
|
||||
|
||||
你是不是想说最大的TPS是700?
|
||||
|
||||
不管你给出的是不是这个答案,在我的性能理念中,我都想跟你强调一点:**容量场景的最大TPS是指最大的稳定TPS。**
|
||||
|
||||
那么你看,上面这张图已经抖动了,已经不稳定了,我们再去找它的最大TPS还有什么意义呢?你敢让一个生产系统运行在这样抖动的状态中吗?所以,对于上图中这样的TPS曲线,我会把最大的稳定TPS定为第三个阶梯,也就是在600左右,而不会定在700。
|
||||
|
||||
另外,也请你注意,在性能场景中,特别是在容量场景中,经常有人提到“性能拐点”这个词,并且把性能拐点称作是判断性能瓶颈的关键知识。对此,我先不做评判,我们来看一下什么是拐点,它在数学中的定义是这样的:
|
||||
|
||||
>
|
||||
拐点,又称反曲点,在数学上指改变曲线向上或向下方向的点。直观地说,拐点是使切线穿越曲线的点(即连续曲线的凹弧与凸弧的分界点)。
|
||||
|
||||
|
||||
那么在TPS曲线中,你真的能找到这样的点吗?反正我是找不到。就以我们上面那张图为例,图中哪里是拐点呢?也许有人会说这个曲线没有拐点。咳咳,那就没得聊了……
|
||||
|
||||
可见,性能拐点其实是一个在具体执行过程中非常有误导性的概念。请你以后尽量不要再用“性能拐点”这个词来尝试描述性能的曲线,除非你是真的看到了拐点。
|
||||
|
||||
**3. 稳定性场景**
|
||||
|
||||
在完成了容量场景之后,我们就要进入稳定性场景的阶段了。到现在为止,在性能的市场中,还没有人能给出一个稳定性场景应该运行多长时间的确切结论。我们知道根据业务属性不同,稳定性场景也有不同的设计思路,可是这样说起来未免有些空泛。所以,我在这里给出一个稳定性场景的运行指导思路。
|
||||
|
||||
在稳定性场景中,我们只有两个关键点:
|
||||
|
||||
**第一个关键点:稳定性场景的时长。**
|
||||
|
||||
关于稳定性场景的时间,我经常看到网上有人说一般运行两小时、7*24小时之类的话。可是,什么叫“一般”,什么又叫“不一般”呢?作为从业十几年的老鸟,我从来没有按照这样的逻辑执行过,也从来没有看到过这些运行时长的具体来源,只看到过很多以讹传讹的文章。
|
||||
|
||||
在性能领域中,这样的例子实在太多了,现在我也见怪不怪,毕竟保持本心做正确事情最为重要。下面我给你解释一下什么才是合理的稳定性场景时长。
|
||||
|
||||
我们知道,一个系统上线之后,运维人员肯定会做运维巡检,如果发现有问题就会去处理。有的系统是有固定的运维周期,比如说会设定固定的Job来做归档之类的动作;有的系统是根据巡检的结果做相应的动作。
|
||||
|
||||
而稳定性要做的就是保证在运维周期之内业务可以正常。 所以,在性能的稳定性场景中,我们要完全覆盖业务容量。比如说对于下面这张图:
|
||||
|
||||
<img src="https://static001.geekbang.org/resource/image/cc/5d/cc63a34d0df3e245d92d8b007173235d.jpg" alt="">
|
||||
|
||||
在运维周期内,有1亿笔业务容量。根据上面容量场景中的测试结果,假设最大稳定TPS是500,那稳定性场景的执行时长就是:
|
||||
|
||||
$$稳定性时长 = 100000000 \div 500 \div 3600 \div 24 \approx 2.3 (天)$$
|
||||
|
||||
通过这样的计算,我们就能知道稳定性场景应该跑多长时间,这也是唯一合理的方式。
|
||||
|
||||
**第二个关键点:用多大的TPS来执行。**
|
||||
|
||||
对此,我看到网上有人提到,用最大TPS的80%来运行稳定性场景。这里我不禁要问了,为什么?凭什么不能用最大的来运行呢?
|
||||
|
||||
记得我在做培训的时候,有过多次这样的讨论。有人说,之所以用最大TPS的80%,是因为在执行稳定性场景时不能给系统太大的压力,否则容易导致系统出现问题。
|
||||
|
||||
这种说法就奇怪了。既然容量场景都能得出最大的TPS,为什么稳定性就不能用呢?如果用最大的TPS执行稳定性场景会出现问题,那这些问题不正是我们希望测试出来的性能问题吗?为什么要用低TPS来避免性能问题的出现呢?
|
||||
|
||||
所以,用最大TPS的80%来做稳定性场景是一个错误的思路。
|
||||
|
||||
在我的性能理念中,**在执行稳定性场景时,完全可以用最大的稳定TPS来运行,只要覆盖了运维周期之内的业务容量即可**。如果你不用最大的稳定TPS来运行,而是用低TPS来运行,那也必须要覆盖运维周期之内的业务容量。
|
||||
|
||||
讲到这里,我觉得上述内容足以指导你做出正确合理的稳定性场景测试了。
|
||||
|
||||
**4. 异常场景**
|
||||
|
||||
对于异常场景,有些企业是把它放到非功能场景分类中的,这个我倒觉得无所谓。不管放在哪里都是要有人执行的。我之所以把异常场景放在性能部分,是因为这些异常场景需要在有压力的情况下执行。
|
||||
|
||||
对于常规的异常场景,我们经常做的就是:
|
||||
|
||||
<li>
|
||||
宕主机;
|
||||
</li>
|
||||
<li>
|
||||
宕网卡;
|
||||
</li>
|
||||
<li>
|
||||
宕应用。
|
||||
</li>
|
||||
|
||||
除此之外,在现在微服务盛行的时代,我们还有了新的招——宕容器。
|
||||
|
||||
当然,你也可以用一些所谓的“混沌工程“的工具来实现对容器的随机删除、网络丢包、模拟CPU高等操作,不过,这就是一个大话题了。在这后面的课程里,我会设计几个常用的异常性能场景来带你看下效果。
|
||||
|
||||
### 监控设计
|
||||
|
||||
#### 全局监控
|
||||
|
||||
其实,有了前面的监控工具部分,监控设计就已经出现在写方案之人的脑子里了。对于我们这个课程所用的系统,全局监控如下所示:
|
||||
|
||||
<img src="https://static001.geekbang.org/resource/image/c4/dd/c4fcbd3e0cc44ac772b7dfe334deb1dd.jpg" alt="">
|
||||
|
||||
从上图来看,我们使用Prometheus/Grafana/Spring Boot Admin/SkyWalking/Weave Scope/ELK/EFK就可以实现具有全局视角的第一层监控。对工具中没有覆盖的第一层计数器,我们只能在执行场景时再执行命令来补充了。
|
||||
|
||||
#### 定向监控
|
||||
|
||||
那后面的定向监控怎么办呢?在这里我也大体列一下常用的工具。不过,请你注意,这些工具是在有问题的时候才会去使用。
|
||||
|
||||
<img src="https://static001.geekbang.org/resource/image/6b/e8/6b96495d6983f668981f73b3805392e8.jpg" alt="">
|
||||
|
||||
其实在性能分析中,除了表格中的这三个工具之外,还有很多工具会在查找性能瓶颈证据链时使用,我在这里无法全部罗列出来,只能根据系统使用到的技术组件,大概罗列一下我能想到的常用工具。在后续课程的操作中,如果你发现我们用了表格中没有列出的工具,也请你不要惊讶。
|
||||
|
||||
## 项目组织架构
|
||||
|
||||
在性能方案中,我们一定要画出项目的组织架构图,并且请你一定要在这部分写明各组织人员的工作范围和职责,避免出现扯皮的情况。我大体画一下常见的组织架构:
|
||||
|
||||
<img src="https://static001.geekbang.org/resource/image/a7/7e/a74520ff665ba1fbef725f3ee178337e.jpg" alt="">
|
||||
|
||||
这是我按照事情来划分的,而非职场中的工作职位,我觉得这是一个合理的组织架构。在这张图中,性能脚本工程师所负责的事情,其实是现在大部分性能从业人员都在做,并且仅仅在做的事。至于性能分析工程师,在很多性能项目中几乎不存在,也没有这样的固定职位。其实,性能分析工程师很有必要存在。
|
||||
|
||||
此外,架构师、开发工程师、运维工程师都需要在支持性能分析的状态。请注意,我说的“支持”,并不是指站在旁边看着,而是在有了问题之后,要能具体地给出支持,而非推诿责任。
|
||||
|
||||
业务方是性能的业务需求来源,这是一定要有的。如果业务方提不出来什么合理的性能需求,那这个项目基本上会是稀碎的样子。
|
||||
|
||||
至于老板这个角色,在性能项目中,我经常看到的老板都不懂什么叫性能,只会叫着要支持XXX并发用户数,支持XXX在线用户数。其实,这样的老板沟通起来也很简单,就是拿结果给他就好了。不过,在性能项目的执行过程中,当资源不足时,请你一定要让老板知道,同时降低老板的预期,要不然在后续的沟通中会非常费劲。
|
||||
|
||||
## 成果输出
|
||||
|
||||
### 过程性输出
|
||||
|
||||
1. 脚本
|
||||
1. 场景执行结果
|
||||
1. 监控结果
|
||||
1. 问题记录
|
||||
|
||||
在性能项目中,过程性输出有这些内容就够了,不用更多,当然,也不能更少了。我经常看到很多性能项目在执行完之后,除了有一份性能测试报告之外,什么过程性输出都没有。我实在不理解这样的企业是怎么积累性能经验的。所以,我还是要规劝你一句,**在性能项目中,尽量多做一些归档整理的工作,以备在后面的项目中查阅,并实现自己的技术积累。**
|
||||
|
||||
### 结果输出
|
||||
|
||||
通常情况下,在我做的性能项目中,都会输出两个最终交付的文档:一个是性能场景执行结果记录的报告(就是现在我们常写的性能测试报告),另一个是性能调优报告。
|
||||
|
||||
#### 性能项目测试报告
|
||||
|
||||
性能项目测试报告想必大家见得多了,这里我只强调几点:
|
||||
|
||||
<li>
|
||||
性能结果报告一定要有结论,而不是给出一堆“资源使用率是多少”、“TPS是多少”、“响应时间是多少”这种描述类的总结语。你想想,性能结果都在这个报告中了,谁还看不见怎么滴?还要你复述一遍吗?我们要给出“当前系统可支持XXX并发用户数,XXX在线用户数”这样的结论。
|
||||
</li>
|
||||
<li>
|
||||
一定不要用“可能”、“或许”、“理应”这种模棱两可的词,否则就是在赤裸裸地耍流氓。
|
||||
</li>
|
||||
<li>
|
||||
性能结果报告中要有对运维工作的建议,也就是要给出关键性能参数的配置建议,比如线程池、队列、超时等。
|
||||
</li>
|
||||
<li>
|
||||
性能结果报告中要有对后续性能工作的建议。
|
||||
</li>
|
||||
|
||||
#### 性能调优报告
|
||||
|
||||
为什么我要强调单独写调优报告呢?因为**调优报告才是整个性能项目的精华,调优报告中一定要记录下每一个性能问题的问题现象、分析过程、解决方案和解决效果**。可以说,调优报告完全是一个团队技术能力的体现。
|
||||
|
||||
## 项目风险分析
|
||||
|
||||
对于性能项目的风险,我把比较常见的风险列在这里:
|
||||
|
||||
1. 业务层的性能需求不明确
|
||||
1. 环境问题
|
||||
1. 数据问题
|
||||
1. 业务模型不准确
|
||||
1. 团队间协调沟通困难
|
||||
1. 瓶颈分析不到位,影响进度
|
||||
1. ......
|
||||
|
||||
在我们这个课程所用的项目中,比较大的风险就是:
|
||||
|
||||
1. 硬件资源有限。
|
||||
1. 项目时间不可控,因为出了问题,并没有人支持,只能自己搞。
|
||||
|
||||
不过请你放心,我会努力克服困难,把这个项目的执行过程都记录下来。
|
||||
|
||||
到这里,整个性能项目实施方案就结束了。如果你认真看到了这里,那么恭喜你,你已经超越了很多人。我为你点赞!
|
||||
|
||||
# 总结
|
||||
|
||||
在这节课里,我把一个性能方案该有的内容以及要写到什么程度,都给你梳理了一遍。希望能给你一些借鉴。
|
||||
|
||||
性能方案是一个性能项目的重要输出。如果你是在项目中做快速迭代,可能并不需要写如此复杂并且重量级的文档。因为文档里描述的很多工作都已经做过了,你可能只需要跟着版本去做迭代比对就好了。
|
||||
|
||||
但对于一个完整的项目来说,性能方案就显得极为重要。因为它指导了这个项目的整个过程。在性能方案中,我们强调了几个重点:业务模型、性能指标、系统架构图、场景设计、监控设计等,它们都会对整个项目的质量起到关键作用。
|
||||
|
||||
最后,我希望你可以在后续的项目中,尝试去写一个完整的性能方案。
|
||||
|
||||
# 课后作业
|
||||
|
||||
学完这节课,请你思考两个问题:
|
||||
|
||||
1. 如何精确模拟业务模型?
|
||||
1. 为什么我们要强调系统架构图的重要性?
|
||||
|
||||
欢迎你在留言区与我交流讨论。当然了,你也可以把这节课分享给你身边的朋友,他们的一些想法或许会让你有更大的收获。我们下节课见!
|
||||
|
||||
**关于课程读者群**
|
||||
|
||||
点击课程详情页的链接,扫描二维码,就可以加入我们这个课程的读者群哦,希望这里的交流与思维碰撞能帮助你取得更大的进步,期待你的到来~
|
251
极客时间专栏/高楼的性能工程实战课/性能工程的实践关键点/06 | 如何抽取出符合真实业务场景的业务模型?.md
Normal file
251
极客时间专栏/高楼的性能工程实战课/性能工程的实践关键点/06 | 如何抽取出符合真实业务场景的业务模型?.md
Normal file
@@ -0,0 +1,251 @@
|
||||
<audio id="audio" title="06 | 如何抽取出符合真实业务场景的业务模型?" controls="" preload="none"><source id="mp3" src="https://static001.geekbang.org/resource/audio/ab/0a/abb074f62d72e4ae059cd68e3863830a.mp3"></audio>
|
||||
|
||||
你好,我是高楼。
|
||||
|
||||
我们知道,业务模型一直是性能项目中很重要的环节。在容量场景中,每个业务比例都要符合真实业务场景的比例。如果不符合,那场景的执行结果也就没有意义了。
|
||||
|
||||
但是,我们经常可以看到,很多性能从业人员因为对业务模型的抽取过程不够了解,或者是拿不到具体的数据,导致业务模型和生产业务场景不匹配,进而整个性能项目都变得毫无意义。
|
||||
|
||||
也有大量的项目,并没有拿历史业务数据做统计,直接非常笼统地拍脑袋,给出相应的业务模型,这样显然也是不合理的。可是,这种情况在金融、互联网等行业中十分常见。
|
||||
|
||||
当然,也有人为了让业务模型和真实业务场景尽可能匹配,会直接拿生产环境的请求进行回放。可是,即便我们拿生产环境的请求录制回放了,也不能保证业务模型和未来的业务场景一致,因为未来的业务场景会随着业务推广而变化。
|
||||
|
||||
**所以说,我们在做场景时首先要明白,当前的场景是要模拟历史业务场景,还是未来业务场景。**
|
||||
|
||||
如果是未来的业务场景,那就要靠业务团队给出评估,而非性能团队。不过,在当前的性能市场中,经常有企业要求性能团队给出业务模型,这显然是不理智的。首先,性能团队的业务背景不如业务团队更熟悉;其次,他们对业务市场的把握也不够专业。
|
||||
|
||||
其实,在真实的工作场景中,业务模型的确认从来都不应该由一个团队来做,而应该由业务团队、架构团队、开发团队、运维团队和性能团队共同确定,并最终由项目的最上层领导确认。
|
||||
|
||||
如果一个系统有历史业务数据,那我们获得业务模型就有背景数据了。这时候,性能团队应该从历史业务数据中抽取出各场景的业务模型。如果系统没有历史数据,那就应该像对未来业务模型评估一样,需要各团队协作给出当前的业务模型。
|
||||
|
||||
正是基于我们前面提到的各种问题,经常有性能从业人员问我,我们应该如何从历史业务数据中抽取出业务模型?可能你也有这样的困惑,下面我们就来详细地说一说,同时我会借助实例为你展示一个具体的过程。
|
||||
|
||||
大体上来说,抽取真实业务模型有两个大步骤:
|
||||
|
||||
1. 抽取生产业务日志。这一步可以通过很多种手段来实现。这节课我给你展示两种比较常见的手段。一种是当没有日志统计系统时,使用awk命令来抽取;另一种是使用ELFK来抽取。
|
||||
1. 梳理业务逻辑。
|
||||
|
||||
对于第一步而言,我们抽取生产业务日志是为了得到对应的业务比例。下面我们先来看看怎么用命令抽取生产业务日志。
|
||||
|
||||
## 使用命令抽取生产业务日志
|
||||
|
||||
这里我以少量的Nginx日志举例。在Nginx中,日志格式通常如下所示:
|
||||
|
||||
```
|
||||
120.220.184.157 - - [26/Oct/2020:14:13:05 +0800] "GET /shopping/static/skin/green/green.css HTTP/1.1" 200 4448 0.004 0.004 "https://www.xxx.cn/shopping/" "Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/45.0.2454.101 Safari/537.36" "124.127.161.254"
|
||||
120.220.184.203 - - [26/Oct/2020:14:13:05 +0800] "GET /shopping/static/js/manifest.0e5e4fd8f66f2b389f6a.js HTTP/1.1" 200 2019 0.003 0.003 "https://www.xxx.cn/shopping/" "Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/45.0.2454.101 Safari/537.36" "124.127.161.254"
|
||||
120.220.184.149 - - [26/Oct/2020:14:13:05 +0800] "GET /shopping/static/js/app.cadc2ee9c15a5c1b9eb4.js HTTP/1.1" 200 138296 0.100 0.005 "https://www.xxx.cn/shopping/" "Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/45.0.2454.101 Safari/537.36" "124.127.161.254"
|
||||
|
||||
```
|
||||
|
||||
以上数据内容及顺序可以在Nginx配置文件中配置。我们的目标是抽取其中某个时间内的每秒的访问量,所以,我们只需要把对应的时间取出来统计一下即可,命令如下:
|
||||
|
||||
```
|
||||
cat 20201026141300.nginx.log|awk '{print $4}' |uniq -c
|
||||
|
||||
```
|
||||
|
||||
我们得出下面的结果:
|
||||
|
||||
```
|
||||
5 [26/Oct/2020:14:13:05
|
||||
3 [26/Oct/2020:14:13:06
|
||||
14 [26/Oct/2020:14:13:07
|
||||
4 [26/Oct/2020:14:13:08
|
||||
1 [26/Oct/2020:14:13:09
|
||||
2 [26/Oct/2020:14:13:10
|
||||
1 [26/Oct/2020:14:13:12
|
||||
2 [26/Oct/2020:14:13:20
|
||||
14 [26/Oct/2020:14:13:23
|
||||
1 [26/Oct/2020:14:13:24
|
||||
2 [26/Oct/2020:14:13:26
|
||||
2 [26/Oct/2020:14:13:29
|
||||
9 [26/Oct/2020:14:13:30
|
||||
9 [26/Oct/2020:14:13:31
|
||||
1 [26/Oct/2020:14:13:32
|
||||
13 [26/Oct/2020:14:13:35
|
||||
2 [26/Oct/2020:14:13:37
|
||||
20 [26/Oct/2020:14:13:38
|
||||
2 [26/Oct/2020:14:13:39
|
||||
33 [26/Oct/2020:14:13:44
|
||||
17 [26/Oct/2020:14:13:46
|
||||
5 [26/Oct/2020:14:13:47
|
||||
23 [26/Oct/2020:14:13:48
|
||||
29 [26/Oct/2020:14:13:49
|
||||
4 [26/Oct/2020:14:13:50
|
||||
29 [26/Oct/2020:14:13:51
|
||||
26 [26/Oct/2020:14:13:52
|
||||
22 [26/Oct/2020:14:13:53
|
||||
57 [26/Oct/2020:14:13:59
|
||||
1 [26/Oct/2020:14:14:02
|
||||
|
||||
```
|
||||
|
||||
这样就可以知道哪段时间里的访问量最高了。这里我们其实可以灵活运用,如果你只想取到分钟、某小时、某天都可以做相应的命令调整。例如,我们想取到分钟级,只要加上相应的截取命令就可以了,如下所示:
|
||||
|
||||
```
|
||||
cat 20201026141300.nginx.log|awk '{print $4}' |cut -c 2-18|uniq -c
|
||||
|
||||
```
|
||||
|
||||
对应的结果如下:
|
||||
|
||||
```
|
||||
352 26/Oct/2020:14:13
|
||||
1 26/Oct/2020:14:14
|
||||
|
||||
```
|
||||
|
||||
上述结果的意思是,在我这个日志中有两分钟内的数据,第一分钟中有352个请求,第二分钟中只有一个请求。
|
||||
|
||||
如果你想请求URL来做统计,那么就可以修改命令:
|
||||
|
||||
```
|
||||
cat 20201026141300.nginx.log|awk '{print $7}' |cut -c 1-50|uniq -c
|
||||
|
||||
```
|
||||
|
||||
结果如下:
|
||||
|
||||
```
|
||||
................
|
||||
1 /shopping/checkLogin
|
||||
1 /shopping/home/floor
|
||||
1 /sso/loginOut
|
||||
1 /shopping/home/navigation
|
||||
6 /shopping/home/floor
|
||||
2 /shopping/home/floorGoods
|
||||
1 /shopping/home/sysConfig
|
||||
4 /shopping/home/floorGoods
|
||||
1 /shopping/home/floor
|
||||
1 /sso/loginOut
|
||||
................
|
||||
|
||||
```
|
||||
|
||||
此时我们取日志中的第七个数据,然后截取统计。这样我们就可以知道,每个时间段内每个请求的数量,也就可以得到相应的业务比例了。
|
||||
|
||||
上面这些命令你只要灵活运用,处理数据量不太大的文件是没什么问题的。
|
||||
|
||||
## 使用ELFK抽取生产业务日志
|
||||
|
||||
如果你想使用ELFK抽取日志,具体可以按如下步骤来做:
|
||||
|
||||
1. 安装ELFK。这里的ELFK是指ElasticSearch/Logstash/FileBeat/Kibana的组合。具体安装方法可以搜索一下,网上的教程多如牛毛。
|
||||
1. 配置好ELFK后,在Kibana的Discover界面就能看到收集的信息。注意,一条日志对应的就是一次命中。
|
||||
1. 通过选择时间段就可以看到有多少请求在这个时间段内。
|
||||
1. 要想得到接口请求的百分比,可以点击“Dashboard”中的“可视化”,创建一个Lens可视化面板,选择相应的URL字段就可以看到各个接口的百分比了。
|
||||
|
||||
总体来说,用ELFK抽取生产日志得到业务模型,可以分为两个阶段。
|
||||
|
||||
第一个阶段是统计大时间段的日志信息,然后逐渐缩小范围,比如说按年、月、天、时、分的顺序。这一步是为了将系统的峰值请求覆盖住。
|
||||
|
||||
第二个阶段是细化所选择的时间段。虽然我们在第一个阶段已经把时间段细化到分了,但由于我们的场景得到的结果是按秒来统计TPS的,所以我们要再细化到生产环境的TPS级别。这样就可以把生产的业务场景和测试中的场景进行对比了。
|
||||
|
||||
下面我通过实例给你详细讲一讲这两个阶段。
|
||||
|
||||
### 第一个阶段:统计大时间段的日志。
|
||||
|
||||
在查看ELFK中的数据时,建议你尽可能选择覆盖全部业务场景的时间段。比如说,我们要选择峰值时间段,一开始要选择时间段的范围设置大一些,这样才不会漏掉数据。然后再通过柱状图的高低做范围缩小。
|
||||
|
||||
通过这样的操作就可以知道生产环境中各业务接口的总体百分比了。
|
||||
|
||||
其实,实时将相应的日志输出到ELFK中,是很多企业对日志处理的常用方法。这样不仅可以实现对日志的灵活查找,也可以实现对日志的长时间存储,并且也可以做更多的后续处理,生成可视化图形之类的。在这里,我们来实际操作一下。
|
||||
|
||||
<img src="https://static001.geekbang.org/resource/image/a4/c3/a4bd8b3f81d7deaff887236cf3f948c3.png" alt="">
|
||||
|
||||
如上图所示,我们在Kibana中截取了一段时间的日志,这段日志总共有6,624,578次请求。你可以用Kibana直接生成下面这样的表格视图:
|
||||
|
||||
<img src="https://static001.geekbang.org/resource/image/71/be/71c53f2d9760fe0e5a0744aec6887cbe.jpg" alt="">
|
||||
|
||||
这样你就可以知道哪些请求比较靠前。为什么我没有显示总数呢?因为在一段时间之内的每个请求,我们要生成相应的柱状图,如果看到它们的集中时间段是相同的,那就做一个场景即可;如果不同,则要做多个场景。下面我们来搜索一下。
|
||||
|
||||
<li>
|
||||
<p>**/mall-member/sso/login**<br>
|
||||
<img src="https://static001.geekbang.org/resource/image/81/de/8155c3d6990711af543b669974c9f6de.png" alt=""></p>
|
||||
</li>
|
||||
<li>
|
||||
**/mall-portal/home/content**
|
||||
</li>
|
||||
|
||||
<img src="https://static001.geekbang.org/resource/image/02/b4/02ffb12068e7ff73e37b94eb78b51eb4.png" alt="">
|
||||
|
||||
- **/mall-member/member/address/list**
|
||||
|
||||
<img src="https://static001.geekbang.org/resource/image/c9/13/c91b94aa66c6fae22a6ff485ceb9db13.png" alt="">
|
||||
|
||||
其他接口的图类似,我就不一一列了。
|
||||
|
||||
看图上的数据时间点,在我这个例子中,所有的请求量级的时间点都是相同的,所以我们只需要做一个场景即可全部覆盖。请你务必要注意,在你的实际项目中,并不见得会是这样。**如果出现某个请求的高并发时间点和其他的请求不在同一时间点,就一定要做多个场景来模拟,因为场景中的业务模型会发生变化。**
|
||||
|
||||
在我这个示例中,我们把数据量也列在表格中,同时求出比例关系,也就是拿某请求的数量除以总请求数,如下所示:
|
||||
|
||||
<img src="https://static001.geekbang.org/resource/image/ba/14/ba318242f4bce666b72587b1cae06d14.jpg" alt="">
|
||||
|
||||
这是在这一个时间段的业务平均比例。
|
||||
|
||||
通过第一阶段,我们已经可以知道哪个时间段的请求高了。但是这个时间段范围是5分钟,这对于任何一个系统来说,都算是比较集中的时间段了。但是我们的动作还没有结束,因为我们不仅要知道哪一段的用户操作比较集中,还要知道的是生产上能达到的TPS有多高。所以还需要细化,只有细化了我们才能知道具体生产的TPS。
|
||||
|
||||
### 第二个阶段:细化时间段
|
||||
|
||||
通过主面图中的Timestamp,可以看到是时间间隔是5分钟,我们选择最高请求的时间段,点进去。
|
||||
|
||||
<img src="https://static001.geekbang.org/resource/image/d6/ca/d63dae9a487e3bb52a94d169086475ca.png" alt="">
|
||||
|
||||
这样我们就得到时间间隔为5秒的图了:
|
||||
|
||||
<img src="https://static001.geekbang.org/resource/image/94/dc/94b6539b4be5129504a2ca8cc4fe6fdc.png" alt="">
|
||||
|
||||
然后按命中次数来计算TPS,就可以得到如下结果:
|
||||
|
||||
$生产TPS = 9278 \div 5 = 1,855.6$
|
||||
|
||||
当然了,你还可以再细化,得到毫秒级的图:
|
||||
|
||||
<img src="https://static001.geekbang.org/resource/image/19/cf/1994cdcb109f360c6b21780e4f1f4dcf.png" alt="">
|
||||
|
||||
通过这种方式,我们就可以得知一个系统在生产环境中的峰值的TPS有多大。1855.6这个值是我们要定的测试环境中总的TPS。如果达不到这个值,那就要接着优化,或者增加硬件资源。
|
||||
|
||||
通常情况下,得到总TPS之后,我们要根据测试目标,分三种方式处理这个总TPS,而这三种方式都是以**业务目标**为前提的。
|
||||
|
||||
1. **业务无变化,应用版本有小变化**(通常只是小的功能变化或修改Bug):在这种情况下,我们只要将计算出来的TPS做为性能场景总的TPS指标即可。
|
||||
1. **业务无变化,应用版本有大变化**(比如说架构变更):在这种情况下,我们只要将计算出来的TPS做为性能场景总的TPS指标即可。
|
||||
1. **业务有变化,应用版本有大变化**:在这种情况下,我们要根据业务估算的增量来做相应的TPS增量计算。如果根据业务变化趋势预估会增加20%,那你就可以在上面计算的总TPS的基础上,再加上对应的20%即可。
|
||||
|
||||
## 梳理业务逻辑
|
||||
|
||||
在上面的接口得到业务模型之后,我们就可以根据接口的量级梳理业务逻辑,以便更真实地模拟生产业务场景。其实在上面的步骤中,我们已经按顺序做了排列,你可以看一下前面的表格。
|
||||
|
||||
所以在这个示例中,大概有58%的用户会完整地走完流程。为什么是58%呢?因为登录的业务比例是12%,而后面下单比例是7%。所以是:<br>
|
||||
$ 7\%\div12\% \approx 58\% $
|
||||
|
||||
## 整体流程说明
|
||||
|
||||
最后我们来梳理一下整体的流程:
|
||||
|
||||
<img src="https://static001.geekbang.org/resource/image/01/61/0140f3ac2e260967e13c95020aafe161.jpg" alt="">
|
||||
|
||||
请注意,上面的业务场景在实际的项目业务统计过程中可以有多个。这个思路可以解决任何性能场景和生产场景不一致的问题。
|
||||
|
||||
## 总结
|
||||
|
||||
最后,我们再一起回顾下这一讲的重点内容。在业务模型抽取时我们要注意几个关键点:
|
||||
|
||||
1. 抽取时间:抽取时间一定要能覆盖生产系统的峰值时间点;
|
||||
1. 抽取范围:抽取的范围要足够大,因为在一些场景中,即便不是峰值,但由于某个业务量较大,也会出现资源消耗大的情况;
|
||||
1. 业务比例在场景中的实现:得到业务模型之后,我们在性能场景中一定要配置出对应的业务比例,不能有大的偏差。
|
||||
|
||||
只要做到以上几点,性能场景基本上就不会和真实业务场景有大的差异了。
|
||||
|
||||
## 课后作业
|
||||
|
||||
学完这节课后,请你认真思考两个问题:
|
||||
|
||||
1. 为什么性能场景中要模拟出真实业务比例?
|
||||
1. 抽取生产数据并最终得到业务模型的步骤是什么?
|
||||
|
||||
欢迎你在留言区与我交流讨论。当然了,你也可以把这节课分享给你身边的朋友,他们的一些想法或许会让你有更大的收获。我们下节课见!
|
||||
|
||||
**关于课程读者群**
|
||||
|
||||
点击课程详情页的链接,扫描二维码,就可以加入我们这个课程的读者群哦,希望这里的交流与思维碰撞能帮助你取得更大的进步,期待你的到来~
|
313
极客时间专栏/高楼的性能工程实战课/性能工程的实践关键点/07 | 性能场景的数据到底应该做成什么样子?.md
Normal file
313
极客时间专栏/高楼的性能工程实战课/性能工程的实践关键点/07 | 性能场景的数据到底应该做成什么样子?.md
Normal file
@@ -0,0 +1,313 @@
|
||||
<audio id="audio" title="07 | 性能场景的数据到底应该做成什么样子?" controls="" preload="none"><source id="mp3" src="https://static001.geekbang.org/resource/audio/ec/a1/ec8bf8yy7f94897d221ab090161977a1.mp3"></audio>
|
||||
|
||||
你好,我是高楼。
|
||||
|
||||
在性能项目中,性能数据是非常重要的输入资源。但是我经常看到有人拿着少得可怜的数据,来做比较大的压力,这显然不符合真实的场景,虽然拿到的结果很好看,但并不会得到什么有价值的结果。所以,今天我们就来讲一下性能场景中的数据到底应该做成什么样子。
|
||||
|
||||
在RESAR性能工程中,场景里使用的数据需要满足两个方面:
|
||||
|
||||
- 第一,数据要符合真实环境中的数据分布,因为只有这样,我们才能模拟出相应的IO操作;
|
||||
- 第二,要符合真实用户输入的数据,以真正模拟出真实环境中的用户操作。
|
||||
|
||||
而这两个方面分别对应着两类数据:铺底数据和参数化数据。我们先来看铺底数据。
|
||||
|
||||
## 铺底数据
|
||||
|
||||
在通常的线上系统架构中,系统中用到的数据分为两部分:静态数据(图中红色点)和动态数据(图中绿色点),这也是我们在性能场景中需要存入的铺底数据。<br>
|
||||
<img src="https://static001.geekbang.org/resource/image/29/c5/290yye70f03665b1ab2a73bfa386eec5.jpg" alt="">
|
||||
|
||||
从这个简单的结构图中不难看出,如果没有铺底数据,那就相当于是一个空系统。但是在生产环境中,这个系统肯定不会是空的,所以要有足够的数据在里面。如果数据不真实,我们就无法模拟出生产上有真实数据的场景,比如应用的内存占用、数据库IO能力、网络吞吐能力等。
|
||||
|
||||
其中,对于静态数据而言,我们最容易出现的问题是,一想到它占的网络带宽大,就觉得要用CDN了 ;或者是觉得不模拟静态数据,就是不符合真实场景,不支持我们的优化结果了。其实,数据放在哪里,怎么做最合理,怎么做成本最低,这些都需要综合考虑,并不是一味跟风,别人怎么做我们就要怎么做。
|
||||
|
||||
我曾经看到有的官方门户网站明明没几个流量,却在做技术规划的时候,非要把零星的几个图片放到CDN上去,以显示自己设计的架构有多先进。
|
||||
|
||||
我也经常看到一些企业认为网站上的图片很重要,出于不懂技术又要寻找安全感的逻辑,非要把图片都放到自己的服务器里。本来图片就很大,一张有3~4M,用户一访问,自然就会吵吵着慢。
|
||||
|
||||
像这两种极端都不可取。要知道,当外行指使内行干活的时候,基本上没什么好结果,因为有些外行觉得只要压力发起就可以了,在细节上根本不在乎结果会怎么样。在我看来,处理这样问题的最合理的方式是先分析业务逻辑,再判断技术架构怎么实现。
|
||||
|
||||
我们知道,静态数据通常有两个可以存放的地方,一个是服务端的Web层,另一个是CDN。对于大系统而言,流量大,网络带宽自然就要求得多。在这种情况下,数据必然要放CDN,你也没有其他选择(当然了,你可以选择不同的CDN厂商)。
|
||||
|
||||
对于一些小的业务系统,由于用的人并不多,整体网络流量要求也少,那我们就可以把静态数据直接放到负载均衡服务器(比如Nginx)或应用服务器中去。用户访问一次之后,后续的访问直接走本地缓存就可以了,对系统的压力也不会产生多大的影响。
|
||||
|
||||
静态数据讲完了,我们再来看动态数据,这就需要我们好好分析一下了,因为有些动态数据是可以放到CDN中的。
|
||||
|
||||
还是前面那张图,在我们不用任何预热加载的情况下,这些动态数据都是存放在数据库中的。当我们使用预热加载时,这些数据就会转到缓存中去(当然,这也取决于架构设计和代码实现),变成下图中这样:
|
||||
|
||||
<img src="https://static001.geekbang.org/resource/image/c1/be/c14441d737781a96bfca1f48bb5225be.jpg" alt="">
|
||||
|
||||
所以按照这样的逻辑,真实场景中业务操作的数据量实际有多少,我们就要模拟出多少,不然会出现一些问题。当模拟数据量与实际数据量差别较大时,会对数据库、缓存等造成不同的影响。下面我列出了五点,为你具体分析一下。
|
||||
|
||||
- **对数据库压力的区别**
|
||||
|
||||
假设线上系统中有100万的用户量,而我们在做压力测试时,由于没有生产数据,造数据又比较麻烦,所以就直接使用1000条甚至更低的用户量来做性能场景。那一个表里有100万条数据和1000条数据的差别是什么呢?我们来实际操作一下。
|
||||
|
||||
在这里,有一个前提条件:同样的硬件环境,同样的数据库,同样的表结构,同样的索引,只是两张表的数据不同。
|
||||
|
||||
两条SQL如下:
|
||||
|
||||
```
|
||||
select * from ob_tuning.temp1_1000 where id = '3959805';
|
||||
select * from ob_tuning.temp2_100w where id = '3959805';
|
||||
|
||||
```
|
||||
|
||||
因为表内的数据量不同,所以结果如下:
|
||||
|
||||
<img src="https://static001.geekbang.org/resource/image/e2/51/e24c66edbd4b6321f903f258626f4e51.png" alt="">
|
||||
|
||||
可以看到,查询时间一个是19ms,一个是732ms。我们不妨再来看一下表的操作细节。
|
||||
|
||||
第一个表(用户量为1000)的操作细节:
|
||||
|
||||
<img src="https://static001.geekbang.org/resource/image/f1/28/f1dfa550a63de0dca8d4bf7a4485a128.png" alt="">
|
||||
|
||||
第二个表(用户量为100万)的操作细节:
|
||||
|
||||
<img src="https://static001.geekbang.org/resource/image/91/8a/916b34e1cb31b3e9f502203a7d762f8a.png" alt="">
|
||||
|
||||
这里我们只需要对比“executing”这一行就能看到明显的差距,它告诉我们当执行这个语句时,需要的CPU时间明显因为数据量的增加而增加了。所以我们不难发现,如果你没有足够的铺底数据放在性能场景中,那一开始便注定了悲剧的结果。
|
||||
|
||||
- **缓存的区别**
|
||||
|
||||
数据量的多少在缓存中有很明显的区别,如下图所示:
|
||||
|
||||
<img src="https://static001.geekbang.org/resource/image/82/16/8224018b4f37f1a75a7977bab88f5a16.jpg" alt="">
|
||||
|
||||
也就是说,场景中用的数据量越多,缓存必然要求越大。
|
||||
|
||||
- **压力工具使用的区别**
|
||||
|
||||
压力工具中使用的数据多少,不仅影响着压力工具本身需要的内存,同时也影响着性能场景的执行结果。这一点,我们会在后面的课程中详细讲到。
|
||||
|
||||
- **网络的区别**
|
||||
|
||||
其实不同的数据量,不管是走缓存,还是数据库,对客户端和服务器之间的网络消耗都是差不多的。只要不是缓存在客户那边,都是要走到服务器里转一圈的。所以我们认为,数据量是多还是少,对客户端和服务器之间的网络的压力没有什么区别。如果你用的是CDN,那可以做另外的考虑。
|
||||
|
||||
- **应用的区别**
|
||||
|
||||
如果不是在应用中直接缓存数据,我们也认为对应用没什么区别,反正不管是什么样的请求过来,都是要到缓存或数据库中去取数据的,应用的Self Time不会有什么差别,方法依旧要执行。但是,如果你的应用是直接在应用的缓存中存数据的,那就有区别了,同样也是数据量越大,对内存的要求就越大。
|
||||
|
||||
基于以上几点,**我们可以看到<strong><strong>有两个**</strong>比较重要**<strong>的**</strong>环节:数据库**<strong>和**</strong>缓存</strong>**,****这是直接的影响。**
|
||||
|
||||
那间接的影响有什么呢?比如说,数据在数据库中执行得慢了,在同步调用的应用中必然需要更多的应用线程来处理。
|
||||
|
||||
我们假设有一个100TPS的系统,先忽略其他时间,只看数据库时间。如果数据库执行需要10ms,那应用只需要一个线程就能处理完了。如果数据库需要100ms,而我们仍然想达到100TPS,那应用就得有10个线程来同时处理。
|
||||
|
||||
<img src="https://static001.geekbang.org/resource/image/17/0c/17f49aeb6bd2551c278124f76f24fc0c.jpg" alt="">
|
||||
|
||||
与此同时,整个链路上的所有线程、队列、超时等都会因为受到数据量的影响而产生大的变化。所以,我们要想模拟出生产时候的样子,在铺底数据上一定不能含糊。
|
||||
|
||||
## 参数化数据
|
||||
|
||||
有了前面铺底数据的分析,我们在做参数化的时候就会明确很多。不过,在场景中应该用多少量的数据,是性能场景中最容易出问题的一个环节。
|
||||
|
||||
参数化数据量应该是多少,取决于场景运行多长时间。而在场景运行中,我们通常要用到两类数据:唯一性数据和可重复使用的数据。
|
||||
|
||||
对于唯一性数据(比如用户数据)来说,我们需要使用多少参数化数据是非常容易计算的。比如一个运行半小时的场景,TPS如果是100的话,那就需要18万的数据量,计算过程如下:
|
||||
|
||||
$数据量 = 30min \times 60s \times 100TPS = 18w$
|
||||
|
||||
对于可重复使用的数据量,我们需要分析真实业务场景中是如何重复的,比如说电商系统中商品的数据量,我们在做参数化的时候就可以重复,毕竟多个人是可以同时购买同一个商品的。我们假设平均有1000个用户在10个商品中,那当我们有18万个用户时,就需要1800个商品:
|
||||
|
||||
$商品数量 = 18w用户 \div 1000用户 \times 10 商品 = 1800 商品$
|
||||
|
||||
上述就是唯一性数据量和可重复使用数据量的计算方式。
|
||||
|
||||
你可能会问,如果参数化数据量太大,在压力工具中处理不了怎么办?比如说我们在用JMeter处理文件参数化数据时,如果参数化文件太长,会导致JMeter消耗更多的时间。其实像这种参数化数据量要求多的情况,我们可以采用连接远程缓存(比如Redis)或数据库(比如MySQL)的方式来做参数化。
|
||||
|
||||
- **连接Redis做参数化**
|
||||
|
||||
**方法一**:直接在JMeter中写Beanshell连接Redis取数据。
|
||||
|
||||
```
|
||||
import redis.clients.jedis.Jedis;
|
||||
//连接本地的 Redis 服务
|
||||
Jedis jedis = new Jedis("172.16.106.130",30379);
|
||||
log.info("服务正在运行: "+jedis.ping());
|
||||
String key = vars.get("username");
|
||||
String value = vars.get("token");
|
||||
vars.put("tokenredis",jedis.get(key));
|
||||
|
||||
```
|
||||
|
||||
**方法二**:使用Redis Data Set组件。
|
||||
|
||||
<img src="https://static001.geekbang.org/resource/image/2f/76/2f7770b642d22d782e1dc21c97e6b176.png" alt="">
|
||||
|
||||
这两种方式都可以用Redis做参数化的数据源。
|
||||
|
||||
- **连接MySQL做参数化**
|
||||
|
||||
**第一步**:创建一个JDBC Connection Configuration。
|
||||
|
||||
<img src="https://static001.geekbang.org/resource/image/a0/36/a0fdd542b00ae70c4dc992d225d18936.png" alt="">
|
||||
|
||||
同时,配置好连接信息,比如用户名密码等:<br>
|
||||
<img src="https://static001.geekbang.org/resource/image/33/71/33a2486b569ddb3c0cbfc63e4e4b0f71.png" alt="">
|
||||
|
||||
**第二步**:创建一个JDBC Request。
|
||||
|
||||
<img src="https://static001.geekbang.org/resource/image/7e/35/7eac9a5f4c647fb4d79677c8a86bab35.png" alt="">
|
||||
|
||||
用JDBC Request把数据取回来:
|
||||
|
||||
<img src="https://static001.geekbang.org/resource/image/4e/ba/4e84085211ff540a1752c3fde04aa2ba.png" alt="">
|
||||
|
||||
**第三步**:用${user_name}引用参数。
|
||||
|
||||
完成上述三步,我们就实现了用数据库的方式做参数化。
|
||||
|
||||
知道了RESAR性能工程中需要什么样的数据后,我们接下来聊一下如何造数据。
|
||||
|
||||
## 如何造数据?
|
||||
|
||||
因为我们这个项目中的电商平台是开源的,数据库也完全是空的,系统中没有任何的数据。所以,我们虽然只是实现了电商的主流程,但需要的数据量仍然不少。这些数据包括:
|
||||
|
||||
- 用户数据;
|
||||
- 地址数据;
|
||||
- 商品数据;
|
||||
- 订单数据。
|
||||
|
||||
下面我们具体考虑一下数据量应该怎么设置。
|
||||
|
||||
根据我们[第5讲](https://time.geekbang.org/column/article/357539)中的性能方案,登录TPS如果是每秒150,并且如果按容量场景的需求,在场景连续递增时,大概在20分钟内(这是一个经验值,在具体的场景执行中也会有变化)会递增到最大值,然后再执行10分钟,也就是说总时间大概为30分钟。
|
||||
|
||||
但是因为场景是递增的,一开始我们并没有要求达到150TPS,同时登录场景TPS最大值能达到多少,我们现在也没法预知。根据经验来看的话,登录的TPS在当前的硬件环境中,就算是不走缓存,达到三、四百应该是没有多大问题的。
|
||||
|
||||
如果按最大400TPS来算,跑半个小时,需要的数据量就是54万,而我们造出来的用户量要远远大于这个量级。这里我们就先造200万的用户量,因为地址的数据量肯定大于用户的数据量,所以会多于200万。
|
||||
|
||||
我们先查一下当前的数据库中有多少数据量,再确定要造多少数据。
|
||||
|
||||
<img src="https://static001.geekbang.org/resource/image/41/f8/41428bf45c683832eaae0893faebcdf8.jpg" alt="">
|
||||
|
||||
这个数据量级明显是不够的,太少了。下面我们来看看怎么造出那么多的数据量。
|
||||
|
||||
我们造的数据主要分为两种:用户数据和订单数据。
|
||||
|
||||
- **登陆用户**
|
||||
|
||||
对于登陆用户数据而言,我们要先了解表结构,因为造出来的数据只有符合业务逻辑才能使用。我们先看一下用户表结构和数据。
|
||||
|
||||
用户表:
|
||||
|
||||
<img src="https://static001.geekbang.org/resource/image/a6/33/a64e039b690b4cdbdd1fe48f29b04c33.png" alt=""><br>
|
||||
<img src="https://static001.geekbang.org/resource/image/eb/f9/ebc31a2f5fe492201b87cebc639ef0f9.png" alt="">
|
||||
|
||||
地址表:
|
||||
|
||||
<img src="https://static001.geekbang.org/resource/image/a4/64/a41f1dd70053c41d89d9688065d3f164.png" alt=""><br>
|
||||
<img src="https://static001.geekbang.org/resource/image/d9/31/d9000ac7563dbe23ce4d80a86e061131.png" alt="">
|
||||
|
||||
根据我的经验,**造数据时不要往数据库里直接写存储过程插数据,除非你非常清楚表之间的关系,并且存储过程又写得非常溜**。否则你会把数据库弄得一团乱,最后不得不在数据库的表里改数据,这是非常被动的做法。在这里,我推荐你使用接口直接调用来造数据,这个操作比较简单,也比较安全。
|
||||
|
||||
如果你想用代码来造数据,那就需要做下面这些分析。
|
||||
|
||||
在这里,我们的用户表和地址表之间是有对应关系的,你可以通过下面这段代码查看到,地址表中的MemberID就是用户ID号。
|
||||
|
||||
```
|
||||
@Override
|
||||
public int add(UmsMemberReceiveAddress address) {
|
||||
UmsMember currentMember = memberService.getCurrentMember();
|
||||
address.setMemberId(currentMember.getId());
|
||||
return addressMapper.insert(address);
|
||||
}
|
||||
|
||||
```
|
||||
|
||||
其实造用户数据就是实现注册流程。你可以先分析下用户注册的代码,直接把其中的注册代码部分拿过来用就行了。具体调用代码如下:
|
||||
|
||||
<img src="https://static001.geekbang.org/resource/image/77/3a/77d58be326b648bda54b701cd083513a.png" alt="">
|
||||
|
||||
看到这里,你可能会想,造数据需要关心注册流程吗?其实如果我们是调接口造数据,就不需要;但如果写代码开启了多线程来造数据,我们就需要了解接口之间的调用关系了。
|
||||
|
||||
下面我们截出中间一部分来分析它们的调用关系:
|
||||
|
||||
<img src="https://static001.geekbang.org/resource/image/8f/1a/8fb4c093f15b4d4ecd9fb0b7d5faff1a.png" alt="">
|
||||
|
||||
因为注册用户表中的密码都是加密的,所以我们可以通过注册用户实现类代码,如下:
|
||||
|
||||
```
|
||||
@Override
|
||||
public void register(String username, String password, String telephone, String authCode) {
|
||||
...............................
|
||||
//获取默认会员等级并设置
|
||||
UmsMemberLevelExample levelExample = new UmsMemberLevelExample();
|
||||
levelExample.createCriteria().andDefaultStatusEqualTo(1) ;
|
||||
List<UmsMemberLevel> memberLevelList = memberLevelMapper.selectByExample(levelExample);
|
||||
if (!CollectionUtils.isEmpty(memberLevelList)) {
|
||||
umsMember.setMemberLevelId(memberLevelList.get(0).getId());
|
||||
}
|
||||
//插入用户
|
||||
memberMapper.insert(umsMember);
|
||||
umsMember.setPassword(null);
|
||||
}
|
||||
|
||||
```
|
||||
|
||||
了解了上面的内容之后,我们就可以直接写一段代码来造用户数据了,具体请见:[《造用户代码.java》](https://github.com/ZeeBJ/-/blob/main/%E9%80%A0%E7%94%A8%E6%88%B7%E4%BB%A3%E7%A0%81.java)
|
||||
|
||||
有了用户数据,我们还需要下单用户的地址等详细信息,只有这样才能完成下单。所以,接下来我们就开始分析怎么造出可以下单的地址数据。
|
||||
|
||||
- **用户地址**
|
||||
|
||||
首先,我们要根据用户地址资源路径找到Controller层,查看用户地址的代码调用关系,如下:
|
||||
|
||||
<img src="https://static001.geekbang.org/resource/image/73/9b/73d55607d9ebb13311d40d1148c63c9b.png" alt="">
|
||||
|
||||
然后找到用户地址的关键代码:
|
||||
|
||||
```
|
||||
@Override
|
||||
public int add(UmsMemberReceiveAddress address) {
|
||||
UmsMember currentMember = memberService.getCurrentMember();
|
||||
address.setMemberId(currentMember.getId());
|
||||
//插入地址
|
||||
return addressMapper.insert(address);
|
||||
}
|
||||
|
||||
```
|
||||
|
||||
从这段代码中,我们可以观察到这几个信息:
|
||||
|
||||
- 调用地址接口需要用户登陆态,通过登陆态来解析用户ID号;
|
||||
- 用户ID号是地址代码中的MemberID号;
|
||||
- 用户ID号是自增加。
|
||||
|
||||
具体参考请见[《造用户地址代码.java》](https://github.com/ZeeBJ/-/blob/main/%E9%80%A0%E7%94%A8%E6%88%B7%E5%9C%B0%E5%9D%80%E4%BB%A3%E7%A0%81.java)。
|
||||
|
||||
通过上面的代码编写,然后再开启Java线程池与多线程,我们就可以把基础数据快速造完了。下面是造用户地址数据的时间记录(每台电脑配置不一样,用的数据也许会有差异):
|
||||
|
||||
<img src="https://static001.geekbang.org/resource/image/f3/8b/f348yya540bc435b8756113298595b8b.png" alt="">
|
||||
|
||||
通过以上手段,我们最后造出如下数据量:
|
||||
|
||||
<img src="https://static001.geekbang.org/resource/image/ea/c1/eac80e522a51b6c357265a53e295cbc1.jpg" alt="">
|
||||
|
||||
表中的订单数据会在做基准场景时补充上去。等这些数据量都有了,我们在容量场景中就有了足够的铺底数据。
|
||||
|
||||
## 总结
|
||||
|
||||
在这节课里,我们一起学习了性能场景中的数据到底应该做成什么样子。对于造数据而言,方法有很多,我们不用拘泥于某种造数据的手段,只要能快速造出足够的数据量就好。在RESAR性能工程中,性能场景需要两类数据:铺底数据和参数化数据。其中,铺底数据需要满足这三个条件:
|
||||
|
||||
- 一定要造出符合生产量级的数据量;
|
||||
- 数据量要真实模拟出生产的数据分布;
|
||||
- 数据要真实可用。
|
||||
|
||||
参数化数据需要满足这两个条件:
|
||||
|
||||
- 参数化数据量要足够;
|
||||
- 要符合真实用户的输入数据。
|
||||
|
||||
有了以上这些知识,我们就不会在造数据时出现混乱的情况了。
|
||||
|
||||
## 课后作业
|
||||
|
||||
这就是今天的全部内容,最后给你留两个思考题吧:
|
||||
|
||||
1. 为什么要造出符合生产量级的数据量?
|
||||
1. 为什么参数化时要用符合真实用户的输入数据?
|
||||
|
||||
记得在留言区和我讨论、交流你的想法,每一次思考都会让你更进一步。
|
||||
|
||||
如果这节课让你有所收获,也欢迎你分享给你的朋友,共同学习进步。我们下一讲再见!
|
439
极客时间专栏/高楼的性能工程实战课/性能工程的实践关键点/08 | 并发、在线和TPS到底是什么关系?.md
Normal file
439
极客时间专栏/高楼的性能工程实战课/性能工程的实践关键点/08 | 并发、在线和TPS到底是什么关系?.md
Normal file
@@ -0,0 +1,439 @@
|
||||
<audio id="audio" title="08 | 并发、在线和TPS到底是什么关系?" controls="" preload="none"><source id="mp3" src="https://static001.geekbang.org/resource/audio/74/c7/74749eae9c12907aecc2182d3037e6c7.mp3"></audio>
|
||||
|
||||
你好,我是高楼。
|
||||
|
||||
在性能领域中,我们经常用“并发用户数”来判断一个系统是否达到性能需求,比如说用“系统支持1000用户”这样的描述来说明性能需求。但是并发是怎么个并发法?它和TPS之间是什么关系?并发用户数和在线用户数又是什么关系呢?
|
||||
|
||||
这样的问题长期以来困扰着性能工程师们。不管是网上看到的文章或者是各个群里的讨论,我们都能听到不同的声音。所以,我即便是冒着引起争论的危险,也要写一下这个问题。
|
||||
|
||||
## 典型的争论
|
||||
|
||||
有一天,有个小伙跟我说,他和同事们看到我上一个专栏[《性能测试实战30讲》](https://time.geekbang.org/column/intro/100042501)中的文章后,在公司会议室吵翻了天,有一个同事还把微积分都搬出来了。我很高兴听到这样的争论,就像战国时期的稷下学宫一样,不争论,哪有那么辉煌的文明高峰呢。
|
||||
|
||||
他们的争论点是这样的:有一个项目,性能目标是对一个底层是Kubernetes、上层是微服务架构的系统进行容量评估(系统性能验证)。而他们的争论点就在于这个容量评估的方法。
|
||||
|
||||
对于评估方法,他们分成了两个流派。第一种是根据DAU(Daily Active User,日活跃用户数量)和用户业务模型,推导出并发用户数(工具中未来的线程数)。而第二种反对第一种,认为第一种估算不合理,要站在服务端层面,去推导 服务端要承载的 并发请求数(TPS)。
|
||||
|
||||
这个小伙说,在两个流派争论的过程中,有一些概念无法达成一致,包括用户、工具中的线程数、TPS和响应时间。后来他又告诉我,第一种流派的评估方法突出用户,但没有考虑用户的动作。第二种流派则从用户操作的角度出发,按照操作频率,计算“用户操作次数/时间段”,以这个为需要达到的TPS,加上他们自己设定的容忍度RT,反过来推算并发用户数。
|
||||
|
||||
在争议的过程中,大家都没说服对方。第一种认为第二种偏差可能会更大;第二种认为,不讲业务指标换算成技术指标就是耍流氓。其中有一个同事,甚至弄出了一个公式:
|
||||
|
||||
$并发用户数 = TPS \times RT$
|
||||
|
||||
所以,这场争论的结果就是:没有结果。
|
||||
|
||||
现在我们不妨思考一下,上面的争论中到底谁对呢?他们给出的公式哪个合理呢?
|
||||
|
||||
我给你举个例子来说(为了简化问题,以下示意图不考虑响应时间的变化):
|
||||
|
||||
<img src="https://static001.geekbang.org/resource/image/a7/23/a7ff0b6856d499dcbf289594fdc70923.jpg" alt="">
|
||||
|
||||
你看,在这个示意图中,压力线程是5个,在1秒内每个线程可以完成5次请求。根据上面的公式,我们应该得到的结论是:
|
||||
|
||||
$并发 = TPS \times RT = 25(事务总数) \times 0.2(响应时间) = 5$
|
||||
|
||||
这个5,显然是并发线程的个数,但是这个并发线程是从用户角度出发的吗?显然不是的。因为从示意图上看,每一个事务才是一个真实的用户。
|
||||
|
||||
这就涉及到一个关键的概念,并发到底是什么?
|
||||
|
||||
在百度百科上是这样描述的:
|
||||
|
||||
>
|
||||
并发,在操作系统中,是指一个时间段中有几个程序都处于已启动运行到运行完毕之间,且这几个程序都是在同一个处理机上运行,但任一个时刻点上只有一个程序在处理机上运行。
|
||||
|
||||
|
||||
在WiKi上是这样描述的:
|
||||
|
||||
>
|
||||
In computer science, concurrency is the ability of different parts or units of a program, algorithm, or problem to be executed out-of-order or in partial order, without affecting the final outcome. This allows for parallel execution of the concurrent units, which can significantly improve overall speed of the execution in multi-processor and multi-core systems. In more technical terms, concurrency refers to the decomposability of a program, algorithm, or problem into order-independent or partially-ordered components or units of computation.
|
||||
|
||||
|
||||
这两个描述看上去有点不太一样,所以我们要理解它们其中的共性和差异。如果你用英文描述的角度来理解并发,我觉得不用运行性能场景就能知道并发是多少,数一下处理器有几个核就行了。如果你用中文描述的角度来理解,那就必须考虑在“时间段”内完成了多少。
|
||||
|
||||
说了这么多,你还要注意一点,**这些描述都是在处理器的层级来描述的。**
|
||||
|
||||
那站在用户的角度,你觉得怎么描述更合理呢?在我看来,更合理的描述是:**并发是在单位时间内完成的事务(T)的个数。如果这个事务(T)是用户的操作,那就是并发的用户了。**
|
||||
|
||||
我们现在再回到前面的那个例子,公式中如果对应的是100TPS,就是100并发了。而不是10个并发,因为10个并发是没有“时间段”的概念的。
|
||||
|
||||
## 行业内的谬传
|
||||
|
||||
在《性能测试实战30讲》中的[第3](https://time.geekbang.org/column/article/178080)[讲](https://time.geekbang.org/column/article/178080),我也描述过并发和在线之间的关系。其中也描述了两个在网上最常见的且被描述成“业界标准”、“经典公式”的计算公式,来自于一篇Eric Man Wong 写的文章——《Method for Estimating the Number of Concurrent Users》。为了给你省些麻烦,我把这两个公式列在这里。
|
||||
|
||||
>
|
||||
<p>平均并发用户数的计算(公式一):<br>
|
||||
$C = nL \div T$<br>
|
||||
其中: C 是平均的并发用户数;n 是 Login Session 的数量;L 是 Login session 的平均长度;T 指考察的时间段长度。</p>
|
||||
|
||||
|
||||
>
|
||||
<p>并发用户数峰值计算(公式二):<br>
|
||||
$C' \approx C + 3\times \sqrt[]{C}$<br>
|
||||
其中:C’指并发用户数的峰值,C 就是上一个公式中得到的平均的并发用户数。该公式是假设用户的 Login session 产生符合泊松分布而估算得到的。</p>
|
||||
|
||||
|
||||
显然上述公式中没有提“在线用户数”。而在原文中,“在线用户数”是假设出来的,包括Little定律,和这两个公式的逻辑是一样的。以上两个公式的问题,我在上一个专栏中已经详细描述了,这里不再啰嗦了,如果你有兴趣,可以去看《性能测试实战30讲》中的[第3](https://time.geekbang.org/column/article/178080)[讲](https://time.geekbang.org/column/article/178080)。只是在上一篇中,我没有对其中的公式进行详细的解释,所以我觉得写得不够完整。
|
||||
|
||||
下面我跟你讲一下这两个公式为什么不能称为“业界标准”、“经典公式”。
|
||||
|
||||
首先,在原文中,作者用这张图表达了用户并发的状态。
|
||||
|
||||
<img src="https://static001.geekbang.org/resource/image/40/5a/40c5169yy32be453de1992092620ed5a.png" alt="">
|
||||
|
||||
并且假设了用户到达率是符合泊松分布的,于是代入了泊松分布的公式。作者因为泊松分布是用于对随机和独立事件的到达率进行建模的最可行、最常用的工具,并且在大部分的统计学书中都能找得到,所以就假设了这个前提。
|
||||
|
||||
但是,这个跳跃直接就把很多情况给过滤掉了,因为你的系统到达率可能不是泊松分布,而是其他分布,比如说这些分布类型:
|
||||
|
||||
<img src="https://static001.geekbang.org/resource/image/8e/c3/8ee5d3a53d5bda36c4e4a6a354f381c3.png" alt="">
|
||||
|
||||
如果想确定你的系统是属于哪种分布,就需要分析用户数据。虽然泊松分布很常用,但对于一个特定的系统来说,还是不行的,比如说地铁系统。2018年,有一篇针对北京地铁客流量的分析论文中就有说明,北京地铁客流量随时间分布如下:
|
||||
|
||||
<img src="https://static001.geekbang.org/resource/image/b8/b0/b82b20a112907373a2d796cb1c780db0.png" alt="">
|
||||
|
||||
针对上述客流进行分析之后,作者得到客流数据是符合伽玛分布的,于是根据不同的高峰时段进行了细分,然后得到如下分布拟合结果:
|
||||
|
||||
<img src="https://static001.geekbang.org/resource/image/15/13/151ayy2f78f2c37823464203796eae13.png" alt="">
|
||||
|
||||
由此,你就可以知道,在文章《Method for Estimating the Number of Concurrent Users》中,假设用户到达率是符合泊松分布的,只描述了一种可能的结果,所以我们前面提到的这两个计算公式,自然也就不能成为“业界标准”。
|
||||
|
||||
>
|
||||
<p>平均并发用户数的计算(公式一):<br>
|
||||
$C = nL \div T$<br>
|
||||
其中: C 是平均的并发用户数;n 是 Login Session 的数量;L 是 Login session 的平均长度;T 指考察的时间段长度。</p>
|
||||
|
||||
|
||||
>
|
||||
<p>并发用户数峰值计算(公式二):<br>
|
||||
$C' \approx C + 3\times \sqrt[]{C}$<br>
|
||||
其中:C’指并发用户数的峰值,C 就是上一个公式中得到的平均的并发用户数。该公式是假设用户的 Login session 产生符合泊松分布而估算得到的。</p>
|
||||
|
||||
|
||||
并且在这篇文章中,作者在后面又将泊松分布近似到了正态分布,而第二个公式就是通过标准正态分布中平均值等于0、标准差等于1,对应到标准正态分布的统计表中查找的结果。
|
||||
|
||||
那么,问题就来了:
|
||||
|
||||
<li>
|
||||
我们的系统是像上图中展示的那样,一个用户和系统没有等待时间的交互吗?这只是假设了一个最简单的场景。如果你的场景不是这个最简单的场景,那公式一就不好使了。
|
||||
</li>
|
||||
<li>
|
||||
在公式二中假设了C是泊松分布的,这就意味着,你要想使用这个公式,首先就得确定在你的系统中,用户的到达率是符合泊松分布的。而我们在系统中做这样的分析时,你会看到,很多系统都是无法满足这个条件的。
|
||||
</li>
|
||||
<li>
|
||||
在原文中也说明了,C说的是平均值。正因为是平均值,所以这个C和并发用户的峰值误差会比较大。
|
||||
</li>
|
||||
<li>
|
||||
公式二是通过将泊松分布近似到标准正态分布中平均值等于0、标准差等于1的情况下得出的,那你想想,你的系统中平均用户数符合这个条件吗?
|
||||
</li>
|
||||
<li>
|
||||
这两个公式实际上是针对一个最简单的业务,进行的假设推导。而我们的系统,可能支持的是多种业务操作,那对于这些业务操作,我们是否需要把每个都算一遍呢?
|
||||
</li>
|
||||
<li>
|
||||
在技术的层面,不管是在线用户,还是并发用户,都是要体现到请求级别的。但是这两个公式显然没有达到这个级别。在原文中,作者是拿请求做了示例,但是这只是用来算请求速率和带宽的。
|
||||
</li>
|
||||
<li>
|
||||
还有一个大问题就是,这种并发用户数估算方法是在一个业务功能上做的。如果一个系统有多个业务功能,那显然就不能这样计算了。
|
||||
</li>
|
||||
|
||||
综上,你就可以知道,所谓的“业界公认”的计算公式,其实有很多的限制条件。并且,我们很难在真实的场景中,把它的这个逻辑套用到自己的系统中去。
|
||||
|
||||
在2011年,有一篇国内的论文《The Estimation Method of Common Testing Parameters in Software Stress Testing》用了切比雪夫不等式来做的计算,你有兴趣也可以去看一下。
|
||||
|
||||
我并不是想否认这些人所做的努力,我只是希望性能从业人员能看清楚问题在哪里。如果你做了各种统计分析之后,发现能够满足原文中的各种假设条件,那上面的公式就可以用。如果不满足,那显然我们不能生搬硬套。
|
||||
|
||||
## **实践出真知**
|
||||
|
||||
既然我们在行业内对并发用户、在线用户、TPS这个关键的关系如此重视,又没有统一可用的落地参考,而一些人的努力也得不到有效的印证。那我们是不是就没有办法了呢?
|
||||
|
||||
当然不是。接下来,我想通过一个具体的实践,让你看到这个关键点的推导逻辑,然后你再来思考如何在自己的系统中落地。
|
||||
|
||||
在这里,我用一个电商系统的下单示例来做操作,请你不要过于关注系统是什么类型的,我希望你能瞪大眼睛看清楚这里面的逻辑。
|
||||
|
||||
我先说明一下,因为我要做的操作是**从用户角度出发的。所以,在这里我搭建了一个有用户界面的系统来做这个示例,这主要是为了给你讲清楚在线用户、并发用户和TPS之间的关系。
|
||||
|
||||
这个示例的前端操作总共有7步,如下所示:
|
||||
|
||||
<img src="https://static001.geekbang.org/resource/image/5e/b5/5ef9785b3657e37bbd9da1953a9490b5.jpg" alt="">
|
||||
|
||||
(注:上图中显示的最后一个图是退出后的界面,没有操作,所以总共是7步操作。)
|
||||
|
||||
现在我们就是要知道这个操作全过程中产生了哪些请求。具体的请求日志如下所示:
|
||||
|
||||
```
|
||||
{"client_ip":"59.109.155.203","local_time":"27/Mar/2021:23:16:50 +0800","request":"GET / HTTP/1.1","status":"200","body_bytes_sent":"23293","http_x_forwarded_for":"-","upstream_addr":"127.0.0.1:8180","request_time":"0.001","upstream_response_time":"0.000"}
|
||||
中间省略98行
|
||||
{"client_ip":"59.109.155.203","local_time":"27/Mar/2021:23:21:00 +0800","request":"GET /resources/common/fonts/iconfont.ttf?t=1499667026218 HTTP/1.1","status":"200","body_bytes_sent":"159540","http_x_forwarded_for":"-","upstream_addr":"127.0.0.1:8180","request_time":"0.259","upstream_response_time":"0.005"}
|
||||
|
||||
```
|
||||
|
||||
这是一个用户从打开首页、登录、选择商品、下单、退出整个流程的全部操作日志,总共100条。我们先不管是静态资源还是接口调用。现在我们主要来说一下,这些请求是怎么转化为TPS的,而TPS和在线用户、并发用户之间又是什么关系。
|
||||
|
||||
## 在线用户和TPS之间的关系
|
||||
|
||||
我们一定要从实际操作的级别来看在线用户和TPS之间的关系才可以,要不然只是臆想,是无法服众的。
|
||||
|
||||
上面我们已经通过一个用户的操作抓取了相应的日志(包括静态资源)。这个用户也显然就是一个在线用户了。
|
||||
|
||||
- **单个在线用户的TPS计算**
|
||||
|
||||
从上面的时间窗口来看,这个用户的整个操作流程是从23:16:50到23:21:00,时间窗口总共是250秒(这么巧,是一个吉利数字),请求总共是100个。但是我们通常都会设置事务的,对不对?这时我们就得来掰扯掰扯事务是怎么定义的了。
|
||||
|
||||
<li>
|
||||
<p>如果你把事务T设置为每个请求一个事务,那显然,你就不用计算了,一个用户需要的就是0.4TPS。对应的TPS计算如下:<br>
|
||||
$1(用户)\times 100(请求数) \div 250(时间窗口) \approx 0.4(请求数/秒)$</p>
|
||||
</li>
|
||||
<li>
|
||||
<p>如果你把事务定义到每个业务操作的级别,对应前面我们说的,总共是7个业务操作,而这7个业务操作是在250秒内完成的,那对应的TPS就是:<br>
|
||||
$1(用户)\times 7(单业务操作级事务)\div 250(时间窗口)\approx 0.028 (TPS)$</p>
|
||||
也就是说如果你把事务定义在业务操作级别,在这个示例中,一个用户就需要0.028TPS。请注意,这里面的每一个事务的请求数并不一致哦。
|
||||
</li>
|
||||
<li>
|
||||
<p>如果你把事务定义到整个用户级别(通常情况下,业务部门会这样要求,因为只有做完了这些步骤才是一个业务完成了),那显然这250秒内只完成了1个事务。那对应的TPS就是:<br>
|
||||
$1(用户)\times 1(完整用户级事务)\div 250(时间窗口) \approx 0.004 (TPS)$</p>
|
||||
</li>
|
||||
|
||||
你看,把事务大小定义在不同级别时,我们得到的结果必然是不一样的。所以,我们如果在项目中只是简单地说,性能需求是要达到多少多少TPS这样的笼统需求,就必然会导致不同的人理解的TPS内容不一样。所以,如果有人让你实现1000TPS,那你就要问,T是什么级别的?
|
||||
|
||||
请你注意,在这个逻辑中,我没有把业务模型加进来一起讨论,因为加了业务模型,反而会让问题变得更复杂。
|
||||
|
||||
- **多在线用户的TPS计算**
|
||||
|
||||
上面的计算是根据一个用户的操作进行的,那如果是另一个用户呢?再操作一遍指定不会是恰好250秒了吧。并且,如果有成千上万个用户呢?那也必然不会全都用250秒。所以,这个前提条件就成了一个难点。
|
||||
|
||||
为此,我们先假设(注意,我这里做的假设只是为了后续的计算方便,并不是说这个假设条件是成立的)系统中有100000用户都是平均250秒完成业务,并且是在一个小时内完成的(这个数据已经非常集中了)。那你就可以计算需要多少TPS了。
|
||||
|
||||
<li>
|
||||
<p>请求级的TPS:<br>
|
||||
$(100000(用户) \times 100(请求数)) \div 3600(秒) \approx 2,777.78(TPS)$</p>
|
||||
</li>
|
||||
<li>
|
||||
<p>单业务操作级TPS:<br>
|
||||
$(100000(用户) \times 7(业务操作))) \div 3600(秒) \approx 194.44(TPS)$</p>
|
||||
</li>
|
||||
<li>
|
||||
<p>用户级TPS:<br>
|
||||
$(100000(用户) \times 1(用户级) \div 3600(秒) \approx 27.78(TPS)$</p>
|
||||
</li>
|
||||
|
||||
通过这样的计算,我们就可以知道需要多少TPS来和在线用户对应。
|
||||
|
||||
- **峰值在线用户的TPS计算**
|
||||
|
||||
显然上面是按一小时内所有的用户都平均分布的方式算的,如果有峰值呢?这个算法就不对了吧?这就是为什么我说要历史业务峰值的原因,具体统计过程请见我们[第6讲](https://time.geekbang.org/column/article/358483)内容。
|
||||
|
||||
线上业务峰值的统计时间段越短,显然是越准确的。如果我们从生产上统计出来10万用户是在1小时内完成的。其中,1万用户在1个小时内的某1分钟内完成业务。这样的数据其实已经达到大型电商的秒杀级别了。那根据上面的计算方式,我们可以得到:
|
||||
|
||||
<li>
|
||||
<p>请求级的TPS:<br>
|
||||
$(10000(用户) \times 100(请求数)) \div 60(秒) \approx 16,666.67(TPS)$</p>
|
||||
</li>
|
||||
<li>
|
||||
<p>单业务操作级TPS:<br>
|
||||
$(10000(用户) \times 7(业务操作))) \div 60(秒) \approx 1,166.67(TPS)$</p>
|
||||
</li>
|
||||
<li>
|
||||
<p>用户级TPS:<br>
|
||||
$(10000(用户) \times 1(用户级) \div 60(秒) \approx 166.67(TPS)$</p>
|
||||
</li>
|
||||
|
||||
**想要得到精确的峰值TPS,其实很明显的前提就是统计的时间段够不够精准。**
|
||||
|
||||
通过以上的计算过程,我们可以知道在包括静态资源的时候,在线用户数怎么转化到相对应的不同级别的TPS。对于不包括静态资源的计算过程,你也可以根据上面的逻辑自行计算。
|
||||
|
||||
## 并发用户和TPS之间的关系
|
||||
|
||||
从上面的在线用户计算示例中,相信你已经发现,在日志中两个操作之间的是有时间间隔的。那如果一个用户在操作的时候没有间隔,TPS应该是多少呢?
|
||||
|
||||
通过JMeter录制浏览器的行为,我们先把同样的操作步骤变成JMeter脚本,然后再回放一下,抓一下日志,看看在没有停顿的时候,一个完整的用户流程需要多长时间。日志如下:
|
||||
|
||||
```
|
||||
{"client_ip":"59.109.155.203","local_time":"28/Mar/2021:01:08:56 +0800","request":"GET / HTTP/1.1","status":"200","body_bytes_sent":"23293","http_x_forwarded_for":"-","upstream_addr":"127.0.0.1:8180","request_time":"0.109","upstream_response_time":"0.109"}
|
||||
中间省略98行
|
||||
{"client_ip":"59.109.155.203","local_time":"28/Mar/2021:01:09:02 +0800","request":"GET /resources/common/fonts/iconfont.ttf?t=1499667026218 HTTP/1.1","status":"200","body_bytes_sent":"159540","http_x_forwarded_for":"-","upstream_addr":"127.0.0.1:8180","request_time":"0.005","upstream_response_time":"0.005"}
|
||||
|
||||
```
|
||||
|
||||
从时间戳上来看,从第一个请求到最后一个请求,共有100个请求,总共用了6秒(请你注意这个响应时间,为了让你看得更清楚,我只截了一个用户的完整请求。实际上这里应该是用压力场景中的包括这些请求的平均响应时间)。
|
||||
|
||||
同样地,我们来计算一下对应的TPS。
|
||||
|
||||
<li>
|
||||
<p>请求级的TPS:<br>
|
||||
$1(用户) \times 100(请求数)) \div 6(秒) \approx 16.67(TPS)$</p>
|
||||
</li>
|
||||
<li>
|
||||
<p>单业务操作级TPS:<br>
|
||||
$1(用户) \times 7(业务操作))) \div 6(秒) \approx 1.17(TPS)$</p>
|
||||
</li>
|
||||
<li>
|
||||
<p>用户级TPS:<br>
|
||||
$1(用户) \times 1(用户级) \div 6(秒) \approx 0.17(TPS)$</p>
|
||||
</li>
|
||||
|
||||
我们可以对应算一下,一个没有停顿的用户(并发用户)相当于多少个有停顿的用户(在线用户)呢?在这个转换的过程中,我们暂时不考虑请求的区别。那么,显然是:
|
||||
|
||||
$16.67\div0.4=1.17\div0.028=0.17\div0.004 ≈ 41.79(倍)$
|
||||
|
||||
你用哪个级别的TPS来算都是一样的。
|
||||
|
||||
这样,我们就清楚了,并发度就是:
|
||||
|
||||
$1(并发用户) \div 41.79(在线用户) \approx 2.4\% (也即是6/250) $
|
||||
|
||||
那么,如果你录制了脚本并且没有设置停顿时间(你可以叫Think Time或等待时间),如果你想支持的是10万在线用户在一小时内完成所有业务,那么支持的对应并发用户数就是:
|
||||
|
||||
$ 100000(在线用户)\times 2.4\% = 2,400(并发用户) $
|
||||
|
||||
而我们一个线程跑出来的请求级的TPS是16.67,要想模拟出10万用户的在线,我们需要的压力线程数就是:
|
||||
|
||||
$2,777.78(10万在线用户时的请求级TPS) \div 16.67(一个压力线程的请求级TPS) \approx 167(压力线程)$
|
||||
|
||||
讲到这里,我们总结一下前面所讲的公式。
|
||||
|
||||
**在线用户数和压力线程之间的关系:**
|
||||
|
||||
<li>
|
||||
用请求级TPS计算:
|
||||
$压力线程 = \frac{(在线用户数 \times 单用户请求数)}{峰值采样时间段} \div 一个压力线程的请求级TPS$
|
||||
</li>
|
||||
<li>
|
||||
用单业务操作级TPS计算:
|
||||
$压力线程 = \frac{(在线用户数 \times 单用户业务操作数)}{峰值采样时间段} \div 一个压力线程的业务操作级TPS$
|
||||
</li>
|
||||
<li>
|
||||
用用户级TPS计算:
|
||||
$压力线程 = \frac{(在线用户数 \times 单用户完整业务数(也就是1)}{峰值采样时间段} \div 一个压力线程的用户级TPS$
|
||||
</li>
|
||||
|
||||
**并发用户数的计算:**
|
||||
|
||||
- $并发用户数 = 在线用户数\times\frac{有停顿时间的单线程TPS}{无停顿时间的单线程TPS}$
|
||||
|
||||
**并发度:**
|
||||
|
||||
- $并发度 = \frac{并发用户}{在线用户} \times 100% (取值要在同一时间段)$
|
||||
|
||||
从以上的计算逻辑中,我们可以看到,这其中有几个关键数据:
|
||||
|
||||
1. 在线用户数。这个值可以从日志中取到;
|
||||
1. 在线用户数统计的时间段。这个值也可以从日志中取到;
|
||||
1. 用户级操作的完整业务流时间(记得多采样一些数据,计算平均时间)。这个值也是从日志中取到;
|
||||
1. 无停顿时间的完整业务流时间。这个值从压力工具中可以取到;
|
||||
1. 单用户完整业务流的请求数。这个值可以从日志中取到。
|
||||
|
||||
## “思考时间”到底怎么用?
|
||||
|
||||
在性能行业中,在线用户和并发用户换算的过程里,有一个概念我们是万万不能跳过的,那就是“思考时间”。因为有太多的人想用思考时间来描述真实在线用户操作时的停顿了,所以,下面我们就来说说这个重要的概念。
|
||||
|
||||
思考时间自从Mercury(LoadRunner最原始的厂商)进入中国市场灌输BTO(Business Technique Optimization)概念时,就随着LoadRunner的普遍使用而渐渐地深入人心。
|
||||
|
||||
但是,如果你想用它,却没有那么容易。
|
||||
|
||||
在前面的示例中,我们看到了一个用户的完整的业务流操作用了250秒,其中就包括了思考时间。对于用户来说是做了7个操作,但是对于系统来说是什么呢?我们先看一下这些操作在时间上的分布。
|
||||
|
||||
<img src="https://static001.geekbang.org/resource/image/14/db/14c42989985487c7af59733820cd63db.png" alt="">
|
||||
|
||||
(注:上图中多出来的请求是一些自动触发的,我们可以忽略掉。)
|
||||
|
||||
你可以看到,每个操作之间实际上都是有间隔的。而这个时间间隔就是我们在性能脚本中经常说的思考时间(Think Time)。如果你想设置思考时间,就得把每两个操作之间的时间间隔拿到。
|
||||
|
||||
并且,注意哦!**你不是只取一个用户的就够了,而是要把大量的真实用户的操作时间间隔拿到**,然后再做平均值、标准方差的计算,最后再配置到压力工具中。
|
||||
|
||||
在我的工作经验中,几乎没有一家企业可以做到这一点的。每当我看到这样的情形时,我都建议他们不要用思考时间了,因为即使用了也并不能说明他们模拟了真实用户的行为。
|
||||
|
||||
## 为什么不能用用户会话的超时时间来计算?
|
||||
|
||||
因为用户的在线时间比较难统计,并且操作的间隔也比较难得到,所以有人提出用用户登录之后的会话(Session)超时时间来做计算。我先画一个示意图,再来给你解析一下。
|
||||
|
||||
<img src="https://static001.geekbang.org/resource/image/b5/ef/b5ccaf9302b7fbea20482fac7099f1ef.jpg" alt="">
|
||||
|
||||
(注:在上图中,一个带箭头的线表示一个完整的用户级的业务流操作。)
|
||||
|
||||
用这个思路来计算并发用户的人,通常都会这样说:
|
||||
|
||||
>
|
||||
你看,一个用户进入系统之后会做一些操作,这时并发是1;但第一个用户还没操作完,第二个用户就进来了,这时的并发就是2;那也有可能用户接着进来,所以并发也有可能变成3.......
|
||||
|
||||
|
||||
是不是看起来非常合理?在我们前面提到的那个《Method for Estimating the Number of Concurrent Users》中就用了这个思维逻辑。那问题是什么呢?问题有两个:
|
||||
|
||||
<li>
|
||||
问题1,你能画出图中的红线吗?显然不能,因为它们是时间点呀!你在系统中做统计的时候,怎么可能拿到时间点的数据呢?不管你的日志记得有多细,就算到纳秒级,那也是时间段。
|
||||
</li>
|
||||
<li>
|
||||
问题2,在系统中,用户的行为可以像图中这样用一条直线来表示吗?显然也不能,从前面我们截的用户操作间隔图中就可以看到,一个用户在操作期间都认为自己是在线的,但是在请求的级别,中间其实是有停顿的。所以,即使一个用户一直在系统中操作,他发出的请求,也不会像水流一样源源不断。
|
||||
</li>
|
||||
|
||||
我们知道,Session是一串保存在用户端和系统中的字符串。在用户和系统交互的过程中,带着Session就可以识别请求。但是,并不是说用户和系统因为Session的存在,就一直有交互并且没有间隔。
|
||||
|
||||
所以,计算Session个数,可以让我们知道有多少用户是在线的,但是,并不是说这些用户和系统有交互。
|
||||
|
||||
对于Session的配置,如果它的有效期是30分钟,那在这30分钟内,用户的操作都会被识别。但是,在这30分钟内,用户并不见得要有请求,就连TCP连接都可能没有保持。对于短连接的系统,请求结束,TCP连接会立即断掉;对于长连接系统,我们要发心跳才能保持连接,不过也仅仅是保持连接,也不见得有数据交互。
|
||||
|
||||
所以,**Session仅仅是消耗着保存字符串的那部分内存,来做用户和系统之间的识别,并不能用来做性能中的并发用户数计算。**
|
||||
|
||||
## RPS和TPS之间到底有没有争议?
|
||||
|
||||
我记得在网上看到过一篇文章,大意是说不建议用TPS(每秒事务数)来衡量系统的性能,而是建议用RPS(每秒请求数)衡量。并且,文章把TPS模式描述为“令人震惊的存在行业多年的误操作”。在我的学员群中,也有过类似的讨论。
|
||||
|
||||
对于RPS和TPS,你可以看到很多人各执一词,并且针锋相对。关键是,这些人居然谁都说服不了谁,然后这个问题就变成了一个哲学问题。
|
||||
|
||||
看了一圈文章之后,如果我理解的没错,大概的争议点是这样的:
|
||||
|
||||
<li>
|
||||
TPS是从压力工具的角度来说的,但是因为TPS会受到响应时间的影响,所以不建议采用TPS模式。
|
||||
</li>
|
||||
<li>
|
||||
在接口串行请求时,由于各种异常问题的出现,TPS不能反映出后端服务的每秒请求数。
|
||||
</li>
|
||||
<li>
|
||||
TPS反映的是业务视角,RPS反映的是服务端视角。
|
||||
</li>
|
||||
|
||||
这些说法看似是成立的,但是有什么误差呢。下面我们来一条一条理解一下。
|
||||
|
||||
<li>
|
||||
在请求-响应的同步逻辑中,TPS必然是和响应时间成反比关系的。那么受响应时间影响,TPS也是合情合理的。而我们要分析的就是,这种响应时间会变长的性能问题。难道用了RPS模式就不关注响应时间了吗?
|
||||
在异步逻辑中,我们要是只关注发送出去多少请求,显然无法反映出系统的完整的处理能力。所以,第一点争论其实是不存在的。
|
||||
</li>
|
||||
<li>
|
||||
即便接口是串行的,并且后端流程长,会在各个节点产生多个请求,那后端请求也肯定是大于压力工具中的TPS的。那在一个固定的场景中,压力工具中的TPS和后端的请求数,不是必然成线性关系吗?
|
||||
如果有异常出现,有报错啥的,导致了后端某些服务的请求变少了,这种情况不就正是我们要分析的性能问题吗? 所以,第二点也是不应该有争议的。
|
||||
</li>
|
||||
<li>
|
||||
这个说法就更奇怪了。本来就没有人把压力工具中的TPS和服务端的RPS混为一谈。这两者是不同的统计手法,为什么会作为争议出现呢?它们本来就在不同的角度,更不应该做为对立的论点呀。所以,第三点也是不应该有争议的。
|
||||
</li>
|
||||
|
||||
我用一个示意图来说明一下请求和TPS之间的关系:
|
||||
|
||||
<img src="https://static001.geekbang.org/resource/image/d7/8e/d70e5d37165f8c53a8989259bbc4028e.jpg" alt="">
|
||||
|
||||
如上图所示,如果压力工具中的一个线程(图中人的位置)发出一个请求(也就是在图中0的位置),系统中共产生了4个请求(图中的0、1、2、3位置)。不管这些请求是同步还是异步,这些请求都是真实存在的。如果再来一个线程,也发同样的一个请求,那系统中必然总共产生8个请求,这个逻辑很清楚。
|
||||
|
||||
如果我们把压力工具中线程的请求做为一个T(压力工具中的事务数),那它对应的后端就应该是4个R(后端请求总数)。请你注意,在压力工具中是无法统计出后端的4个请求的,而且,这也是没有必要统计的。这个统计工作,我们应该留给业务监控、日志监控的系统去做,不用再去增加压力工具的负担。
|
||||
|
||||
显然请求和TPS是线性关系,除非你发的不是这个请求,而是其他的请求,或者是你改变了参数。
|
||||
|
||||
如果你愿意关注后端RPS,就去关注;如果愿意关注压力工具的TPS,也无所谓。但是,**在一个项目的具体实践中,不管是RPS还是TPS,一定要说出来,并且大家都能有同样的理解的。**
|
||||
|
||||
既然TPS、RPS是线性的,那我们实在是没有必要把这两个角度当成是对立面来看待,因为这不仅会增加性能理解的复杂度,也没有实际的价值。也就是说,这根本就不是一个争议点。
|
||||
|
||||
## 总结
|
||||
|
||||
在这节课中,我努力地把在线用户数、并发用户数、并发度和TPS之间的关系做了深入的剖析。如果你在跟不同职位的人沟通时,请注意关心一下他们想说的并发、在线、TPS到底在哪个层级,因为要是不在一个频道上,是无法达成一致结论的。
|
||||
|
||||
在你做性能项目时,如果可以取得其中的关键数据,那就可以根据我们前面讲的相应公式做计算。而这个计算逻辑,不止是对HTTP有效,对任何一个协议也都是有效的。
|
||||
|
||||
在这节课中,我也把在线用户数、并发用户数、并发度和TPS之间存在的误解做了详细的说明,也对一些行业谬传做了深入的解析。从中你可以知道,偏向业务层或TPS层的思路都是不对的,只有将它们关联起来,才是合理的从技术到业务的思考逻辑。
|
||||
|
||||
希望你能理解,并将它们之间的关系真正理透。
|
||||
|
||||
## 课后作业
|
||||
|
||||
这就是今天的全部内容,最后请你思考一下:
|
||||
|
||||
1. 如何获取有效的在线用户的TPS(不管是哪个层级的TPS)?
|
||||
1. 性能场景中不包括静态资源的隐患是什么?
|
||||
|
||||
记得在留言区和我讨论、交流你的想法,每一次思考都会让你更进一步。
|
||||
|
||||
如果这节课让你有所收获,也欢迎你分享给你的朋友,共同学习进步。我们下一讲再见!
|
144
极客时间专栏/高楼的性能工程实战课/性能工程的实践关键点/09 | 如何设计全局和定向监控策略?.md
Normal file
144
极客时间专栏/高楼的性能工程实战课/性能工程的实践关键点/09 | 如何设计全局和定向监控策略?.md
Normal file
@@ -0,0 +1,144 @@
|
||||
<audio id="audio" title="09 | 如何设计全局和定向监控策略?" controls="" preload="none"><source id="mp3" src="https://static001.geekbang.org/resource/audio/b5/31/b54065e44b5b7ce948ec838ea6c23131.mp3"></audio>
|
||||
|
||||
你好,我是高楼。
|
||||
|
||||
纵观软件性能行业的发展历程,十几年前,当性能测试刚刚在国内出现的时候,我们只守着工具,不管是在培训还是在工作中,只要学会了性能测试工具,就可以横行市场。那个时候,会不会使用LoadRunner,就是会不会做性能的标准。
|
||||
|
||||
然而,性能测试行业发展到现在,我们仍然能看到在很多场合中,大家还是在讲性能测试理论和思维,还是在讲性能测试工具的使用和实现。虽然也有性能监控部分的数据说明,但大部分也都只是停留在数据的罗列上,描述一下CPU 90%、内存不足、IO 100M之类的现象。
|
||||
|
||||
至于为什么会是CPU 90%?如何定位到具体的原因?解决方案又是什么?大部分性能工程师都是不知道的,甚至连思路都说不上来。这就是当下行业的现状了。
|
||||
|
||||
前段时间,我看到一个微信群里展开了一场讨论。有一个人去面试性能职位,被问到“某一天夜里,生产上的数据库的CPU突然飙升,该怎么去定位问题原因”。群里议论纷纷,有人说是因为固定的批量执行计划;有人说要看监控数据,看慢SQL等等,总之就是一群人在猜来猜去。
|
||||
|
||||
最后,面试官直接给出答案:因为Redis被击穿,导致数据库压力大,所以CPU高。看到这个答案,当时就有人觉得这和题目中描述的现象并没有什么直接的逻辑关系。
|
||||
|
||||
通过这个事情,我们可以看到,性能监控数据不足带来的问题就是没有分析的证据链。而我一直在强调,从现象到结论要有完整的分析链路,只有这样才是真正的性能分析,否则就是在连蒙带猜做性能。
|
||||
|
||||
现在有很多企业(不管是互联网大厂,还是金融机构等)的监控都看似做得挺全面,但其实并没有监控层级的细化。而不做监控的细化,导致的问题就是,经常会出现需要分析某个数据时,只能重新运行场景去抓取数据,并且还要临时添加监控工具。
|
||||
|
||||
所以基于上述种种现状,今天这节课我要跟你讲一讲如何设计全局定向监控策略,我希望你能明白从全局到定向的思路,事先设计好监控策略的重要性。
|
||||
|
||||
在设计监控策略时,我们第一步是分析架构。通过分析架构,我们要确定有哪些需要监控的点。
|
||||
|
||||
## 分析架构
|
||||
|
||||
我们先列出这个课程所示例的系统中都有哪些机器。
|
||||
|
||||
<img src="https://static001.geekbang.org/resource/image/64/1e/640da7cdb93cbaac52739ed33c1d641e.png" alt="">
|
||||
|
||||
在前面的[第4讲](https://time.geekbang.org/column/article/356789)里,我们已经画出了系统架构,如下所示:
|
||||
|
||||
<img src="https://static001.geekbang.org/resource/image/f2/2f/f2294e162a078c7053ef0887f86b762f.png" alt="">
|
||||
|
||||
从上面的信息中,我们要列出需要监控的组件,也就是下面的这张表格。请你注意,对于上面的各层实例,我们现在只配置了一个,但并不是说我们只需要一个,在后续的测试过程中,当需要增加实例时,我们再增加。
|
||||
|
||||
<img src="https://static001.geekbang.org/resource/image/40/bc/4014b5e22c01d1f499e528ced19898bc.jpg" alt="">
|
||||
|
||||
## 监控工具选型
|
||||
|
||||
基于上面的组件列表,我们接下来要选择相应的监控工具。有一点你要注意,这是我们选择的第一层监控工具,也就是全局监控工具。对于定向监控中需要的工具,我们现在还无法确定,因为定向监控工具取决于性能分析过程中有什么问题。
|
||||
|
||||
在我们通过全局监控计数器发现问题之后,想要定位问题的具体原因是什么,就需要分析更详细的监控数据。但是这一点,全局监控计数器无法做到,所以我们需要选择合适的定向监控工具,得到更细的监控数据,我称之为定向监控。
|
||||
|
||||
不难理解,全局监控和定向监控的区别就是,全局监控是第一层的监控,它可以将一个技术组件的各个模块的关键性能体现出来,比如说操作系统的CPU就是典型的全局监控计数器。
|
||||
|
||||
下面我们来看怎么选择全局监控工具。
|
||||
|
||||
- **全局监控策略和工具选型**
|
||||
|
||||
我们说,全局监控是为了判断整个系统的瓶颈点在什么方向,但并不能给出具体的原因。基于这一点,我们在选择全局监控工具时,要注意几个关键点:
|
||||
|
||||
<li>
|
||||
**数据精准**:这一点非常重要,因为对于性能计数器来说,数据的精准直接决定了下一步的步骤。
|
||||
</li>
|
||||
<li>
|
||||
**成本低**:这里的成本包括费用和人工成本。不管是成型的收费产品、免费产品、自主研发产品,还是组合产品,费用都是容易计算的,我就不多啰嗦了。对于人工成本,我们直接拿员工的工资和时间计算就行。如果是做临时的项目,我建议最好选择比较流行、通用的监控工具。
|
||||
</li>
|
||||
<li>
|
||||
**范围大**:也就是监控工具要足以覆盖全局监控计数器。在[第4讲](https://time.geekbang.org/column/article/356789)中,我们讲了怎么构建性能分析决策树,而监控工具要做的就是,把性能分析决策树列出的计数器都尽量覆盖全。如果工具能力实在有限,又没时间扩展,那就要在选择好工具之后,明确哪些计数器无法监控到。然后在性能分析的过程中,使用命令弥补工具上的不足。
|
||||
</li>
|
||||
<li>
|
||||
**历史数据可保存**:在性能项目中,实时查看性能数据是必要的,而监控的历史数据可保存也至关重要。因为在场景执行结束后,我们做性能分析和性能报告时会使用到历史数据。
|
||||
</li>
|
||||
|
||||
基于这几点,接下来我们就要选出对应的全局监控工具。
|
||||
|
||||
根据这个系统的架构,我们选择的工具要监控到这几个层面:第一层,物理主机;第二层,KVM虚拟机;第三层,Kubernetes套件;第四层,各种应用所需要的技术组件。
|
||||
|
||||
<img src="https://static001.geekbang.org/resource/image/ae/de/aeabb079616dc3c9426d2b8e92f739de.jpg" alt="">
|
||||
|
||||
因此,对应的监控工具如下表所示。
|
||||
|
||||
<img src="https://static001.geekbang.org/resource/image/02/0a/02df644a1c9ae9391141f2a0e03cb40a.jpg" alt="">
|
||||
|
||||
以上工具都是免费、开源的,可以完全满足我们的监控需求,我们只要部署一下就行。对于其中的操作系统监控工具,我们在[第4讲](https://time.geekbang.org/column/article/356789)RESAR性能分析逻辑中就已经说明了它的局限性,你要是忘记了,可以再回顾一下。
|
||||
|
||||
在我们这个系统中,物理机和KVM都是完整的操作系统,所以我们直接用[第4讲](https://time.geekbang.org/column/article/356789)中的node_exporter就可以完全覆盖。但是,往上一层的Kubernetes,我们怎么才能全面监控呢?这里就涉及到Kubernetes的监控套件了。现在,我们来看一个Kubernetes全局监控套件,如下所示:
|
||||
|
||||
<img src="https://static001.geekbang.org/resource/image/cf/c3/cf1cfd1bf86c4b5c43e5a3e61f02b4c3.png" alt=""><br>
|
||||
<img src="https://static001.geekbang.org/resource/image/b7/10/b7f08283d7e4a889e68d0112437ed310.png" alt="">
|
||||
|
||||
类似这样的模板有很多,我就不一一列举了。虽然各个工具展示的方式不同,但都能达到我们全局监控Kubernetes的目标。所以,我们只需要选择一个合适自己业务系统的Kubernetes监控套件就可以了。
|
||||
|
||||
其实,如何选择一个监控套件来实现各层的监控需求,是全局监控的一个难点。在全局监控中,我一直在强调一个词——“分层”。因为在我参与过的项目中,经常有人说:“我们的监控是全的。”但当我自己动手查看时,只看到操作系统级的数据,而其上运行的其他内容就没有了。
|
||||
|
||||
还有一个我亲身经历的例子。我在给一个金融机构做培训时,他们说线上有问题,让我帮着分析一下。同时,他们还胸有成竹地跟我说:“我们的监控数据是很全的,只是不知道问题在哪里。”
|
||||
|
||||
可是,我拿过数据一看,发现没有Java线程级的数据,他们的监控平台也不支持细化到线程级。而从系统的数据来看,这恰巧又是一个线程的问题。于是,他们就只有重新采集数据。等数据再拿过来,问题在哪里一目了然。
|
||||
|
||||
这就是全局监控数据缺失,进而导致分析链路断裂的典型例子。所以,全局监控的完整性是性能分析非常重要的部分。
|
||||
|
||||
- **定向监控策略和工具选型**
|
||||
|
||||
完成了全局监控之后,性能场景就可以运行起来了。但是当我们遇到问题时,我们在全局监控数据中就只能看到第一层的计数器,比如说CPU高、内存不足、IO高、网络带宽大等信息。从这些信息中,我们无法知道做什么样的优化才能使CPU降下来、内存使用变少、IO变低、网络变小。
|
||||
|
||||
所以,这时候我们必须要做定向监控,定向监控就是为了寻找更细节的证据。在RESAR性能工程中,我之所以把数据分为全局和定向,是因为性能分析是有逻辑链路的。如果不做区分,只是一股脑地全看,会让你有一种数据很多,但不知道哪个是关键数据的感觉。
|
||||
|
||||
请你注意,在我的分析理念中,全局和定向是必须分开的。因为对于全局监控数据,我们会一直采集并保存一段时间,这样对系统整体的性能影响并不大。可是,如果我们对定向数据也一直采集的话,就会影响系统整体的性能,比如说线程栈的数据采集、对象的内存消耗采集等等,这些操作其实对性能都有影响,不管工具厂商吹嘘得有多完美,我们在实践中已经有足够的数据可以证明这一点。
|
||||
|
||||
不过,当前市场上的很多监控工具是不区分全局监控和定向监控的。所以,在我们前面罗列的全局监控工具中,你也可以看到定向监控需要的数据。比如说,我们在用JvisualVM监控Java的时候,不仅能看到CPU、JVM、Class、Thread等全局信息,也能看到栈、方法、对象等定向信息。
|
||||
|
||||
对于Java微服务应用,我们用表格中列出的工具,其实就可以看到比较细节的数据了。像方法级、对象级这些,Spring Boot Admin、JvisualVM和其他的一些JDK自带的监控工具等都可以做到。如果我们在使用中觉得哪里不足,还可以再考虑其他的定向监控工具。
|
||||
|
||||
而有了对Spring Cloud微服务的监控工具之后,在提供服务的过程中,我们需要看到的是业务链路,这时上面的对单个微服务进行监控的工具就做不到了。
|
||||
|
||||
所以这里,我用APM工具SkyWalking来链路的监控。
|
||||
|
||||
在SkyWalking中,我们不仅能看到链路图,也可以用它看到更细化的数据。这张图就是SkyWalking中的链路图,我把它定义为全局监控。
|
||||
|
||||
<img src="https://static001.geekbang.org/resource/image/90/fb/90e0794b86b07c6a8991f27cc776f5fb.png" alt="">
|
||||
|
||||
下面这张图是用SkyWalking工具看到的更细化的数据,我把这样的数据定义为定向监控数据。
|
||||
|
||||
<img src="https://static001.geekbang.org/resource/image/74/5d/7470ab205c982e07bd36a38d1bca405d.png" alt="">
|
||||
|
||||
这个图展示的是定向分析的一个中间环节。我们从图中可以看到一个请求对应的每一段的耗时,比如说,一个接口调用另一个接口、JDBC、Redis等后续组件。当我们发现哪一段耗时比较长的时候,就可以到耗时长的那个组件上,根据定向监控的数据接着往下分析了。
|
||||
|
||||
通过上述内容,我们知道了在定向监控时需要哪些数据。所以在我们分析完系统架构之后,也要对定向监控工具进行选型,把需要的工具都准备好,以免出现有问题时无工具可用的情况。不过,**定向监控只是先准备好,不用一开始就使用**,这一点你要切记。
|
||||
|
||||
在这里,我列出了在我们这个示例系统中可能会用到的定向分析工具,我主要考虑是覆盖系统级、代码级、数据库级和缓存级。
|
||||
|
||||
<img src="https://static001.geekbang.org/resource/image/c6/af/c6b0c37d90c86241c7857d798aa45baf.jpg" alt="">
|
||||
|
||||
这样一来,我们在后续的性能分析工作中,就不用再临时抓瞎到处找工具了。
|
||||
|
||||
## 总结
|
||||
|
||||
在我的逻辑中,全局和定向必须要分开,这一点我在前面跟你强调过,不分开就会导致资源浪费,并且我们需要的数据还有可能是缺失的。
|
||||
|
||||
另外,请你注意,监控的全面性直接取决于项目级性能分析决策树的构建,也就是说用什么工具并不是关键,关键在于这些监控工具有没有把性能分析决策树的树叶都覆盖全。
|
||||
|
||||
在选择监控工具时,我们主要考虑的是成本、范围、层次、使用的延续性等因素。只有合理的监控策略和监控工具,才能让性能分析决策树真地落地,才能让性能瓶颈证据链的查找具有可能性。
|
||||
|
||||
最后,我还想提醒你一点,请不要认为监控到技术组件这个层级就足够了,把对应技术组件的模块和模块对应的计数器都覆盖到才是重要的。因为在分析瓶颈的过程中,我们要找到计数器之间的关联性,如果有一个计数器缺失,就会导致分析中断。
|
||||
|
||||
## 课后作业
|
||||
|
||||
这就是今天的全部内容,我给你留了两道题以巩固今日所学,请你思考一下:
|
||||
|
||||
1. 如何判断自己选择的性能监控工具,有没有覆盖全性能分析决策树?
|
||||
1. 为什么不建议选择更多的定向监控分析工具?
|
||||
|
||||
记得在留言区和我讨论、交流你的想法,每一次思考都会让你更进一步。
|
||||
|
||||
如果这节课让你有所收获,也欢迎你分享给你的朋友,共同学习进步。我们下一讲再见!
|
Reference in New Issue
Block a user