后端笔记08 | 高并发系统稳定性:压测理论学习及代码实践

高并发系统稳定性:压测理论学习及代码实践

1. 参考资料

迈向及格线的开发必备的JMeter性能测试总结1:基础概念与环境搭建

如何压测?2 个案例带你入门压测!

一次压测引发的数据库CPU飙升…

手把手教你压测

JMeter 压测工具的配置和使用

谈谈压测方案的那点事 | 京东物流技术团队

Prometheus + Grafana 监控,验证 Hystrix 超时熔断

你们线上 QPS 多少?你 怎么知道的?

2. 内容设问

  1. 什么是压力测试&性能测试?
  2. 压力测试和性能测试的关系是什么?
  3. 性能测试包含哪些类型?
  4. 压力测试的目的是什么?
  5. 性能测试核心指标类型有哪些?
  6. 性能测试核心指标的关系图?
  7. 计算压测指标举例
  8. 何时进行压力测试
  9. 压测的一般步骤
  10. 思考:如何确定系统需要进行扩容?
  11. 思考:反射对性能的影响?
  12. 如何知道线上的QPS?【基于grafana和Prometheus的例子】
  13. 进行压测的方式?

3. 正文

1. 什么是压力测试&性能测试?

  • 概念说明

压力测试(Stress Testing) 针对系统或者组件,为要确认其稳定性而特意进行的测试。测试系统的极限承受能力,通过将系统暴露于超负荷或极端条件下,观察系统在异常情况(如超负载、高并发、大数据量)下的表现。

性能测试(Performance Testing) 是测试系统在正常负载下的响应速度、吞吐量和资源利用率等,评估其性能 瓶颈。

  • 性能测试 & 压力测试的关系

① 性能测试的压力测试的基础

性能测试通常是渐进式的,通过模拟正常的业务负载来逐步增加负载,从而评估系统性能。是对系统常规负载下的性能进行评估, 根据性能测试的结果可以进行压力测试,看超出负载后会出现哪些瓶颈。

② 压力测试是对性能测试的延申和补充

性能测试关注的是系统在正常使用情况下的表现, 而压力测试则将系统置于极限负载下,验证系统是否能承受更高的并发量、大流量或者长时间运行等异常情况。

  • 性能测试类型包括哪些类型

基准测试:单用户理想环境下的性能基准

负载测试:逐步增加并发用户直至预期最大值

压力测试:超过系统设计容量的极端测试(如双十一流量3倍的模拟)

尖峰测试:瞬间流量暴涨测试(1秒内从100QPS到10000QPS)

耐久测试:持续72小时以上的长时间稳定性测试

2. 压力测试的目的是什么?

找出系统的承载极限:找出性能短板,针对性优化,估计系统的性能上限

测试系统能承受的最大负载、并发量和数据量,找出系统的性能瓶颈崩溃点。主要关注的是吞吐量和时延。

② 测试系统的稳定性和韧性:负载增加,各性能指标的变化情况是否有异常

验证系统在超负荷或极端情况下的表现,是否能够稳定运行,或者在出现异常时能否平稳恢复

③ 测试系统的容错机制:对熔断、限流等高并发策略进行测试,判断系统在高并发下是否会报错,进程是否会挂掉

确保系统能在出现错误或崩溃时,正确地进行容错恢复。(比如对熔断机制、限流、降低等的测试)

3. 压测核心性能指标有哪些?

  • 核心指标:

响应时间RT、吞吐量、资源利用率、并发用户数UV、错误率,主要指标分类如下。

  • 系统能力指标:并行和并发
指标解释健康阈值
并发处理器逻辑上同时处理多个任务的能力。通常 50-100 并发线程,具体值依硬件和系统优化能力而定。
并行多个处理器物理上同时处理多个不同的任务。根据处理器数量和硬件资源,通常为 CPU 核心数
  • 吞吐量类指标:QPS、TPS、事务
指标解释健康阈值
QPS (Queries Per Second)每秒处理请求的数量。根据系统容量,常见阈值:500-5000 QPS。超出此值需要扩展。
事务用户一次或几次请求的集合。通常与 QPS 相似,具体值依据负载量和事务大小
TPS (Transaction Per Second)每秒处理事务的数量。视系统和应用而定,健康范围大致为 200-1000 TPS
  • 响应时间指标:RT、P99
指标类型解释健康阈值
RT (Response Time)响应时间类指标请求到响应所需的时间。通常 < 500ms:延迟低于此值表示良好,过高则影响用户体验。
P99 (99th Percentile)响应时间类指标99%的请求响应时间小于某个值,主要用于评估高并发时的尾部延迟。一般 < 2秒:P99 延迟大于 2 秒时说明系统尾部请求性能问题。
  • 可靠性指标:错误率指标
指标解释
错误率请求成功与请求失败的比率。
  • 机器性能指标:用于了解系统的运行状态,识别性能瓶颈,优化资源分配,确保系统在高负载下仍能稳定运行。
机器性能解释健康阈值
CPU 使用率CPU 使用率高,意味着系统处理请求的能力达到了瓶颈。高 CPU 使用率会导致系统响应延迟。不超过 80%-85% :如果持续高于该值,可能导致系统负载过重、响应延迟增大,甚至崩溃。
内存使用率过高的内存使用率可能导致频繁触发 swap,导致性能下降,甚至出现系统崩溃。不超过 75%-80% :过高的内存占用可能导致性能下降。 如果使用 swap 操作,性能会严重影响。
磁盘 IO在数据库密集型应用中,磁盘瓶颈可能导致显著的性能下降。频繁的磁盘读取/写入会影响整体响应时间。磁盘带宽占用不超过 70%-80% :如果持续接近 100%,可能会导致磁盘 I/O 阻塞,从而影响数据库性能。
网络带宽如果带宽过低,可能导致请求的延迟增加或丢包,影响数据的传输速度和服务响应。不超过 70%-80% :超出该值会导致带宽瓶颈,影响请求响应速度和稳定性。 需要定期监控并进行扩展。
  • 访问指标:用于衡量网站的流量和活跃度。

同时,分析 PV/UV 比(即每个独立访客的页面浏览量),可以帮助判断是否存在重复访问的趋势,以及是否需要增强内容吸引力或优化用户体验。

除此之外,还有DAU(Daily Active User),日活跃用户数量;MAU(Month Active User),月活跃用户数量。

指标解释健康阈值
PV (Page Views)页面浏览量,指某页面被访问的总次数。一个用户访问同一页面多次,会多次计算。根据具体的业务需求、网站的规模和流量预期而定。通常,每日 PV 数量要在百万级别或更高,特别是在高流量的网站中
UV (Unique Visitors)独立访客,指在一定时间内访问网站的独立用户。一个用户多次访问只计算为一个 UV。根据行业平均水平和预期流量设置,常见的阈值是每天访问量在几万到几十万 UV。 具体值视业务目标而定。

4. 性能测试指标关系图

四种性能指标: 响应时间(RT)、并发用户量(UV)、吞吐量(Throughput)和资源利用率(Utilization)。

图片记忆【源于link:迈向及格线的开发必备的JMeter性能测试总结1:基础概念与环境搭建 :2333:即两个点、三条线、三个区、三个状态。

① 三条线: 吞吐量、响应时间、利用率

② 两个点: 最优并发用户数(并发情况能够实现最佳性能的用户数)、最大并发用户数(能够处理的最大负载量下,系统可以不崩溃或者发生严重错误的最大并发数)

③ 三个区:轻负载、重负载、塌陷区(无法处理请求、 RT急剧增加、资源利用率过高)

④ 三个状态: 资源饱和、吞吐下降、用户受影响
在这里插入图片描述

5. 计算压测指标举例

压测我们需要有目的性的压测,这次压测我们需要达到什么目标

(如:单台机器的性能为 100 QPS ?网站能同时满足 100W 人同时在线。此次指标为吞吐量指标和访客指标)

压测原则:每天 80% 的访问量集中在 20%的时间里,这 20% 的时间就叫做峰值时段, 这个时间也就是系统负载最重的时候。

基于帕累托原则得出结论,认为大多数情况下,80%的结果或者影响来自于20%的原因或者输入。

因此可以计算预期峰值QPS:

每天秒数通常为86400 秒(24 小时)

预期峰值 QPS = 总 PV 数 × 80 % 每天的秒数 × 20 % \text{预期峰值 QPS}=\frac{\text{总 PV 数}\times80\%}{\text{每天的秒数}\times20\%} 预期峰值 QPS=每天的秒数×20% PV ×80%

需要的机器的数量 = 峰值时间每秒钟请求数(QPS) / 单台机器的 QPS

示例:

网站每天的用户数(100W),每天的用户的访问量约为 3000W PV,这台机器的需要多少 QPS ?

答:(30000000*0.8) / (86400 * 0.2) ≈ 139 (QPS)

计算所需机器支撑: 单台机器的的 QPS 是 58,需要需要多少台机器来支撑?

答:139 / 58 ≈ 3.

6. 何时进行压力测试

① 旧的接口优化:对比优化前后的性能指标,是否有优化效果,来充分应对不断增长的用户量。

② 新接口需要评估业务预估调用量:常规的操作是上线前对系统进行一个摸高压测。根据预估的流量,对系统配置进行优化调整,保证运行期间,系统能正常运行。

7. 压力测试的一般步骤?

① 需求分析:明确需量化的指标

明确定义性能指标,比如RT、QPS、CPU占有率和UV等。

② 基于性能指标定义到实际的性能场景以及测试数据的量级

根据业务需求和系统负载预期,​设计与实际业务场景相符的测试用例。

性能场景如单一业务场景(只测试登录等单个功能的性能)和混合场景(测试用户同时进行多个操作的功能)。

③ 根据不同的场景编写脚本

根据单一场景或者业务场景等编写测试的脚本。

④ 执行脚本:开始压测,关注关键指标的变化

⑤ 性能诊断: 内存、CPU、磁盘 I/O、网络 I/O 等

8.思考:如何确定系统需要扩容的时机?

(一)技术指标触发扩容

① 资源利用率持续高位

关注CPU、内存、磁盘I/O、网络带宽等指标,超过健康阈值

② 服务性能瓶颈

响应时间变慢、错误率上升、队列堆积等

③ 容量限制接近阈值

数据库连接数、存储空间剩余不足等

(二) 业务需求触发扩容

① 用户增长与流量趋势

日活用户(DAU)、请求量(QPS/TPS)持久上升,超出当前系统承载能力;

业务计划上线新功能、 营销活动(如双11、秒杀)或进入新市场,预期流量激增。

② 用户体验下降

  • 用户投诉增加(如页面加载慢、支付失败)。

  • 关键业务指标(如转化率、留存率)因性能问题下降。

③ 业务连续性要求

  • 系统需满足高可用性(如SLA 99.99%),当前架构存在单点故障风险。

  • 灾备需求(如跨地域部署、冗余扩容)。

(三) 总结扩容决策流程

监控报警触发 → 分析具体瓶颈(是CPU、网络还是代码问题?)

评估业务影响 → 是否影响核心功能或用户体验?

选择扩容方案 → 临时扩容(云实例+自动伸缩)还是长期扩容(采购硬件)?

实施与验证 → 扩容后持续监控,确保问题解决且无副作用

9. 思考:反射对性能的影响

① 反射操作对性能的影响

  • 直接调用 vs 反射调用(Java基准测试)

反射调用耗时可能比直接调用高 100倍以上,尤其在循环或高频调用时更为显著。

// 直接调用
obj.method(); // 平均耗时 1 ns

// 反射调用
Method method = obj.getClass().getMethod("method");
method.invoke(obj); // 平均耗时 100-1000 ns

  • 字段访问

反射访问字段的速度通常比直接访问慢 10-50倍

② 性能瓶颈来源

  • 类型解析开销

元数据查找:反射需要在运行时动态解析类、方法或者字段的元数据,比编译时静态绑定慢。

安全检查: 反射回调用验证访问权限,增加额外开销。

  • 方法调用与字段访问

间接调用:反射调用方法(如Method.invoke()​)或访问字段时,需通过JVM内部机制,无法享受编译器优化(如内联)。

装箱/拆箱:若反射操作涉及基本类型(如int​),可能触发频繁的装箱拆箱(如转为Integer​),影响性能。

  • 缓存缺失:反复解析

未缓存反射对象(如Method​、Field​)时,重复解析同一元数据会导致冗余开销。

③ 优化策略:核心在于减少运行时解析次数和绕过安全检查

  • 缓存反射对象:避免重复解析元数据
// 缓存Method对象
private static final Method CACHED_METHOD;

static {
    try {
        CACHED_METHOD = MyClass.class.getMethod("method");
        CACHED_METHOD.setAccessible(true);
    } catch (NoSuchMethodException e) {
        throw new RuntimeException(e);
    }
}

  • 绕过安全检查:减少对私有成员访问时调用权限验证
Field field = MyClass.class.getDeclaredField("privateField");
field.setAccessible(true); // 仅需一次设置

  • 在以下场景避免对反射的使用

高频调用的核心逻辑(如游戏循环、算法核心)。

对延迟敏感的系统(如金融交易、实时控制)。

10. 如何知道线上的QPS?

① 为什么要监控QPS?

QPS 是衡量后端服务性能的关键指标,可以帮助你及时发现性能瓶颈和问题。

② 所需要的工具:

Prometheus:开源监控框架,用于收集和存储时序数据;

Grafana:开源的可视化工具,用于创建和共享交互式仪表盘,展示Prometheus等监控系统收集的数据;

③ 如何可视化QPS的数据?

在 Grafana 中创建一个仪表盘,添加一个图形面板,显示 my_app_requests_total{method=“GET”} 度量标准,并且可以添加红线标记阈值(Thresholds)。

④ 代码实践

参考链接:Prometheus + Grafana 监控,验证 Hystrix 超时熔断

注意点】

① 在根pom.xml文件中添加Actuator和Prometheus的依赖以及Hytsrix依赖

   <dependency>
            <groupId>com.netflix.hystrix</groupId>
            <artifactId>hystrix-javanica</artifactId>
            <version>1.5.18</version>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-actuator</artifactId>
        </dependency>
        <dependency>
            <groupId>io.micrometer</groupId>
            <artifactId>micrometer-registry-prometheus</artifactId>
        </dependency>

② 用docker容器化技术在服务器部署Prometheus和Grafana

注意: 两者以及启动应用程序需要在同一个网络,此处自定义network。

并且,在项目的docs目录下Prometheus和grafana目录下对应文件顺序不变,要根docker-compose文件容器的volumns相对应。

version: '3'  # 指定 docker-compose 文件的版本为 3,适用于大多数场景

networks:
  app-net:
    driver: bridge
# 服务定义部分
services:
  # Prometheus 服务:用于数据采集
  prometheus:
    image: bitnami/prometheus:2.47.2  # 使用 bitnami/prometheus 镜像,版本为 2.47.2
    container_name: prometheus  # 容器名称为 prometheus
    restart: always  # 容器停止时自动重启
    networks:
      - app-net
    ports:
      - 9090:9090  # 将宿主机的 9090 端口映射到容器的 9090 端口,方便访问 Prometheus 服务
    volumes:
      - ./etc/prometheus/prometheus.yml:/etc/prometheus/prometheus.yml # 将本地配置文件映射到容器内的配置文件,定制 Prometheus 配置

  # Grafana 服务:用于展示 Prometheus 收集的数据
  grafana:
    image: grafana/grafana:10.2.0  # 使用 grafana 镜像,版本为 10.2.0
    container_name: grafana  # 容器名称为 grafana
    restart: always  # 容器停止时自动重启
    networks:
      - app-net
    ports:
      - "4000:4000"  # 添加 Grafana 端口映射
    depends_on:
      - prometheus  # 依赖于 Prometheus 服务,确保 Prometheus 在 Grafana 启动之前启动
    volumes:
      - ./etc/grafana/provisioning:/etc/grafana  # 将本地配置文件映射到容器内的配置文件,用于定制 Grafana 配置

③ grafana相关定义

a/ 用docker部署时,数据源的url的地址是prometheus容器的名称 + 端口号;

apiVersion: 1

datasources:
  - name: Prometheus
    type: prometheus
    url: http://prometheus:9090
    access: proxy
    isDefault: true

b/ 对dashboard定义的json文件

datasource要修改为自己在grafana中创建的文件的uid:通过connections中打开对应datasource,其url中 …/edit/xxx,edit后面的内容就是uid。

expr: 其job名称要跟prometheus中定义的一致。

      "targets": [
        {
          "datasource": {
            "type": "prometheus",
            "uid": "bf0bc282-d985-449b-bc12-b9bd4a41677a"
          },
          "disableTextWrap": false,
          "editorMode": "code",
          "expr": "tomcat_connections_config_max_connections{job=\"dev-test\"}",
          "fullMetaSearch": false,
          "includeNullMetadata": true,
          "instant": false,
          "legendFormat": "__auto",
          "range": true,
          "refId": "A",
          "useBackend": false
        }
      ],

④ 对Prometheus的抓取地址

Prometheus.yml文件中,targets的地址:

如果在服务器部署了对应的容器,则需要在服务器部署对应的app;

如果在本地用docker部署的容器,targets则通过cmd指令ipconfig查看本地的ipv4地址进行替换。

但是服务器部署容器不能访问本地的springboot应用地址, 因为服务器属于外网,无法访问本机内网。(使用cpolar进行内网穿透会出现targets格式问题)

global:
  scrape_interval: 15s # 全局配置:抓取数据的间隔为15s

scrape_configs:
  - job_name: 'dev-test'
    metrics_path: '/actuator/prometheus'
    static_configs:
      - targets: ['hystrix-test-app:8091']

11. 进行压测的方式

① 简单方式: 通过APIPost一键压测。

② 专业方式: JMeter进行压测

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值