线上故障定位:从报警到根因的实战指南
当监控告警响起时,如何30分钟内锁定问题根源?
一、问题定位黄金法则
保持冷静
➤ 禁用非必要变更(代码/配置发布)
➤ 立即启动故障响应流程
➤ 留存案发现场:禁止重启服务!三大核心问题
1. 现象是什么? (错误率上升?接口超时?OOM?) 2. 影响范围多大? (单个节点还是集群?特定用户还是全局?) 3. 何时开始异常? (结合监控时间线定位触发点)
二、四类经典场景定位手册
▎场景1:CPU 100% 飙升
排查步骤:
# 1. 定位异常进程
top -c -Hp <PID> # 2. 找出高耗用线程(10进制转16进制)
printf "%x" <THREAD_ID> → 得到nid# 3. 抓取线程栈
jstack <PID> > thread_dump.log# 4. 定位问题线程栈
grep -A 20 'nid=0x<HEX_ID>' thread_dump.log# 5. 反查代码(重点关注:死循环/复杂正则/密集计算)
高频原因:
➤ 死循环(如while(true)
未设退出条件)
➤ 正则表达式回溯(如(a+)+
匹配长字符串)
➤ 大数据集排序/计算(未做分页处理)
▎场景2:内存泄漏/OOM
排查步骤:
# 1. 查看GC状态(关注老年代)
jstat -gcutil <PID> 1000 5# 2. 导出内存快照(JDK8)
jmap -dump:live,format=b,file=heap.hprof <PID># 3. 分析内存对象(MAT/JProfiler)
> 按Retained Heap排序 → 定位可疑对象链
> 对比多个dump文件 → 识别持续增长对象
经典泄漏点:
// 静态集合持有大对象(如全局Cache)
static Map<String, byte[]> CACHE = new HashMap<>();// 未关闭的资源(数据库连接、文件流)
Connection conn = DriverManager.getConnection(url);
// 忘记conn.close()!
▎场景3:接口响应缓慢
链式排查法:
关键命令:
# 查看网络延迟
mtr -z 目标IP# 抓取HTTP请求详情
tcpdump -i eth0 'tcp port 8080' -w http.pcap# 检测MySQL慢查询(实时)
SHOW FULL PROCESSLIST;
▎场景4:线程池阻塞/死锁
定位技巧:
# 1. 检查线程状态
jstack <PID> | grep -E 'BLOCKED|WAITING' -C 3# 2. 用Arthas快速诊断
[arthas@]$ thread -b # 找出阻塞线程
[arthas@]$ watch *ClassName methodName "{params,returnObj}" -x 3
典型死锁代码:
// 线程1
synchronized(lockA){synchronized(lockB){...} // 等待lockB
}// 线程2
synchronized(lockB){synchronized(lockA){...} // 等待lockA
}
三、定位工具箱(必备技能)
工具类型 | 推荐工具 | 核心用途 |
---|---|---|
系统监控 | Prometheus + Grafana | 实时观测CPU/内存/网络指标 |
链路追踪 | SkyWalking / Jaeger | 跨服务调用链路分析 |
实时诊断 | Arthas / VJTools | 动态方法监控/热修复 |
日志分析 | ELK(ES+Logstash+Kibana) | 快速检索异常日志 |
网络抓包 | tcpdump + Wireshark | 协议级问题定位 |
压测工具 | JMeter / LoadRunner | 复现高并发场景问题 |
四、避坑指南(血泪经验)
日志陷阱
➤ 避免e.printStackTrace()
(异步丢失日志)
➤ 关键日志增加TraceID(链路追踪)监控盲区
[必须监控项] √ JVM堆外内存(DirectBuffer/Native) √ 线程池队列堆积情况 √ 第三方接口P99耗时 √ 数据库连接池使用率
止血原则
优先级顺序: 1. 服务降级(关非核心功能) 2. 流量调度(切走故障单元) 3. 滚动重启(单节点隔离重启) → 严禁直接全量重启集群!
五、构建预防体系
故障演练常态化
- 每月一次Chaos工程(模拟网络分区/节点宕机)
- 核心服务设计熔断/降级预案
防御性编码规范
// 资源关闭必须try-with-resources try (Connection conn = dataSource.getConnection()) {// ... }// 线程池必须设拒绝策略 new ThreadPoolExecutor(..., new ThreadPoolExecutor.CallerRunsPolicy());
健康检查三板斧
# K8s存活探针配置 livenessProbe:httpGet:path: /actuator/healthinitialDelaySeconds: 20 # 给JVM预热时间failureThreshold: 3 # 连续失败3次判定异常
终极提示:把每一次故障写成事故报告,核心包含——时间线、根因、改进措施、知识沉淀(形成团队智库)
附:紧急响应清单
1. jstack <PID> > jstack.log # 保存线程栈
2. jmap -dump:format=b,file=heap.hprof # 堆内存快照
3. top -Hp <PID> -b -n 3 > top.log # 系统资源快照
4. grep 'ERROR' app.log -A 50 -B 20 # 关键错误上下文
5. tcpdump -i eth0 port 8080 -w net.cap # 网络封包
掌握这些技能,下一次线上告警将是你的高光时刻!