第一章:Go协程监控体系的生产级意义与挑战
在高并发微服务架构中,Go 协程(goroutine)是实现轻量级并发的核心抽象,但其“无限创建”的表象极易掩盖资源失控风险。单个进程内数万甚至百万级 goroutine 并非罕见,而默认无栈大小限制、无生命周期追踪、无统一调度视图,使得内存泄漏、阻塞型协程堆积、死锁隐式传播等问题在生产环境中往往滞后暴露,造成服务响应陡增、OOM Killer 触发或节点雪崩。
协程失控的典型生产征兆
- HTTP 服务 P99 延迟持续升高,
runtime.NumGoroutine()指标呈单调上升趋势; pprof/goroutine?debug=2输出中大量syscall,semacquire,selectgo状态协程长期驻留;- Prometheus 中
go_goroutines指标与process_resident_memory_bytes强正相关,暗示协程未释放导致内存滞留。
监控体系必须覆盖的三大维度
| 维度 | 关键指标示例 | 采集方式 |
|---|---|---|
| 数量与增长 | go_goroutines, goroutines_created_total |
Prometheus 官方 Go client + 自定义计数器 |
| 状态分布 | 阻塞态/运行态/等待态协程占比 | runtime.ReadMemStats() + debug.ReadGCStats() 辅助推断 |
| 栈帧溯源 | 协程创建位置(文件+行号)、阻塞调用链 | 启用 GODEBUG=gctrace=1 + 自定义 runtime.Stack() 采样 |
快速启用基础协程健康检查
# 1. 启动时注入调试端点(需在 main 包中注册)
import _ "net/http/pprof" // 自动注册 /debug/pprof/
# 2. 实时查看活跃协程堆栈(按状态分组统计)
curl -s http://localhost:6060/debug/pprof/goroutine?debug=2 | \
awk '/goroutine [0-9]+.*:/ { state = $3 } !/^$/ && !/^goroutine/ { if (state) print state }' | \
sort | uniq -c | sort -nr
该命令解析 pprof 输出,提取每条 goroutine 的当前状态(如 running, chan receive, select),并统计频次——若 semacquire 占比超 30%,极可能遭遇锁竞争或 channel 缓冲区耗尽。
缺乏细粒度协程生命周期追踪机制,将导致故障定位从“分钟级”退化为“小时级”。真正的生产级监控,不是记录数字,而是让每个 goroutine 可追溯、可归因、可干预。
第二章:Go运行时核心指标原理与采集实践
2.1 goroutines_total指标的生命周期建模与突增告警策略
goroutines_total 是 Prometheus 客户端库自动暴露的计数器,记录 Go 程序自启动以来创建的 goroutine 总数(含已退出者),非瞬时活跃数。
数据同步机制
该指标由 runtime.NumGoroutine() 采样,但 goroutines_total 实际是 promhttp 内置的 go_goroutines 指标(类型为 Gauge),值为当前活跃 goroutine 数量——需特别注意命名歧义。
// prometheus/client_golang/prometheus/go_collector.go
func (c *goCollector) updateGoMetrics() {
c.goroutines.Set(float64(runtime.NumGoroutine())) // ← 实时活跃数,非累计
}
goroutines是 Gauge 类型,每秒采集runtime.NumGoroutine()返回值;无重置逻辑,不满足“total”语义,属历史命名遗留。
突增检测关键阈值
| 场景 | 建议阈值 | 依据 |
|---|---|---|
| Web 服务常规负载 | > 500 持续30s | 避免协程泄漏误报 |
| 批处理任务峰值 | > 5000 突增200% | 结合 rate(goroutines[5m]) 动态基线 |
告警决策流
graph TD
A[每15s采集 goroutines] --> B{delta > 300 in 60s?}
B -->|Yes| C[触发二级检查:rate[2m] > 50/s]
C -->|Confirmed| D[推送告警 + dump goroutines]
B -->|No| E[静默]
2.2 go_gc_duration_seconds直方图解析与GC停顿根因定位实战
go_gc_duration_seconds 是 Go 运行时暴露的关键 Prometheus 直方图指标,记录每次 GC STW(Stop-The-World)阶段的持续时间分布。
直方图结构语义
该指标包含 le(less than or equal)标签,如:
go_gc_duration_seconds_bucket{le="0.001"} # ≤1ms 的GC次数
go_gc_duration_seconds_sum # 所有GC耗时总和(秒)
go_gc_duration_seconds_count # GC 总触发次数
根因定位三步法
- ✅ 识别异常尖峰:查询
rate(go_gc_duration_seconds_sum[5m]) / rate(go_gc_duration_seconds_count[5m])得平均停顿时长 - ✅ 下钻分布偏移:对比
histogram_quantile(0.99, rate(go_gc_duration_seconds_bucket[1h]))与基线 - ✅ 关联内存压力:叠加
go_memstats_heap_alloc_bytes和go_gc_cycles_automatic_total
典型高停顿场景对照表
| 场景 | le="0.01" 占比 |
heap_alloc 增速 | 可能根因 |
|---|---|---|---|
| 正常低负载 | >95% | 平缓 | GC 频率低、对象生命周期短 |
| 大量短生命周期对象 | 快速脉冲式增长 | 分配速率过高,触发高频 GC | |
| 内存泄漏 | 持续下降 | 单向陡峭上升 | 对象无法回收,触发强制 GC |
GC 停顿链路关键节点(mermaid)
graph TD
A[GC 触发] --> B[Mark Start STW]
B --> C[并发标记]
C --> D[Mark Termination STW]
D --> E[并发清扫]
E --> F[Sweep Done]
style B stroke:#ff6b6b,stroke-width:2px
style D stroke:#ff6b6b,stroke-width:2px
2.3 go_memstats_alloc_bytes与go_memstats_heap_inuse_bytes协同分析内存泄漏模式
核心指标语义辨析
go_memstats_alloc_bytes:当前已分配并仍在使用的对象字节数(含逃逸到堆的栈对象),实时反映活跃内存负载;go_memstats_heap_inuse_bytes:堆中已向操作系统申请、当前被 Go 运行时标记为“正在使用”的内存页总字节数(含未被 GC 回收的 span 开销)。
数据同步机制
二者均来自 runtime.MemStats,但更新时机不同:alloc_bytes 在每次 mallocgc 后原子更新;heap_inuse_bytes 在 GC 周期结束或 heap growth 时批量刷新。存在短暂非一致性窗口。
var m runtime.MemStats
runtime.ReadMemStats(&m)
fmt.Printf("Alloc: %v, HeapInuse: %v\n", m.Alloc, m.HeapInuse)
// Alloc ≈ live objects; HeapInuse ≥ Alloc(因包含元数据、span header、未释放页)
该读取捕获瞬时快照;若 HeapInuse - Alloc 持续增长且 Alloc 不回落,表明存在不可达但未被 GC 清理的对象(如循环引用、全局 map 泄漏)。
协同诊断模式
| 场景 | alloc_bytes 趋势 | heap_inuse_bytes 趋势 | 推断原因 |
|---|---|---|---|
| 健康应用 | 波动后回落 | 同步波动 | 正常 GC 回收 |
| 全局 map 未清理键值 | 持续上升 | 持续上升(差值稳定) | 活跃对象泄漏 |
| goroutine 持有大 slice | 缓慢上升 | 阶跃式上升(GC 后不降) | 堆页未归还 OS(inuse > alloc) |
graph TD
A[持续采集 MemStats] --> B{Alloc ↑ & Inuse ↑}
B --> C{Inuse - Alloc 是否扩大?}
C -->|是| D[存在未释放元数据/碎片化]
C -->|否| E[活跃对象真实增长]
D --> F[检查 runtime.MemStats.HeapIdle/HeapReleased]
2.4 go_threads与go_goroutines比值监控:识别线程膨胀与协程滥用风险
Go 运行时通过 M:N 调度模型将 goroutine 复用到 OS 线程(M)上,但异常比值暴露底层失衡风险。
为什么比值关键?
go_threads / go_goroutines ≈ 1:健康状态(多数 goroutine 在少量线程上高效复用)> 0.3:潜在线程泄漏(如 cgo 阻塞未释放 M)> 1.0:严重线程膨胀(每个 goroutine 独占线程,失去调度优势)
Prometheus 查询示例
rate(go_threads[5m]) / rate(go_goroutines[5m])
此比率需在 5 分钟滑动窗口内计算,避免瞬时抖动误报;分母为
go_goroutines(当前活跃 goroutine 数),分子为go_threads(OS 级线程数),二者均为 Gauge 类型。
典型风险场景对比
| 场景 | go_threads | go_goroutines | 比值 | 风险类型 |
|---|---|---|---|---|
| 健康 HTTP 服务 | 12 | 2400 | 0.005 | — |
| cgo 阻塞未超时 | 86 | 192 | 0.448 | 线程泄漏 |
runtime.LockOSThread() 滥用 |
157 | 150 | 1.05 | 协程滥用+资源耗尽 |
自动化检测逻辑(Golang)
func checkThreadGoroutineRatio(threads, gors int64) bool {
if gors == 0 {
return false // 防除零
}
ratio := float64(threads) / float64(gors)
return ratio > 0.3 // 阈值可配置
}
threads来自debug.ReadBuildInfo().Settings或/debug/pprof/接口;gors可通过runtime.NumGoroutine()获取;该函数用于告警钩子,响应延迟敏感型服务。
2.5 process_cpu_seconds_total与go_sched_goroutines_goroutines联合建模CPU协程负载效率
协程密度是评估Go服务资源效率的关键指标,需将CPU时间消耗与并发协程数进行归一化建模。
核心指标语义对齐
process_cpu_seconds_total:进程累计CPU秒(wall-clock无关,含用户态+内核态)go_sched_goroutines_goroutines:瞬时活跃goroutine数量(非历史峰值)
效率比值定义
# 每goroutine平均占用CPU秒/采样周期(推荐1m rate)
rate(process_cpu_seconds_total[1m]) / go_sched_goroutines_goroutines
逻辑分析:
rate()消除绝对累积量干扰,分母为瞬时快照值,确保分子分母时间维度对齐;结果单位为“秒/协程/分钟”,值越低说明调度器负载越轻、协程越“轻量”。
典型阈值参考
| 场景 | 健康区间(s/goroutine/min) | 风险信号 |
|---|---|---|
| I/O密集型微服务 | > 2.0 → 协程阻塞加剧 | |
| CPU密集计算任务 | 1.5–4.0 |
协程效率瓶颈定位流程
graph TD
A[高CPU_sec/ goroutine] --> B{goroutine数是否突增?}
B -->|是| C[检查channel阻塞/锁竞争]
B -->|否| D[分析GC停顿或系统调用阻塞]
第三章:Prometheus服务端深度配置与指标治理
3.1 自定义Job与Instance标签体系设计:支撑多集群协程指标血缘追踪
为实现跨集群协程级指标的可追溯性,需突破Prometheus原生job/instance静态标签的局限,构建动态、嵌套、语义化的标签体系。
标签分层结构
job: 绑定逻辑任务单元(如payment-orchestrator)instance: 关联协程唯一ID(如coro-7f3a9b@cluster-east-02)- 新增维度:
coroutine_id、trace_id、parent_coro_id
动态标签注入示例(OpenTelemetry SDK)
# 在协程启动时注入上下文标签
from opentelemetry import trace
tracer = trace.get_tracer(__name__)
with tracer.start_as_current_span("process_order") as span:
span.set_attribute("coroutine_id", "coro-7f3a9b")
span.set_attribute("trace_id", "0xabcdef1234567890")
span.set_attribute("parent_coro_id", "coro-2d1e8c") # 支持血缘回溯
该代码在Span生命周期内绑定协程元数据,使Exporter可将coroutine_id映射为Prometheus instance,trace_id作为高基数标签用于关联分析。
标签映射关系表
| Prometheus Label | 来源字段 | 说明 |
|---|---|---|
job |
服务逻辑名 | 静态,标识业务职责域 |
instance |
coroutine_id |
动态生成,全局唯一协程标识 |
trace_id |
OpenTelemetry Span | 支持跨集群调用链对齐 |
血缘追踪流程
graph TD
A[协程A启动] --> B[注入coroutine_id + trace_id]
B --> C[上报指标至本地Prometheus]
C --> D[Remote Write带trace_id标签]
D --> E[中心化TSDB按trace_id聚合]
E --> F[反向遍历parent_coro_id构建DAG]
3.2 指标采样精度调优与远程写入稳定性保障(remote_write+TSDB压缩策略)
数据同步机制
Prometheus 的 remote_write 配置需平衡吞吐与可靠性:
remote_write:
- url: "http://tsdb-gateway:9090/api/v1/write"
queue_config:
capacity: 5000 # 内存队列上限,防OOM
max_shards: 20 # 并发写入分片数,提升吞吐
min_shards: 1 # 低负载时最小分片,节省资源
max_samples_per_send: 1000 # 单次HTTP请求样本数,影响网络包大小与重试粒度
max_samples_per_send=1000在高基数场景下可降低单次请求失败影响面;过小则HTTP开销剧增,过大易触发网关超时(如TSDB侧read_timeout=30s)。
压缩协同策略
TSDB(如VictoriaMetrics、Mimir)需匹配Prometheus采样节奏:
| 参数 | 推荐值 | 作用 |
|---|---|---|
--retention.period |
30d | 与Prometheus --storage.tsdb.retention.time 对齐,避免远端残留冷数据 |
--storage.tsdb.min-block-duration |
2h | 块切分粒度,应 ≥ 最大scrape_interval×2,防止指标碎片化 |
稳定性保障流程
graph TD
A[Prometheus scrape] --> B{sample_rate > threshold?}
B -->|是| C[启用exemplar & native histogram]
B -->|否| D[常规float64序列]
C --> E[remote_write batch + compression]
D --> E
E --> F[TSDB LZ4 block encoding]
3.3 指标卡片化治理:基于__name__正则与metric_relabel_configs实现9项核心指标白名单管控
指标卡片化治理将监控指标抽象为可独立生命周期管理的“卡片”,每张卡片对应一个业务关键度明确、语义清晰的指标实体。
白名单匹配逻辑
通过 metric_relabel_configs 在采集端前置过滤,仅保留符合业务契约的指标:
- source_labels: [__name__]
regex: '^(http_request_total|process_cpu_seconds_total|go_goroutines|node_memory_MemAvailable_bytes|redis_connected_clients|kafka_topic_partition_count|jvm_memory_used_bytes|nginx_http_requests_total|etcd_disk_wal_fsync_duration_seconds)$'
action: keep
该规则利用
__name__原生标签进行精确正则匹配,action: keep表示仅保留匹配成功的指标;regex中显式列出9项核心指标(如http_request_total表征服务入口流量),避免隐式通配导致噪声注入。
卡片元数据映射表
| 卡片ID | 指标名 | 业务域 | SLA等级 |
|---|---|---|---|
| CARD-001 | http_request_total |
API网关 | P0 |
| CARD-002 | redis_connected_clients |
缓存层 | P1 |
治理流程示意
graph TD
A[Prometheus scrape] --> B{metric_relabel_configs}
B -->|匹配白名单| C[入库存储]
B -->|不匹配| D[丢弃]
第四章:Grafana高可用看板构建与SLO驱动告警闭环
4.1 协程健康度仪表盘:goroutines_growth_rate + gc_pause_p99 + heap_alloc_rate三维动态基线
协程健康度需从并发膨胀、内存压力与GC扰动三维度联合建模。传统静态阈值易误报,动态基线通过滑动窗口(如15m)实时拟合历史分布。
三维指标语义
goroutines_growth_rate:每分钟新增协程数斜率,突增预示泄漏或扇出失控gc_pause_p99:GC STW时长P99,>10ms触发预警heap_alloc_rate:单位时间堆分配字节数,持续>50MB/s暗示高频短生命周期对象
动态基线计算(Prometheus+Grafana)
# 三维联合异常评分(0~100)
(
(rate(goroutines{job="api"}[5m]) - avg_over_time(rate(goroutines[1h])[5m]))
/ stddev_over_time(rate(goroutines[1h])[5m])
+
(histogram_quantile(0.99, sum(rate(gc_heap_goal_bytes[5m]))) - avg_over_time(histogram_quantile(0.99, sum(rate(gc_heap_goal_bytes[1h])))))
/ stddev_over_time(histogram_quantile(0.99, sum(rate(gc_heap_goal_bytes[1h]))))
+
(rate(go_memstats_alloc_bytes_total[5m]) - avg_over_time(rate(go_memstats_alloc_bytes_total[1h])))
/ stddev_over_time(rate(go_memstats_alloc_bytes_total[1h]))
) / 3 * 100
逻辑分析:各指标标准化为Z-score后归一加权,消除量纲差异;分母使用1小时标准差增强对慢变趋势的鲁棒性;分子采用5分钟速率保障响应灵敏度。
| 指标 | 健康区间 | 危险信号 |
|---|---|---|
| goroutines_growth_rate | > 80/min(持续2min) | |
| gc_pause_p99 | > 15ms(P99) | |
| heap_alloc_rate | > 100MB/s(5min均值) |
关联诊断流程
graph TD
A[指标超阈值] --> B{是否三指标同步超标?}
B -->|是| C[定位协程泄漏:pprof/goroutine?debug=2]
B -->|否| D[单点分析:GC调优 or 内存池优化]
4.2 SLO达标率看板:基于go_gc_duration_seconds_quantile计算“GC合规性SLI”并映射至Error Budget
GC合规性SLI定义
SLI = rate(go_gc_duration_seconds_quantile{quantile="0.99"}[1h]) < 0.05s 的时间占比(窗口内满足阈值的秒数 / 总秒数)。
Prometheus 查询与告警逻辑
# 计算每小时SLI值(0~1区间)
1 - avg_over_time(
(go_gc_duration_seconds_quantile{quantile="0.99"} > 0.05)[1h:]
)
逻辑说明:
go_gc_duration_seconds_quantile{quantile="0.99"}表示P99 GC暂停时长(单位:秒);> 0.05标识违规样本;avg_over_time求该窗口内违规比例,1 -即为合规率。此即SLI原始值。
Error Budget 映射规则
| SLO目标 | 月度Error Budget | 对应SLI下限 |
|---|---|---|
| 99.9% | 43.2分钟 | ≥0.999 |
| 99.5% | 3.6小时 | ≥0.995 |
可视化看板关键指标
- ✅ 实时SLI值(滚动1h窗口)
- ⚠️ Error Budget消耗速率(/h)
- ❌ 连续3个周期SLI
graph TD
A[Prometheus] -->|go_gc_duration_seconds_quantile| B[Recording Rule: gc_sli_1h]
B --> C[Alertmanager: budget_burn_rate_high]
C --> D[Grafana SLO Dashboard]
4.3 协程逃逸诊断视图:结合pprof trace与go_goroutines瞬时快照实现goroutine leak热区定位
核心诊断组合策略
go tool pprof -http=:8080 http://localhost:6060/debug/pprof/trace?seconds=30获取执行轨迹curl "http://localhost:6060/debug/pprof/goroutine?debug=2"抓取带栈帧的 goroutine 快照
关键比对维度
| 维度 | trace 数据 | go_goroutines 快照 |
|---|---|---|
| 时间粒度 | 纳秒级调度事件 | 瞬时(毫秒级采样点) |
| 状态覆盖 | 包含阻塞、抢占、唤醒链 | 仅记录当前 goroutine 状态 |
| 泄漏识别信号 | 持续增长的 goroutine 创建 节点 | 大量 runtime.gopark 栈底 |
典型泄漏代码片段
func startLeakyWorker(ctx context.Context) {
for { // ❌ 无退出条件,且未监听 ctx.Done()
select {
case <-time.After(1 * time.Second):
http.Get("https://example.com") // 隐式启动新协程(如内部net/http超时处理)
}
}
}
该函数在未受控循环中持续创建隐式协程,trace 中可见高频 runtime.newproc1 调用,而 goroutine?debug=2 快照中大量出现 net/http.(*persistConn).readLoop 栈帧——即 leak 热区。
协程生命周期比对流程
graph TD
A[trace: goroutine create event] --> B{是否在 5s 内 park/exit?}
B -->|否| C[标记为潜在 leak]
B -->|是| D[关联 goroutine?debug=2 中对应 GID]
D --> E[提取栈顶函数+调用路径]
4.4 告警降噪引擎:利用Prometheus recording rules聚合9项指标生成go_runtime_health_score综合评分
核心设计思想
将 GC 频率、goroutine 泄漏、内存分配速率等 9 项运行时指标加权归一化,消除量纲差异,输出 [0, 100] 区间健康分,抑制低危波动告警。
录用规则示例
# recording_rules.yml
- record: go_runtime_health_score
expr: |
(100 - clamp_min(
(10 * (go_goroutines / 1000)) +
(15 * (rate(go_memstats_gc_cpu_fraction[1h]))) +
(20 * (1 - rate(go_memstats_alloc_bytes_total[1h]) /
rate(go_memstats_total_alloc_bytes_total[1h]))) +
(5 * (go_threads > 1000)) +
(10 * (rate(go_gc_duration_seconds_sum[1h]) /
rate(go_gc_duration_seconds_count[1h]) > 0.1)),
0
)) * (1 - (go_info{version=~"go1\\.2[0-3]"} == 0))
逻辑分析:
clamp_min防止负分;各子项按影响权重赋系数(如 GC CPU 占比权重 15);末尾go_info校验确保仅对目标 Go 版本生效。所有原始指标均经rate()或/归一化至无量纲。
指标权重分配表
| 指标维度 | 权重 | 归一化方式 |
|---|---|---|
| Goroutine 数量 | 10 | /1000 |
| GC CPU 占比 | 15 | rate(...[1h]) |
| 内存复用率 | 20 | 1 - alloc/total_alloc |
| 线程超限 | 5 | 布尔转 0/1 |
数据流图
graph TD
A[原始指标采集] --> B[1h滑动窗口rate/sum]
B --> C[加权线性组合]
C --> D[clamp_min + 版本过滤]
D --> E[go_runtime_health_score]
第五章:从监控到自治:协程智能治理演进路径
监控告警的瓶颈初现
某电商中台在大促期间启用 12 万 goroutine 处理实时订单分单,Prometheus + Grafana 告警频次达每分钟 87 次,但其中 93% 为“短暂超时”(
协程画像建模实践
团队在 Go runtime 上注入轻量探针,采集每个活跃 goroutine 的生命周期特征:启动栈帧深度、阻塞类型(syscall/ch block_type, sched_delay_p95, stack_depth, parent_id_hash)。下表为典型异常模式识别结果:
| 行为模式 | 协程数量占比 | 平均存活时长 | 关联故障类型 |
|---|---|---|---|
ch <- (full) + stack_depth > 8 |
2.1% | 14.2s | 缓冲区满导致生产者阻塞 |
syscall.Read + sched_delay_p95 > 120ms |
0.7% | 8.9s | 网络 I/O 轮询卡顿 |
无 parent_id + alive > 300s |
0.3% | 412s | goroutine 泄漏 |
自愈策略闭环设计
基于画像模型,部署三类自治动作:① 对 ch <- (full) 模式自动扩容缓冲区(调用 runtime/debug.SetGCPercent() 动态调整 GC 频率以释放内存压力);② 对长存活无父协程触发栈快照采集并终止;③ 对高延迟 syscall 协程启动熔断计数器,连续 3 次超时则降级为异步批处理。以下 Mermaid 流程图展示自治决策链路:
graph TD
A[采集 goroutine 实时特征] --> B{是否满足泄漏特征?}
B -->|是| C[生成 pprof goroutine stack]
B -->|否| D{是否持续阻塞 >5s?}
D -->|是| E[执行 buffer 扩容 + GC 调优]
D -->|否| F[进入健康观察窗口]
C --> G[上报至追踪平台并 kill]
E --> H[更新服务配置中心]
生产验证效果
在 2024 年双十二压测中,该自治系统拦截 100% 的 goroutine 泄漏事件(原平均每次大促泄漏 37 个),channel 阻塞导致的请求失败率从 0.83% 降至 0.02%,SRE 告警处理耗时减少 86%。关键改进在于将 runtime.GC() 触发逻辑从固定周期改为基于协程堆栈深度方差动态计算——当 stddev(stack_depth) > 5.2 时提前触发,避免因内存碎片化引发的调度雪崩。
治理能力沉淀机制
所有自治动作均通过 OpenPolicyAgent(OPA)策略引擎编排,策略版本与 GitOps 流水线绑定。例如 goroutine-leak-prevention.rego 规则定义:当 count(goroutines[?alive > 300 && ?parent_id == "nil"]) > 5 时,强制执行 debug.WriteHeapDump() 并推送告警至 PagerDuty。策略变更经混沌工程平台注入 kill -SIGUSR1 信号验证后,方可合并至 prod 分支。
运维界面交互升级
内部治理平台新增协程拓扑视图,支持点击任意节点查看其调度热力图(X轴:时间,Y轴:P ID,颜色深浅表示该 P 上 goroutine 密度)。开发人员可拖拽选择 3 秒时间窗,平台自动生成该时段内阻塞协程的火焰图,并标注阻塞点对应的源码行号(精确到 internal/poll/fd_poll_runtime.go:78)。
