线上故障定位:从报警到根因的实战指南

发布于:2025-08-17 ⋅ 阅读:(20) ⋅ 点赞:(0)

当监控告警响起时,如何30分钟内锁定问题根源?

一、问题定位黄金法则

  1. 保持冷静
    ➤ 禁用非必要变更(代码/配置发布)
    ➤ 立即启动故障响应流程
    ➤ 留存案发现场:​禁止重启服务!​

  2. 三大核心问题

    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 复现高并发场景问题

四、避坑指南(血泪经验)

  1. 日志陷阱
    ➤ 避免e.printStackTrace()(异步丢失日志)
    ➤ 关键日志增加TraceID(链路追踪)

  2. 监控盲区

    [必须监控项]
    √ JVM堆外内存(DirectBuffer/Native)
    √ 线程池队列堆积情况
    √ 第三方接口P99耗时
    √ 数据库连接池使用率
  3. 止血原则

    优先级顺序:
    1. 服务降级(关非核心功能)
    2. 流量调度(切走故障单元)
    3. 滚动重启(单节点隔离重启)
    → 严禁直接全量重启集群!

五、构建预防体系

  1. 故障演练常态化

    • 每月一次Chaos工程(模拟网络分区/节点宕机)
    • 核心服务设计熔断/降级预案
  2. 防御性编码规范

    // 资源关闭必须try-with-resources
    try (Connection conn = dataSource.getConnection()) {
       // ...
    }
    
    // 线程池必须设拒绝策略
    new ThreadPoolExecutor(..., new ThreadPoolExecutor.CallerRunsPolicy());
  3. 健康检查三板斧

    # K8s存活探针配置
    livenessProbe:
      httpGet:
        path: /actuator/health
      initialDelaySeconds: 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 # 网络封包

掌握这些技能,下一次线上告警将是你的高光时刻!


网站公告

今日签到

点亮在社区的每一天
去签到