← 返回文章列表
解决方案 4 分钟阅读 炬鲸团队

微服务故障定位慢?一套以 Trace 为中心的排查方案

微服务化之后,一次请求跨十几个服务,日志散落、指标孤立,定位慢接口要翻半天。本文给出以链路追踪为轴心、日志与指标协同的排查方案,包含采样策略、错误指纹聚合与告警联动,帮助团队把 MTTR 从小时级压到分钟级。

症状:知道慢了,不知道慢在哪

微服务化带来的典型困境:用户报「下单卡了」,你看到网关 P99 飙高,但背后十几个服务里到底哪个慢,全靠人肉翻日志。日志是散的,指标是孤立的,链路才是把一次请求串起来的骨架。

炬鲸 OBSERVE 的方案以 Trace 为轴心:用链路定位「慢在哪一跳」,再用该跳的日志看「为什么慢」,最后用指标看「影响多大」。三者围绕同一个 traceId 打通。

第一步:采样策略决定排查效率

全量采样成本高,但固定 1% 采样又会漏掉偶发故障。我们推荐头部采样 + 尾部采样组合:

  • 头部(Head-based):入口按 10% 概率采样,保证日常可分析流量。
  • 尾部(Tail-based):入口只标记不采样,出口根据结果决定保留——只要某跳 status = errorelapsed > 阈值,就强制保留整条链路。

这样错误链路 100% 留存,正常链路低成本抽样。配置上,采集器把 trace 数据发给 OTLP 网关,网关做尾部判定:

sampling:
  head:
    probability: 0.10
  tail:
    force_keep:
      - condition: "span.status == 'ERROR'"
      - condition: "span.duration_ms > 1500"

第二步:从慢 Trace 跳到对应日志

定位到慢 Span 后,下一步是看它的日志。关键动作:

  1. 在慢 Span 上直接下钻日志:系统用 traceId + spanId 反查该 Span 生命周期内的所有日志行,不用再去日志界面手输条件。
  2. 上下游耗时分解:Span 的耗时包含子 Span 调用、DB 查询、序列化等,把火焰图式的耗时分解展开,一眼看出是网络重试还是 SQL 慢。
  3. 核对错误指纹:同类错误(相同异常类型 + 相同栈顶帧)自动聚合成一个指纹,标出「首次出现时间」和「近 1h 出现次数」,判断是新增问题还是存量问题。

第三步:指标与告警联动

链路只回答「这次请求怎么样」,告警要回答「这影响面有多大」。把 trace 指标(延迟分位数、错误率)按服务维度导出到指标系统,告警规则直接消费:

alert: service_latency_p95_high
expr: trace_p95_ms{service="order"} > 1000
for: 5m
labels: {severity: warning}
annotations:
  summary: "order 服务 P95 延迟超过 1s"
  trace_query: "service=order AND elapsed>1000 ORDER BY elapsed DESC"

告警详情里直接带上 trace 查询链接,值班同学点开就是慢链路 Top 列表,省掉「从告警到证据」的拼装时间。

落地建议

  • 不要一上来全量上 trace:先给核心链路(下单、支付、登录)埋点,跑通「慢→日志→根因」的闭环,再逐步扩面。
  • 统一 traceId 传播:用 OpenTelemetry SDK 自动埋点 + 网关统一注入 traceId,避免各语言 SDK 各传各的、链路断裂。
  • 错误指纹要定期清理:指纹库会随版本发布失效,旧指纹保留 30 天即可,否则告警会淹没在历史噪声里。
  • 把采样参数做成可调:大促期间临时提高头部采样率,事后调回,别把采样写死在代码里。

以 Trace 为中心,把日志和指标挂到链路上,故障定位从「翻几十个服务的日志」变成「点开一条慢链路、下钻一次日志」,MTTR 的压缩是实打实的。