Pyroscope 负载测试实战:用 k6 压测连续性能分析平台的读 API

发布时间:2026/11/14 15:18:01

Pyroscope 负载测试实战:用 k6 压测连续性能分析平台的读 API Pyroscope 负载测试实战用 k6 压测连续性能分析平台的读 API【免费下载链接】pyroscopeContinuous Profiling Platform. Debug performance issues down to a single line of code项目地址: https://gitcode.com/GitHub_Trending/py/pyroscopePyroscope 是一个开源的连续性能分析Continuous Profiling平台能够将性能问题定位到单行代码。为了让平台在真实流量下保持稳定官方在仓库中内置了一套基于 k6 目录。本文将以该目录下的 README.md 为骨架结合run.sh、.env.template、lib/与tests/中的真实实现完整讲解如何对 Pyroscope 的读 APIread API发起压力测试、如何配置压测参数、以及每条压测请求背后对应的真实查询端点帮助你快速上手并理解这套压测工具的设计思路。目录结构与整体设计k6 目录 的职责非常清晰README 将其划分为两部分压测脚本load test scripts与辅助函数helper functions。tools/k6/ ├── .env.template # 环境变量模板说明每个压测配置项的含义与默认值 ├── README.md # 使用文档 ├── run.sh # 统一启动脚本支持本地运行与 k6 Cloud 运行 ├── lib/ │ ├── env.js # 读取并校验环境变量 │ ├── request.js # 封装 6 类读 API 请求gRPC 风格与 HTTP 风格 │ └── time.js # 相对时间范围last N 秒/分/时/天计算工具 └── tests/ └── reads.js # 读 API 压测主脚本场景编排、请求组装、查询分布注释从文件结构可以看出这套工具的定位是围绕 Pyroscope 读路径进行持续负载验证目前仓库中提供的压测用例是读路径场景reads.js。快速开始本地运行与云运行README 给出了两种运行方式核心命令只有一条# 1. 对本地 Pyroscope 实例运行压测 ./run.sh reads.js # 2. 从 k6 Cloud 运行压测 ./run.sh -c reads.js注意两点命令必须在仓库根目录下执行run.sh内部固定使用tools/k6/tests/${TEST}拼接测试文件路径因此传入的参数是tests目录下的文件名如reads.js。run.sh的完整逻辑如下源码见 tools/k6/run.shset -euo pipefail usage() { echo Usage: $0 [-c] file echo file File name of the test to run echo -c Run the test in the cloud echo -h Print help message and list all available tests } tests() { echo Available tests: for file in tools/k6/tests/*.js; do echo $(basename ${file}) done } IS_CLOUD while getopts hc opt; do case ${opt} in c) IS_CLOUD1 ;; h) usage; echo; tests; exit 0 ;; *) usage; exit 1 ;; esac done shift $((OPTIND-1)) TEST$1 if [ -z ${TEST} ]; then usage exit 1 fi DIR$(cd $(dirname ${BASH_SOURCE[0]}) ; pwd -P) source ${DIR}/.env if [ -n ${IS_CLOUD} ]; then k6 cloud run tools/k6/tests/${TEST} \ -e K6_BASE_URL${K6_BASE_URL} \ -e K6_READ_TOKEN${K6_READ_TOKEN} \ -e K6_TENANT_ID${K6_TENANT_ID} \ -e K6_QUERY_SERVICE_NAME${K6_QUERY_SERVICE_NAME} \ -e K6_QUERY_DURATIONS${K6_QUERY_DURATIONS} else K6_BASE_URL${K6_BASE_URL} \ K6_READ_TOKEN${K6_READ_TOKEN} \ K6_TENANT_ID${K6_TENANT_ID} \ K6_QUERY_SERVICE_NAME${K6_QUERY_SERVICE_NAME} \ K6_QUERY_DURATIONS${K6_QUERY_DURATIONS} \ k6 run tools/k6/tests/${TEST} fi脚本的关键行为-c切换到 k6 Cloud 运行模式对应k6 cloud run。-h打印用法并列出tools/k6/tests/*.js下所有可用测试方便快速查看当前有哪些用例。执行前会source同目录下的.env文件把其中的变量加载进环境本地运行模式通过命令前置的环境变量赋值把 5 个K6_*变量注入 k6 进程云运行模式则通过-e参数逐个传入。脚本使用set -euo pipefail任一环节失败都会立即终止便于在 CI 中直接使用。云运行的前提README 特别提醒从 k6 Cloud 运行时必须事先配置好.env文件。因为云环境下 k6 的执行节点不在你的机器上它只能通过run.sh传入的-e参数拿到目标地址与凭据而这些值全部来自.env。环境变量配置.env.template 逐项说明README 指出每个压测用例都可以通过环境变量调整行为各变量的含义与默认值记录在.env.template中。使用方式把.env.template复制为.env按需修改后run.sh会自动读取因为脚本会source .env。完整模板内容见 tools/k6/.env.template## Copy this template to .env and replace the values with your own. ## reads.js ## (REQUIRED) The base URL of the application under test. K6_BASE_URLhttp://localhost:4040 ## The read token to authenticate with the server. Only used if the server ## requires authentication. K6_READ_TOKEN ## The tenant ID to use for the test, if in running against a multi-tenant ## environment. K6_TENANT_ID ## (DEFAULT fire-dev-001/ingester) The service name to use in the read ## queries. K6_QUERY_SERVICE_NAME ## (DEFAULT 1h) Comma-separated list of durations to use in the read queries. ## These durations will be used in last X time ranges. K6_QUERY_DURATIONS各配置项的作用如下变量是否必填默认值说明K6_BASE_URL必填http://localhost:4040被测 Pyroscope 服务的 Base URL。本地默认端口 4040K6_READ_TOKEN视情况空读请求的认证 token仅当服务端开启认证时才需要K6_TENANT_ID视情况空多租户环境下的租户 IDK6_QUERY_SERVICE_NAME否fire-dev-001/ingester读查询中使用的 service_name 标签值K6_QUERY_DURATIONS否1h逗号分隔的时间跨度列表用于生成 “last X” 相对时间范围如1h,24h从源码可以印证这些变量的实际用途见 tools/k6/lib/env.js 与 tools/k6/tests/reads.js// lib/env.js缺失 K6_BASE_URL 时直接让 k6 报错失败 export const READ_TOKEN __ENV.K6_READ_TOKEN; export const TENANT_ID __ENV.K6_TENANT_ID; export const BASE_URL __ENV.K6_BASE_URL || fail(K6_BASE_URL environment variable missing);// reads.js时间跨度与 service_name 均可在运行时覆盖 const timeRanges (__ENV.K6_QUERY_DURATIONS || 1h).split(,).map((s) { return [parseInt(s.slice(0, -1)), s.slice(-1)]; }); const serviceName __ENV.K6_QUERY_SERVICE_NAME || fire-dev-001/ingester;例如设置K6_QUERY_DURATIONS1h,24h脚本会依次以“最近 1 小时”和“最近 24 小时”两个时间窗口各执行一轮全部查询设置K6_QUERY_SERVICE_NAMEpayment-service则会把所有查询的service_name标签替换为payment-service。若本地 Pyroscope 未开启认证默认单机部署通常如此K6_READ_TOKEN与K6_TENANT_ID可以留空压测多租户部署时则需要填充这两个值。压测用例reads.js 详解查询分布来自真实生产 7 天流量的统计reads.js 中保留了一段非常有价值的注释——它记录了从 Pyroscope 运营环境中抓取的7 天真实查询分布并标明哪些端点已在压测中实现✅哪些尚未覆盖❌count % endpoint implemented ------ ------ ----------------------------------- ----------- 11997 78.03 /querier.v1.QuerierService/SelectMergeProfile ✅ 2298 14.95 /pyroscope/render ✅ 461 3.00 /querier.v1.QuerierService/SelectMergeStacktraces ✅ 221 1.44 /querier.v1.QuerierService/LabelNames ✅ 130 0.85 /querier.v1.QuerierService/Series ✅ 100 0.65 /pyroscope/render-diff ✅ 59 0.38 /querier.v1.QuerierService/ProfileTypes ❌ 54 0.35 /querier.v1.QuerierService/SelectSeries ❌ 28 0.18 /querier.v1.QuerierService/LabelValues ❌ 26 0.17 /querier.v1.QuerierService/SelectMergeSpanProfile ❌ 1 0.01 /querier.v1.QuerierService/GetProfileStats ❌这份分布表揭示了两个重要信息SelectMergeProfile是绝对主力占比高达 78%它是把匹配的 profile 聚合为 pprof 格式的核心查询/pyroscope/render次之占 15%。当前压测脚本平均分配请求到已实现的 6 个端点作者在注释中明确写到最终目标应是让压测流量贴合上述真实分布Ultimately we should try tune our load tests to match this distribution并进一步识别真实查询参数分布。这说明这套工具仍处于演进状态你可以在自己的环境中按需调整请求比例。场景与阈值配置export const options { ext: { loadimpact: { projectID: 16425, name: reads, }, }, scenarios: { even_reads: { executor: constant-arrival-rate, duration: 5m, rate: 10, timeUnit: 1m, preAllocatedVUs: 3, maxVUs: 10, }, }, thresholds: { checks: [rate0.9], }, };场景名even_reads采用constant-arrival-rate执行器即恒定到达率——在 5 分钟内稳定地以每分钟 10 个迭代的速率发起请求这与读 API 的真实访问模式持续、平稳更为接近而非瞬间爆发。preAllocatedVUs: 3、maxVUs: 10预先分配 3 个虚拟用户峰值最多可扩展到 10 个k6 会在需要时自动扩容以满足恒定到达率。阈值checks: [rate0.9]所有check的通过率必须大于 90%否则压测结束时 k6 会以非零退出码结束——这个阈值可以直接作为 CI 中的门禁条件。压测主体default 函数与请求组装// 启用 Pyroscope 自动埋点auto-labeling pyroscope.instrumentHTTP(); export default function() { const timeRanges (__ENV.K6_QUERY_DURATIONS || 1h).split(,).map((s) { return [parseInt(s.slice(0, -1)), s.slice(-1)]; }); const serviceName __ENV.K6_QUERY_SERVICE_NAME || fire-dev-001/ingester; for (const [scalar, unit] of timeRanges) { group(reads last ${scalar}${unit}, () { const { start, end } newRelativeTimeRange(scalar, unit); doAllQueryRequests(serviceName, start, end); }); } }流程为解析K6_QUERY_DURATIONS得到若干个 (数值, 单位) 组合 → 对每个时间窗口建立一个 k6group→ 通过newRelativeTimeRange计算相对时间范围 → 一次性发出全部 6 类查询。time.js中的newRelativeTimeRangetools/k6/lib/time.js支持s/m/h/d四种单位非法单位会直接抛错export function newRelativeTimeRange(scalar, unit) { const end Date.now(); switch (unit) { case s: return { start: end - scalar * 1000, end }; case m: return { start: end - scalar * 60 * 1000, end }; case h: return { start: end - scalar * 60 * 60 * 1000, end }; case d: return { start: end - scalar * 24 * 60 * 60 * 1000, end }; default: throw new Error(Invalid unit: ${unit}); } }值得注意reads.js从 tools/k6/lib/time.js 导入newRelativeTimeRange但lib/time.js中同时存在同名的函数实现reads.js末尾也内联了一份相同实现这是工具演进过程中留下的冗余理解时以lib/time.js为准即可。6 类读请求的端点与底层实现lib/request.jstools/k6/lib/request.js封装了 6 个请求函数分为两类gRPC 风格通过 Connect 协议走 HTTP POST路径形如/querier.v1.QuerierService/XXXSelectMergeProfile、SelectMergeStacktraces、LabelNames、SeriesHTTP 风格直接 GET Pyroscope 传统查询端点/pyroscope/render、/pyroscope/render-diff。所有请求都会经过withHeaders注入统一头部tools/k6/lib/request.jsfunction withHeaders(headers) { const baseHeaders { User-Agent: k6-load-test, Content-Type: application/json, Authorization: Basic ${encoding.b64encode(${TENANT_ID}:${READ_TOKEN})} }; for (const [k, v] of Object.entries(headers || {})) { baseHeaders[k] v; } return baseHeaders; }可以看到认证方式为Basic Auth用户名为TENANT_ID、密码为READ_TOKEN对应多租户下的读写权限隔离。同时每个请求都设置了tags: { name: ... }便于在 k6 结果报表中按端点维度聚合指标。下面是doAllQueryRequeststools/k6/tests/reads.js中 6 类请求的完整请求体与对应端点说明1. SelectMergeProfilegRPC/ConnectdoSelectMergeProfileRequest({ start, end, profile_typeID: process_cpu:cpu:nanoseconds:cpu:nanoseconds, label_selector: {service_name${serviceName}}, });请求路径POST {BASE_URL}/querier.v1.QuerierService/SelectMergeProfile。该方法在 api/querier/v1/querier.proto 中定义为“返回匹配的 profile并以 pprof 格式聚合”SelectMergeProfile returns matching profiles aggregated in pprof format。它返回的是 google.v1.Profile 类型的 pprof 数据是占比最高的查询也是火焰图等可视化能力的核心数据源。2. /pyroscope/renderHTTPdoRenderRequest({ from: start, until: end, query: process_cpu:cpu:nanoseconds:cpu:nanoseconds{service_name${serviceName}}, aggregation: sum, format: json, max-nodes: 16384, });这是 Pyroscope 的传统渲染端点from/until为毫秒时间戳query使用 Pyroscope Query Languageprofile_type{labelvalue}aggregation: sum指定聚合方式format: json控制返回格式max-nodes限制火焰图最大节点数。从 pkg/querier/http.go 的实现看该端点最终会经由SelectMergeProfile/SelectMergeStacktraces的 gRPC 客户端调用完成数据查询与渲染。3. SelectMergeStacktracesgRPC/ConnectdoSelectMergeStacktracesRequest({ start, end, profile_typeID: process_cpu:cpu:nanoseconds:cpu:nanoseconds, label_selector: {service_name${serviceName}}, max-nodes: 16384, });路径POST {BASE_URL}/querier.v1.QuerierService/SelectMergeStacktracesproto 中描述为“返回匹配的 profile并聚合成 flamegraph 形式”SelectMergeStacktraces returns matching profiles aggregated in a flamegraph。与SelectMergeProfile的区别在于返回格式前者返回 pprof后者返回聚合后的调用栈树flamegraph。4. LabelNamesgRPC/ConnectdoLabelNamesRequest({ start, end, matchers: [ {__profile_type__process_cpu:cpu:nanoseconds:cpu:nanoseconds, service_name${serviceName}}, ], });路径POST {BASE_URL}/querier.v1.QuerierService/LabelNamesproto 定义见 api/querier/v1/querier.protoLabelNames returns a list of the existing label names。请求通过matchers限定 profile 类型与 service 范围用于返回该范围内存在的标签名列表。5. SeriesgRPC/ConnectdoSeriesRequest({ start, end, labelNames: [service_name, __profile_type__], matchers: [], });路径POST {BASE_URL}/querier.v1.QuerierService/Series用于查询指定时间范围内、按labelNames维度展开的序列集合series set常用于图表的时间序列选择与浏览。6. /pyroscope/render-diffHTTPdoRenderDiffRequest({ rightQuery: process_cpu:cpu:nanoseconds:cpu:nanoseconds{service_name${serviceName}}, rightFrom: start, rightUntil: end, leftQuery: process_cpu:cpu:nanoseconds:cpu:nanoseconds{service_name${serviceName}}, leftFrom: start - (end - start), // 左时间窗与右时间窗等长向前平移 leftUntil: start, format: json, max-nodes: 16384, });渲染差分火焰图diff flamegraph右侧right*为最近一个时间窗左侧left*为往前平移等长距离的对照时间窗leftFrom start - (end - start)从而对比“现在 vs 之前”的性能变化这也是 Pyroscope 差分对比功能的负载来源。每个请求函数内部都通过 k6 的check断言status is 200一旦任一请求非 200就会拉低整体通过率并最终触发rate0.9的阈值告警。压测报告的解读与工程化建议运行./run.sh reads.js后k6 会在终端输出汇总报告包括每秒请求数RPS、请求延迟分布P90/P95 等、每个group和tag name维度的耗时明细以及checks通过率。结合tags: { name: ... }你可以直接从报告中对比 6 类读端点的延迟与错误率差异快速定位读路径上的性能瓶颈例如SelectMergeProfile大范围聚合耗时、render-diff双时间窗查询开销等。几点工程化建议基于仓库现有实现推断接入 CIrun.sh在本地运行模式下退出码即为 k6 退出码而 k6 在checks通过率跌破阈值rate0.9时会以非零码退出因此可直接作为 CI 门禁。贴近真实流量当前脚本对 6 个端点平均施压与 7 天运营数据SelectMergeProfile占 78%存在差距如需更真实的压测可在doAllQueryRequests内按上述比例调整各请求的调用权重。多时间窗回归利用K6_QUERY_DURATIONS支持逗号分隔的特性如1h,24h一次运行即可同时覆盖“热数据”与“长跨度聚合”两类典型读路径压力。多租户验证配置K6_TENANT_ID与K6_READ_TOKEN后即可针对启用认证的多租户部署验证读路径的鉴权与租户隔离逻辑。总结这套位于 tools/k6 的负载测试工具是 Pyroscope 官方为验证读路径稳定性而内置的“活文档”run.sh提供了本地/云两种运行方式.env.template完整声明了 5 个可调参数reads.js以恒定到达率场景对 6 类核心读端点SelectMergeProfile、/render、SelectMergeStacktraces、LabelNames、Series、/render-diff持续施压并保留了来自生产环境 7 天真实查询分布的演进路线图。无论你是要为自己的 Pyroscope 部署做容量评估、在发布前验证读路径回归还是想深入了解 Pyroscope 读 API 的查询形态都可以直接基于本目录的脚本二次开发。【免费下载链接】pyroscopeContinuous Profiling Platform. Debug performance issues down to a single line of code项目地址: https://gitcode.com/GitHub_Trending/py/pyroscope创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
延伸阅读

更多相关文章

2026/11/13 10:51:44

对话状态跟踪(DST)在AI应用中的场景自适应优化实践

1. 对话状态跟踪在AI原生应用中的核心价值在智能对话系统的实际开发中,对话状态跟踪(DST)就像交通管制中心的雷达系统,需要实时捕捉和解析用户意图的"飞行轨迹"。去年我们团队为某金融客服系统升级对话模块时,发现传统固定规则的DS…

2026/11/13 3:49:22

Qt散点图实现全解析:从QPainter到QCustomPlot的选型与实践

简介:面向Qt数据可视化学习者的轻量级C源码包,提供散点图完整实现,解决在Qt图形视图框架中自定义二维散点图的展示问题。示例围绕场景、视图与图形项三类核心组件展开,演示数据点如何映射到坐标、如何通过重写绘制方法定制点的颜色…

2026/11/14 15:16:10

济宁去哪里考取电工证别乱报班 考不过白交钱电子证书怎么领

济宁去哪里考取电工证别乱报班 考不过白交钱电子证书怎么领 怕考不过白交培训费,这事儿真不是危言耸听。去年济宁某培训机构就出了个大乌龙,几十个学员交了近两万块培训费,结果考试通过率不足30%。你要是以为报名了就稳了,那可就大错特错了。现在考电…

2026/11/14 15:16:10

就业考什么电工证全流程办理指南

电工证考什么就业有保障?真实经历告诉你怎么避坑 你是不是也遇到过这样的情况?拿着一堆招聘信息,看到“持证上岗”几个字,心里一紧,不知道去哪报名,怕被中介骗钱,更怕花冤枉钱考个没用的证。别急,今天我以信阳浉河区一位培训机构学员的真实经历为例,…

2026/11/14 15:16:10

福建省电工证复审平台在哪考,怕被中介坑?老手教你避坑

福建省电工证复审平台在哪考,怕被中介坑?老手教你避坑 不知道去哪报名怕被中介坑,福建省电工证复审平台在哪考?其实很多人在办理电工证复审时,最大的困扰就是不知道该去哪考,怕被一些不正规的中介坑了钱还办不了证。今天我就从一个在应急管理行业摸爬滚…

2026/11/14 15:16:10

普宁里湖考电工证内部消息:电工证不考就失业?真相来了!

普宁里湖考电工证内部消息:电工证不考就失业?真相来了! 你是不是正在普宁里湖找工作,却因为没有电工证被拒之门外?别急,今天就把【普宁里湖考电工证】的 内部消息 和 真实报考流程 全告诉你,别再走弯路了。 电工证不考就失业?是真的!…

2026/11/14 15:11:09

安徽省电工证在线查询实操考试考什么多少钱

安徽省电工证在线查询实操考试考什么多少钱 实操考试心里没底怕挂科,特别是像低压电工证、高压电工证这类高门槛证书,不少考生最担心的就是考试流程、内容和费用。今天咱们就来聊一聊, 安徽省电工证在线查询 的具体实操内容、 多少钱…

2026/11/13 22:33:32

电工证丢失还能补吗?千万别踩坑,丢了证等于失业

电工证丢失还能补吗?千万别踩坑,丢了证等于失业 你正在找工作,但手里的电工证突然丢了,这下怎么办?别急,先别慌, 电工证丢失还能补吗 ?答案是肯定的,但 千万别踩坑 ,补办流程如果不了解,可能会耽误你上岗、甚至影响到你的收入。…

2026/11/14 0:06:39

土木特种工考证官方报名入口怎么找 之前考的证在外省能不能转过来

土木特种工考证官方报名入口怎么找 之前考的证在外省能不能转过来 之前考的证在外省能不能转过来?这个问题我太熟悉了,作为一名干了十几年的特种作业证书办理从业者,每年都会遇到不少像你一样在外地考了证,回来却发现无法直接使用的学员。别急,今天我就…

2026/11/14 0:06:39

电工证要多久多钱保姆级教程:工地太忙根本没时间复习考试

电工证要多久多钱保姆级教程:工地太忙根本没时间复习考试 工地太忙根本没时间复习考试,你不是一个人。在信阳浉河区,很多刚进入工地的电工都面临这样的困境:工作强度大,时间碎片化,根本没有系统复习考试的机会。但电工证是上岗的硬门槛,…

2026/11/13 22:32:11

面向复杂光照与角度的宠物面部识别鲁棒性增强策略

在智慧养宠时代,基于视觉的宠物身份识别技术正逐步从实验室走向家庭应用。然而,与配合度高的人类不同,宠物是典型的“非合作生物识别对象”——它们不会主动配合摄像头调整姿态,更无法控制光照环境。实际应用中,我们常…

2026/11/13 12:14:39

SSM电子书店源码全解析:结构、原理、部署与排错实战

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

还想了解更多?直接咨询顾问

免费诊断 + 免费方案 + 透明报价。

全国咨询热线400-8866-253
免费获取方案
☎咨询二维码 ☎ ↑