如何通过 k6 和 JMeter 测量代理的吞吐量与并发上限
通过代理工作时,几乎总会遇到一个问题:在延迟飙升、错误开始出现之前,您的“客户端—代理—目标服务器”链路到底能承受多少并发请求?本指南将教您提前测量这一点,而不是等到大规模数据抓取进行时才发现问题,那时损失的是数小时的停机时间。
引言:为什么要提前知道上限,而不是在大规模任务进行时
想象一个典型的场景。您通过代理池启动一个大型公开数据采集任务。在最初几千个请求时一切顺利,但随后延迟开始增加,部分请求因超时而失败,而您不知道——问题出在您的代码、代理还是目标服务器。到这个时候,您已经损失了时间和数据。
更明智的做法是提前进行可控的负载测试,找到性能拐点。这样您就能选择合适的并发线程数,并正确规划数据采集时间。
读者最终会收获什么
完成本指南后,您将能够独立完成:
- 在k6中编写正确的负载测试脚本,并通过代理运行。
- 在JMeter中复现同样的测试,并在测试计划中配置代理。
- 解读报告:理解RPS(每秒请求数)、百分位延迟和错误率。
- 区分代理上限、客户端上限和目标服务器上限。
- 将结果换算为线程数、代理池大小和预计采集时间。
本指南适合谁
本内容面向中级工程师、数据分析师和开发者。如果您已经编写过简单的 JavaScript 脚本或从命令行运行过程序,那么您完全没问题。对于初学者,我们会用通俗的语言解释每个术语。
您需要提前了解什么
只需具备基本技能:如何打开终端、安装程序以及编辑文本文件。了解 HTTP 的“请求和响应”概念会是加分项,但并非必需。
需要多少时间
安装工具需要大约 30-40 分钟。一个小时内您就能在 k6 中运行第一个测试。完成整个 JMeter 和分析流程需要 3-4 小时从容工作。
⚠️ 注意:本指南中的所有示例均用于测试您自己的环境或您获得明确授权的资源。未经同意对他人服务进行负载测试是不可接受的。我们将在文末专门讨论道德问题。
前期准备:工具、要求和访问权限
在开始测量之前,我们需要搭建工作环境。这里列出所有必需项,并演示如何安装每个组件。
所需工具和访问权限
- k6 — 轻量级负载测试工具,脚本使用 JavaScript 编写。
- Apache JMeter — 基于 Java 的经典图形化测试工具。
- Java 17 或更高版本 — 运行 JMeter 所必需。
- Proxeon 代理访问权限:地址、端口、用户名和密码。这些信息可在服务控制面板中获取。
- 测试目标 — 您自己的环境或经授权的资源。
系统要求
推荐使用 4 核 CPU、8GB 内存的机器。对于高负载(数千虚拟用户),建议使用 8 核、16GB 内存。操作系统不限,所有工具均为跨平台。
建议:在独立机器或云服务器上运行负载生成器。这样就不会把笔记本的负载和代理的负载混为一谈。
安装 k6
- 根据系统选择官方安装方式:macOS 使用 Homebrew 包管理器运行 brew install k6。
- Windows 使用 Chocolatey 包管理器:choco install k6。
- Linux 从项目仓库下载安装包,通过系统包管理器安装。
- 运行 k6 version 验证安装 — 您将看到版本号。
✅ 验证:如果 k6 version 输出版本号且无“命令未找到”错误,说明安装成功。
安装 Java 和 JMeter
- 安装 OpenJDK 17 或更高版本,运行 java -version 验证。
- 从 Apache JMeter 官网下载安装包。
- 解压到合适目录,例如主目录。
- 进入解压后 JMeter 文件夹中的 bin 子目录。
- 运行 jmeter(Linux/macOS)或 jmeter.bat(Windows)。
- 等待图形窗口出现,左侧显示测试计划树。
✅ 验证:如果 JMeter 窗口打开,左侧树中出现“Test Plan”项,则一切就绪。
备份与环境准备
如果测试自己的服务,请提前为环境创建快照或备份。负载不应损害生产环境。尽量使用环境副本,而不是生产服务器。
基础概念通俗解释
要读懂报告并避免混淆,我们先解释一下关键概念。
吞吐量和 RPS
吞吐量 — 系统在单位时间内完成的有效工作量。在 HTTP 语境下,通常用RPS(每秒请求数)来衡量。在可接受的延迟前提下,RPS 越高越好。
延迟和百分位
延迟 — 从发送请求到收到响应的时间。单个平均值具有误导性,因为偶发的慢请求会被平均掉。因此,我们使用百分位。
p95 百分位意味着:95% 的请求比这个值快,5% 更慢。p99 百分位反映最慢请求的行为。p95 和 p99 最能反映负载下的真实用户体验。
错误率和性能拐点
错误率 — 失败请求的百分比,包括超时、连接拒绝、5xx 状态码。性能拐点 — 负载增加不再提高 RPS,反而导致延迟和错误急剧上升的点。这就是我们寻找的上限。
并发和虚拟用户
并发 — 同时执行的请求数量。k6 中用VU(虚拟用户)表示,JMeter 中用线程组的线程数表示。一个 VU 或线程按顺序发送请求,多个同时运行则产生并发负载。
负载链路中的代理
代理是位于中间层的节点,您的请求经过它转发。它有自己同时连接数和吞吐量的上限。我们的目标是确定并发达到什么水平时,这个节点会成为瓶颈。
提示:记住利特尔公式:同时请求的平均数约等于 RPS 乘以平均延迟(以秒为单位)。它可以帮助您快速估算所需并发。
第 1 步:准备测试目标和代理数据
本阶段目标:获取可用的目标地址,并构建正确的代理连接字符串。
- 确定要压测的目标 URL,建议使用您自己的环境,例如 https://stend.local/api/health。
- 确认目标在单次请求时响应快速且稳定,可通过浏览器或 curl 工具测试。
- 从 Proxeon 控制面板获取代理数据:主机、端口、用户名和密码。
- 构建连接字符串:http://用户名:密码@主机:端口。例如:http://user:pass@proxy.proxeon.net:8000。
- 使用 curl 单次请求测试代理,将示例替换为您的数据。
测试请求示例:
curl -x http://user:pass@proxy.proxeon.net:8000 -H "Accept: application/json" https://stend.local/api/health⚠️ 注意:切勿将代理用户名和密码硬编码在可能提交到代码仓库的代码中。请使用环境变量。我们将在后续步骤中演示。
预期结果:curl 通过代理成功返回目标响应,无连接错误。
可能的问题:如果 curl 卡住,请检查端口和防火墙。如果出现 407 认证错误,请重新核对用户名和密码。
✅ 验证:您看到来自目标的响应体,并且是通过代理获得的,说明链路正常。
第 2 步:理解正确的负载测试方法
本阶段目标:掌握为什么一次性突袭无效,以及如何构建正确的负载曲线。
为什么一次性突袭会产生误导
如果您同时发起一千个请求,您会得到一个漂亮但无意义的数据。这种突发无法反映系统稳定行为。系统可能通过缓冲区吸收峰值,然后开始降级。或者,在启动时由于连接尚未建立而直接崩溃。
正确测试的三个阶段
正确的负载测试包含三个阶段:
- 预热,前 30-60 秒内缓慢提升负载。连接池、DNS 缓存和内部结构得到预热。预热数据不包含在最终统计中。
- 阶梯式增长。逐步增加并发:例如 10、25、50、100、200 VU,每级保持 1-2 分钟。这样我们可以看到降级从哪一级开始。
- 稳定平台。将负载固定在略低于上限的水平,并保持 5-10 分钟。平台阶段显示系统在持续负载下是否稳定,是否存在泄漏和队列积压。
提示:切勿根据测试前 30 秒的数据下结论。让系统进入稳定状态后,再查看指标。
在每个阶梯记录什么
- 达到的 RPS。
- p50、p95、p99 延迟。
- 错误率。
- 活跃 VU 或线程数。
性能拐点的判断方法:找到 RPS 停止增长、p95 和错误率开始急剧上升的那个阶梯。前一级就是您的安全工作点。
✅ 验证:您能够用自己的话解释预热和平台阶段的区别,以及阶梯式增长的作用。
第 3 步:k6 — 带代理和阶梯负载的现成脚本
本阶段目标:创建并运行一个完整的 k6 脚本,通过代理执行预热、阶梯和平台阶段。
k6 如何使用代理
k6 从环境变量 HTTP_PROXY 和 HTTPS_PROXY 读取代理地址。这样很方便:无需将凭据硬编码到脚本中,只需在运行时传入即可。
现成脚本 load-test.js
创建 load-test.js 文件并粘贴以下代码。将目标地址替换为您自己的。
import http from 'k6/http'; import { check, sleep } from 'k6'; import { Trend, Rate, Counter } from 'k6/metrics'; const latency = new Trend('req_latency', true); const errors = new Rate('req_errors'); const ok = new Counter('req_ok'); export const options = { discardResponseBodies: true, thresholds: { req_errors: ['rate<0.02'], http_req_duration: ['p(95)<1500', 'p(99)<3000'], }, scenarios: { ramp: { executor: 'ramping-vus', startVUs: 0, stages: [ { duration: '1m', target: 10 }, { duration: '2m', target: 25 }, { duration: '2m', target: 50 }, { duration: '2m', target: 100 }, { duration: '2m', target: 200 }, { duration: '5m', target: 200 }, { duration: '1m', target: 0 }, ], gracefulRampDown: '30s', }, }, }; const TARGET = __ENV.TARGET_URL || 'https://stend.local/api/health'; export default function () { const res = http.get(TARGET, { timeout: '10s', tags: { name: 'target' } }); latency.add(res.timings.duration); const good = check(res, { 'status is 200': (r) => r.status === 200 }); errors.add(!good); if (good) ok.add(1); sleep(0.5); }如何通过代理运行脚本
- 在脚本所在目录打开终端。
- 设置包含代理地址和目标的环境变量。在 Linux 和 macOS 上使用 export。
- 运行 k6 命令。
Linux 和 macOS 运行示例:
export HTTP_PROXY=http://user:pass@proxy.proxeon.net:8000 export HTTPS_PROXY=http://user:pass@proxy.proxeon.net:8000 export TARGET_URL=https://stend.local/api/health k6 run load-test.jsWindows PowerShell 使用 $env 语法设置变量:
$env:HTTP_PROXY="http://user:pass@proxy.proxeon.net:8000"; $env:HTTPS_PROXY="http://user:pass@proxy.proxeon.net:8000"; $env:TARGET_URL="https://stend.local/api/health"; k6 run load-test.js提示:参数 discardResponseBodies 会禁用响应体在内存中的存储。这降低了对生成器自身的负载,避免将其过载与代理上限混淆。
阅读 k6 报告
测试结束后,k6 会输出摘要。关注关键行:
- http_reqs — 总请求数和括号中的平均 RPS。这是您的吞吐量。
- http_req_duration — 延迟,其中包含 avg、min、med、max 以及 p(90)、p(95) 百分位。
- req_errors 和 http_req_failed — 错误占比。
- vus — 当前虚拟用户数。
thresholds 行将显示是否满足您的阈值。如果旁边出现叉号,表示阈值被突破——说明当前配置已接近或超过上限。
⚠️ 注意:stages 中的 target 值应针对您的系统调整。200 VU 仅为示例。如果您的目标或代理套餐上限较低,请从更适中的阶梯开始,例如最高 50。
预期结果:测试完成,您看到包含 RPS、百分位和错误率的摘要。
可能的问题:如果所有请求失败,请检查代理变量。如果生成器 CPU 占用 100%,请减少 VU 或使用更强性能的机器。
✅ 验证:k6 摘要中 http_reqs 非零,且至少在前几个阶梯中错误率低于阈值。
第 4 步:JMeter — 在测试计划中配置代理的相同场景
本阶段目标:构建一个包含代理、阶梯和指标收集的等效 JMeter 测试计划。
创建线程组
- 在打开的 JMeter 中,右键点击 Test Plan。
- 选择 Add → Threads (Users) → Thread Group。
- 在 Number of Threads 字段中设置最大线程数,例如 200。
- 在 Ramp-up period 字段中设置达到全负载的时间(秒),例如 540,以复现 k6 中的阶梯式增长。
- 在 Loop Count 中勾选 Infinite,并通过定时器和调度器设置持续时间。
在 HTTP Request Defaults 中配置代理
为避免在每个请求中单独配置代理,我们统一设置一次。
- 右键点击 Thread Group,选择 Add → Config Element → HTTP Request Defaults。
- 在打开的元件中找到代理服务器设置。
- 在代理服务器的 Server Name or IP 字段中输入代理主机,例如 proxy.proxeon.net。
- 在 Port Number(代理)字段中输入端口,例如 8000。
- 在代理的 Username 和 Password 字段中输入您的用户名和密码。
添加 HTTP 请求
- 右键点击 Thread Group,选择 Add → Sampler → HTTP Request。
- 在 Protocol 字段输入 https。
- 在 Server Name or IP 字段输入目标域名,例如 stend.local。
- 在 Path 字段输入路径,例如 /api/health。
- 在 Method 中保持 GET。
添加请求间延迟
- 右键点击 HTTP Request,选择 Add → Timer → Constant Timer。
- 在 Thread Delay 字段输入 500 毫秒,以复现 k6 中的 sleep。
配置指标收集
- 右键点击 Thread Group,添加 Add → Listener → Summary Report。
- 再添加 Add → Listener → Aggregate Report — 它显示百分位。
- 对于长时间测试,不要使用图形监听器,它们会消耗内存。改为将结果保存到文件。
提示:对于重负载测试,使用命令行以非图形化模式运行 JMeter。这样生成器将资源用于负载而非渲染图表。
非图形化运行示例:
jmeter -n -t plan.jmx -l results.jtl -e -o report这里 -n 表示无图形界面,-t 指定测试计划文件,-l 指定原始结果文件,-e -o 在 report 目录中生成 HTML 报告。
阅读 JMeter 报告
打开 report 文件夹中的 index.html。关键指标:
- Throughput — 每秒请求数,即吞吐量。
- Response Times Percentiles — 延迟百分位图。
- Error percentage — 错误率。
- Active Threads Over Time — 并发增长曲线。
⚠️ 注意:JMeter 有时需要对代理认证进行额外配置。如果看到大量 407 响应,请添加一个包含代理凭据的 HTTP Authorization Manager 元件。
预期结果:JMeter HTML 报告打开,显示吞吐量、百分位和错误率。
✅ 验证:在相同阶梯下,JMeter 的吞吐量和百分位与 k6 结果相当。轻微差异是正常的。
第 5 步:区分代理上限、客户端上限和服务器上限 — 三项检查
本阶段目标:精确定位哪个节点是瓶颈。
当您看到性能降级时,确定其来源非常重要。请进行三项控制检查。
检查 1:客户端是否成为瓶颈?
- 测试期间,打开生成器的系统监视器或任务管理器。
- 监控 k6 或 JMeter 进程的 CPU 和内存占用。
- 在 Linux 上使用 ulimit -n 检查文件描述符限制。
如果生成器 CPU 接近 100%,或触及文件描述符限制——问题出在客户端,而非代理。请提高文件描述符限制、减少 VU 数或使用更强性能的机器。
提示:客户端过载的标志是:延迟和生成器 CPU 同时上升,而 RPS 不变。代理不是原因。
检查 2:目标服务器是否成为瓶颈?
- 移除 HTTP_PROXY 和 HTTPS_PROXY 环境变量,直接运行一个短测试,不使用代理。
- 比较直接连接和通过代理两种方式的 RPS 和百分位。
如果直接连接和通过代理的 RPS 上限基本相同——瓶颈在目标服务器。代理只是增加了一个较小的固定延迟。
⚠️ 注意:直接测试仅针对您自己的环境。未经授权,即使是为了诊断,也不要对其他资源进行负载测试。
检查 3:隔离代理本身
- 在自己的环境上部署一个轻量响应桩,对任何请求立即返回 200 OK,内容为空。
- 通过代理针对该响应桩运行测试。
响应桩几乎即时响应,因此延迟和 RPS 上限主要由代理和网络决定。如果 RPS 在响应桩上达到天花板,即说明您找到了代理上限。
用简单的决策表来总结逻辑:
- 生成器 CPU 高、RPS 不增长 — 客户端上限。
- 直接和通过代理表现相同 — 目标服务器上限。
- 通过代理在快速响应桩上 RPS 触顶 — 代理上限。
预期结果:您能明确指出限制链路的节点。
✅ 验证:您已完成全部三项检查,并能证明瓶颈位置。
第 6 步:如何利用结果 — 线程数、代理池和采集时间
本阶段目标:将测试数据转化为实际工作中可用的配置。
选择安全线程数
取性能拐点前一级的数值。假设 100 VU 时获得稳定的 180 RPS、p95 约 900ms、错误率低于 1%;200 VU 时 RPS 未增长,但 p95 飙升至 4 秒。那么工作点约为 100 VU,为留出余量,取 80-90。
计算代理池大小
如果一个代理节点稳定支持 N 个并发连接,而您需要 M 个并发,那么最小池大小为 M 除以 N,向上取整,再加上余量。20-30% 的余量可以应对部分连接被慢响应占用的时段。
提示:始终为代理池预留余量。真实响应比响应桩慢,因此连接保持时间更长,实际并发高于理论值。
估算采集时间
公式很简单:时间(秒)= 总请求数 / 稳定 RPS。如果您需要采集 1,000,000 个请求,稳定 RPS 为 180,那么约为 5,556 秒,即约 1 小时 33 分钟,不考虑暂停和重试。
- 取稳定工作阶梯的 RPS。
- 总任务量除以 RPS。
- 额外增加 15-20% 以覆盖失败重试和暂停。
用伪代码演示计算:
total = 1000000; rps = 180; base_sec = total / rps; final_sec = base_sec * 1.2;