TT Lab
开始
学习 学习路径 课程

压力测试

复现并消除瓶颈

在 TT Lab 中继续学习

本实验在真正的 VM 中进行

这个环境不是 Pod,而是由 KubeVirt 启动的虚拟机。Linux 内核独立运行, systemd 会实际管理服务,docker 也不是模拟程序,而是真正的 Docker 引擎。 通过 docker run 启动的容器会成为实际进程,docker exec 和 docker logs 也都可以照常运行。

过去,本实验在 Pod 内运行。由于环境撤销了全部内核权限,启动容器的步骤 无法执行,因此只能学习手动解开镜像归档的变通办法。现在不再需要变通。

有两点需要了解。

目标

特意制造并复现瓶颈,利用分层假设缩小范围,消除瓶颈后以数字确认改进幅度,并计算承担目标流量所需的实例数。产物放在 /root/lt3/ 下。

为什么重要

寻找瓶颈不是依靠感觉,而是按顺序逐级检查:socket buffer → thread pool queue → connection pool wait → DB lock wait → processing。本实验制造的是其中最前端的瓶颈,即“一次只能处理一个请求”的并发限制。此时出现的信号非常重要:即使并发提高 10 倍,吞吐量也几乎不变,只有延迟增加 10 倍。 亲眼看到吞吐量受限时等待时间会相应增加这一关系后,在生产环境遇到 Connection is not available, request timed out after 30000ms 之类的日志时,就会知道首先查看哪里。最后一步还会学习一条原则:不要直接使用测量值。只使用测得最大吞吐量的 70%,其余留作余量。

准备

请先执行 mkdir -p /root/lt3。可用镜像仅限预先下载的 python:3.12-alpine、nginx:1.27-alpine、alpine:3.20、busybox:1.36。

步骤

  1. 创建 /root/lt3/slow.py。要求:使用标准库 http.server 的 HTTPServer(一次处理一个请求);每个请求执行 time.sleep(0.05) 产生 50ms 延迟;响应正文包含 slow-app 字符串。使用 python:3.12-alpine 镜像运行该脚本,容器名设为 lt-slow,host 端口设为 127.0.0.1:8086。curl http://127.0.0.1:8086/ 的响应中必须看到 slow-app。
  2. 以并发 1 进行测量,保存到 /root/lt3/slow-c1.txt。正常情况下,p95 至少为 0.04 秒,吞吐量不超过 40 RPS(50ms 延迟串行处理,理论值约 20 RPS)。
  3. 以并发 10 进行测量,保存到 /root/lt3/slow-c10.txt。需要确认:吞吐量小于并发 1 时的 2 倍(几乎不变),p95 至少增加 2 倍。吞吐量受限时,等待时间会相应增加。
  4. 在 /root/lt3/hypothesis.md 中写出至少 3 个瓶颈候选项,每项以 - 或 1. 开头。候选项必须来自不同层级(并发/thread/worker、CPU、connection pool、lock contention、IO/network 中至少三个层级)。每个候选项还要写明如何验证。
  5. 创建 /root/lt3/fast.py。要求:使用 ThreadingHTTPServer(或 ThreadingMixIn)支持并发处理;保持 50ms 延迟不变;响应正文包含 fast-app。容器名为 lt-fast,host 端口为 127.0.0.1:8087。以并发 10 测量并保存到 /root/lt3/fast-c10.txt,吞吐量必须至少为 slow-c10.txt 的3 倍。
  6. 对 fast app 也以并发 1测量,生成 /root/lt3/fast-c1.txt,然后在 /root/lt3/compare.csv 中整理四行。格式为 app,concurrency,rps,p95,行分别为 slow,1、slow,10、fast,1、fast,10。值必须读取自各结果文件。
  7. 在 /root/lt3/capacity.txt 中写四行。
    • target_rps=300(目标峰值流量)
    • measured_rps= —— compare.csv 中 fast/10 行的 rps
    • safe_rps= —— 测量值的 70%,舍去小数(整数)
    • instances= —— ceil(300 ÷ safe_rps),即向上取整后的整数
  8. 在 /root/lt3/report.md 中编写报告。需要四节:瓶颈原因、证据(测量值)、措施、容量结论。正文必须原样包含改进前吞吐量、改进后吞吐量、所需实例数这三个数字。

参考

启动慢速应用

创建 /root/lt3/slow.py。要求:使用标准库 http.server 的 HTTPServer(一次处理一个请求);每个请求执行 time.sleep(0.05) 产生 50ms 延迟;响应正文包含 slow-app 字符串。使用 python:3.12-alpine 镜像运行该脚本,容器名设为 lt-slow,host 端口设为 127.0.0.1:8086。curl http://127.0.0.1:8086/ 的响应中必须看到 slow-app。

/root/lt3/slow.py 必须是一个为每个请求加入人为延迟、且一次只处理一个请求的 server。响应正文必须包含 slow-app 字符串,容器名为 lt-slow,host 端口为 8086。

在并发 1 下测量 baseline

以并发 1 进行测量,保存到 /root/lt3/slow-c1.txt。正常情况下,p95 至少为 0.04 秒,吞吐量不超过 40 RPS(50ms 延迟串行处理,理论值约 20 RPS)。

保存到 /root/lt3/slow-c1.txt。加入 50ms 延迟后,p95 应至少为 40ms,吞吐量应在每秒 20 次左右。数值异常时,请先检查目标端口。

将并发提高 10 倍

以并发 10 进行测量,保存到 /root/lt3/slow-c10.txt。需要确认:吞吐量小于并发 1 时的 2 倍(几乎不变),p95 至少增加 2 倍。吞吐量受限时,等待时间会相应增加。

保存到 /root/lt3/slow-c10.txt。串行处理时,吞吐量几乎不变,等待时间则会增加。如果同时观察到这两点,说明瓶颈在并发能力。

提出瓶颈假设

在 /root/lt3/hypothesis.md 中写出至少 3 个瓶颈候选项,每项以 - 或 1. 开头。候选项必须来自不同层级(并发/thread/worker、CPU、connection pool、lock contention、IO/network 中至少三个层级)。每个候选项还要写明如何验证。

在 /root/lt3/hypothesis.md 中写出来自不同层级的三个候选项及各自验证方法。按顺序检查请求处理路径上的 queue,可避免候选项重复。没有验证方法的假设不算假设。

改为并发处理

创建 /root/lt3/fast.py。要求:使用 ThreadingHTTPServer(或 ThreadingMixIn)支持并发处理;保持 50ms 延迟不变;响应正文包含 fast-app。容器名为 lt-fast,host 端口为 127.0.0.1:8087。以并发 10 测量并保存到 /root/lt3/fast-c10.txt,吞吐量必须至少为 slow-c10.txt 的3 倍。

/root/lt3/fast.py 必须能并发处理请求。保持延迟本身不变。本次改进的核心不是降低延迟,而是消除等待。容器名为 lt-fast,端口为 8087。

改进前后对照表

对 fast app 也以并发 1测量,生成 /root/lt3/fast-c1.txt,然后在 /root/lt3/compare.csv 中整理四行。格式为 app,concurrency,rps,p95,行分别为 slow,1、slow,10、fast,1、fast,10。值必须读取自各结果文件。

fast app 也要再以并发 1 测量一次,然后在 /root/lt3/compare.csv 中整理四行。值必须读取自各结果文件。

计算所需实例数

在 /root/lt3/capacity.txt 中写四行。

在 /root/lt3/capacity.txt 中写入目标、测量值、安全吞吐量和实例数。直接使用测得的最大值将没有余量,而实例数必须向上取整。

编写分析报告

在 /root/lt3/report.md 中编写报告。需要四节:瓶颈原因、证据(测量值)、措施、容量结论。正文必须原样包含改进前吞吐量、改进后吞吐量、所需实例数这三个数字。

在 /root/lt3/report.md 中写明原因、证据、措施和容量结论。证据必须是数字而不是句子,因此请把前面步骤得到的值原样写入正文。