如何从报警开始反推到代码行
一句话速记
从报警到代码行的反推链路:报警信息(什么指标异常)→ 监控大盘(确认异常范围和趋势)→ 日志/Trace(找异常时间点的具体请求)→ TraceID(展开完整调用链)→ arthas 在线诊断(trace/watch 定位到方法/代码行)→ 代码定位(从方法名反查 git log 找最近的提交)。核心原则:每步都缩小排查范围,不要跳过中间步骤。
通俗解释(5 分钟版)
报警:P99 延迟从 200ms 飙到 3000ms
│
├─ 步骤 1(30 秒):看监控大盘,确认范围
│ 是单个接口慢 还是所有接口?→ 只有 /api/order/create 慢
│ 是所有实例慢 还是单个实例?→ 所有实例都慢
│ 从什么时候开始?→ 14:30 开始,正好是刚上线的时间
│
├─ 步骤 2(1 分钟):找到异常 Trace
│ 打开 SkyWalking → 选 /api/order/create → 看 14:30 后的 trace
│ → 找到慢的 trace → 展开:
│ OrderService.createOrder() = 2800ms
│ ├── validateStock() → InventoryService = 50ms ✓
│ ├── deductCoupon() → CouponService = 2700ms ✗← 这里慢!
│ │ └── matchRule() = 2680ms ✗← 这个方法
│
├─ 步骤 3(1 分钟):定位到代码行
│ arthas attach 到订单服务
│ arthas> trace com.example.service.CouponService matchRule -n 5
│ → 看到 matchRule() 内部:
│ Pattern.compile() = 2500ms ← 正则编译/匹配在这一步
│ ...
│
├─ 步骤 4(1 分钟):反查代码
│ git log --since="2024-01-15" --until="2024-01-16" -- src/main/java/com/example/service/CouponService.java
│ → commit abc123 在今天 14:28 改了 matchRule() 的正则
│ → 看 diff → 某营销同事加了一个复杂正则规则
│
└─ 结论:新上线的正则规则导致回溯,回滚这个 commit 或优化正则
关键细节
步骤 1:从报警信息定界
不同报警源的排查起点:
报警类型 第一眼看什么 下一步
──────────────────────────────────────────────────────────
P99 延迟飙升 监控大盘:按接口维度看 哪一个接口慢
是单个接口还是全局?
QPS 突降 监控大盘:是入口 QPS 降 上游限流了?
还是处理的 QPS 降? 自己处理不过来?
错误率飙升 监控大盘:错误码分布 4xx(参数)→ 新上线改了什么
(4xx / 5xx) 5xx(服务内部)→ 看日志
CPU / 内存飙升 arthas dashboard thread -n 5 找 CPU 线程
memory 看内存分布
Pod OOMKilled 事件日志 + JVM 参数 是否 HeapDumpOnOutOfMemoryError
kubectl describe pod dump 文件在哪里
DB 连接池耗尽 DB processlist 谁在长时间占用连接
SHOW PROCESSLIST 有没有慢查询
定界四问:
1. 影响范围:所有实例还是个别实例?→ 个别 → 实例级别问题(网络/磁盘/GC)
2. 时间范围:从什么时候开始的?→ 看部署时间线(最近一次上线/配置变更)
3. 影响接口:所有接口还是某个接口?→ 某个 → 查该接口的调用链
4. 影响用户:所有用户还是特定用户?→ 特定 → 看用户数据是否触发边界 case
步骤 2:从 Trace/Log 找异常请求
有全链路追踪:
SkyWalking / Zipkin → 选慢接口 → 筛选时间范围
→ 按 duration 降序 → 找到最慢的 trace
→ 展开 span 树 → 找到耗时占比最大的 span
→ 点进去看 span 的 tags/logs:
可能包含:SQL 语句、RPC 入参、异常堆栈、方法名
只有日志(没有全链路追踪):
# 1. 根据报警时间圈定日志时间范围
grep "2024-01-15 14:30" /var/log/app.log | grep "ERROR\|WARN"
# 2. 按 TraceID 聚合请求
grep "abc123" /var/log/app.log | sort -k1 # 按时间排序
# 手动串出一个请求的完整生命周期
# 3. 找耗时长的方法
grep "2024-01-15 14:3[0-5]" /var/log/app.log | \
grep "execute\|invoke\|call" | \
awk '{print $NF}' | sort -rn | head -20
# 假设最后一列是耗时步骤 3:arthas 在线定位方法耗时
# 反编译确认类和方法签名(不需要源码)
jad com.example.service.CouponService
# 追踪方法调用树和每步耗时(最常用)
trace com.example.service.CouponService matchRule -n 5
# -n 5:只追踪 5 次(避免追踪太久影响生产)
# 输出示例:
# `---ts=2024-01-15 14:30:25;thread_name=http-nio-8080-exec-3;id=2a;is_daemon=true;priority=5
# `---[2680.5ms] com.example.service.CouponService:matchRule()
# +---[0.01ms] com.example.rule.RuleEngine:loadRules()
# `---[2680.0ms] java.util.regex.Pattern:matches() ← 正则匹配 2.68s!
# `---[2680.0ms] java.util.regex.Pattern$Curly:match()
# 查看方法入参和返回值
watch com.example.service.CouponService matchRule '{params,returnObj}' -x 3
# 可以看到每次调用的入参 → 找到导致慢的特定输入步骤 4:反推代码变更
# 找到方法所在的文件
# 从 arthas jad 输出知道类名 → 推断文件路径
find . -name "CouponService.java"
# 看最近的变更
git log --oneline -10 -- src/main/java/com/example/service/CouponService.java
# 看具体改了什么
git diff HEAD~1 -- src/main/java/com/example/service/CouponService.java
# 看是谁改的、什么时候改的
git log --follow -p -- src/main/java/com/example/service/CouponService.java | head -100
# 如果确定了某个 commit 是问题引入点
git show abc123完整排查决策树
报警
│
├─ 是刚上线后马上出现的?
│ → git log 看最近 1 小时的上线记录
│ → 直接怀疑这次上线引入的 → 回滚验证
│
├─ 是逐渐变慢的?
│ → 怀疑数据量增长/泄漏 → jstat -gc 看 Old 区趋势
│ → 怀疑慢查询 → DB processlist
│ → 怀疑连接泄漏 → ss -tan state close-wait + leakDetectionThreshold
│
├─ 是周期性出现的?
│ → 怀疑定时任务/定时 GC → 对比时间线和 GC 日志/crontab
│
└─ 是突发性的(个别请求慢)?
→ 全链路追踪找慢 trace → arthas trace/watch → 定位方法
延伸追问
- Q:事后排查,日志已经被轮转删了怎么办? → 提前把日志打到 ELK/Loki/SLS 等集中式日志平台,设好保留期(至少 7 天)。如果日志真的丢了 → 靠监控指标回顾(Prometheus metrics 通常保留更久),至少能知道”什么时间、什么接口、什么指标异常”。
- Q:trace/watch 在生产环境安全吗?
→ arthas trace 有性能开销(约 5-10%),建议:① 加
-n限制次数;② 只 trace 慢方法而非入口方法;③ 排查完马上下线(stop或reset)。watch 同理,不要对高频方法大量 watch。 - Q:怎么证明”就是这次上线导致的”? → 最硬证据:回滚后问题消失 → 确定是这次上线的代码引入。辅助证据:① 监控图上看指标突变时间 = 上线时间;② git diff 找到可疑代码变更;③ arthas trace 证实该代码确实慢。
我的记法
- 四步反推:报警信息(定范围)→ 监控/Trace(找慢请求)→ arthas trace(定位方法)→ git log(找变更)
- 定界四问:影响范围?什么时间开始?哪个接口?哪个用户?
- 最有力证据:回滚后问题消失 = 100% 确定是这次上线
- 一句话:「从报警到代码行,每步都在缩小包围圈,别跳步,跳步 = 猜」
状态
- 已背速记
- 能讲通俗版
- 能答追问