Java告警案例

wen java案例 2

本文目录导读:

Java告警案例

  1. 典型案例分类
  2. 告警排查通用方法论
  3. 告警规则优化建议
  4. 一个完整的实战案例演示

Java 告警案例通常涉及线上故障排查性能瓶颈分析系统稳定性治理,下面我会从典型场景具体案例排查思路告警规则优化四个方面,结合实战经验为你梳理。


典型案例分类

高频 Full GC / GC 长时间停顿

现象:告警信息“GC 时间超过 500ms”、“Old Gen 使用率 > 90%”。

案例:某电商大促期间,订单服务出现频繁 Full GC,单次 STW(Stop-The-World)长达 2 秒,导致接口超时率飙升。

根因分析

  • 内存泄漏:缓存了未设置过期时间的对象(如将订单详情放入静态 Map 且未清理)。
  • 大对象直接进入老年代:批量查询返回超大 List(超过 -XX:PretenureSizeThreshold)。
  • MetaSpace 膨胀:动态生成大量代理类(如 CGLIB 父类泄漏)。

解决对策

# 使用 JDK 自带工具查看堆内对象
jmap -histo:live [pid] | head -20
# 或使用 MAT / VisualVM 分析 dump 文件
jmap -dump:format=b,file=heap.hprof [pid]

在代码中排查并修正异常的大对象或未释放的引用,调整 JVM 参数(如加大新生代、设置 G1 的 MaxGCPauseMillis)。


线程池耗尽(RejectedExecutionException)

现象:告警“线程池队列已满,任务拒绝”。

案例:文件处理服务使用 Executors.newFixedThreadPool(10),但每个任务耗时较长(如依赖外部 FTP 上传),高峰期导致队列积压 10 万+,最终触发拒绝策略。

根因分析

  • IO 密集型任务误用 CPU 密集配置
  • 未做超时控制:HTTP 调用/数据库连接等待时间过长,线程被长期占用。

解决对策

  • 使用有界队列ArrayBlockingQueue)并自定义拒绝策略(如降级、重试或持久化到 MQ)。
  • 对于 IO 密集型任务,适当调大线程数(如 2 * CPU 核心数)。
  • 给外部调用增加 ReadTimeout / ConnectTimeout,并配合 线程池监控(活跃线程数、队列积压量)。

数据库连接池连接泄漏

现象:告警“HikariCP 连接获取超时”、“连接数达到最大值”。

案例:分页查询服务在循环中执行 SQL 时,因为 try-with-resources 使用不当,导致连接未释放,最终连接池被打满,整个服务假死。

根因分析

  • 代码中手动获取 Connection 后未在 finally 中关闭。
  • 异常提前 return,跳过释放代码。
  • 慢 SQL 占住连接不放。

解决对策

  • 强制使用 try-with-resources@Transactional 托管连接。
  • 开启连接池的泄漏检测:
    spring:
    datasource:
      hikari:
        leak-detection-threshold: 60000  # 超过60秒未关闭则告警
  • 开启慢 SQL 日志,优化 SQL 索引。

内存中的“幽灵对象”导致的 OOM(OutOfMemoryError)

现象:告警“java.lang.OutOfMemoryError: Java heap space”或“GC overhead limit exceeded”。

案例:报表服务从 Kafka 消费数据时,将原始报文存储在本地 ThreadLocal 中,但消费线程被复用,导致数据残留膨胀。

根因分析

  • ThreadLocal 未 remove(在线程池场景是重灾区)。
  • 静态集合持有大对象。

解决对策

  • 在线程池任务中,finally 中务必调用 ThreadLocal.remove()
  • 建议使用 -XX:+HeapDumpOnOutOfMemoryError-XX:HeapDumpPath 保存现场,便于事后 MAT 分析。

告警排查通用方法论

  1. 看 JVM 状态

    jstat -gcutil [pid] 1000

    观察 YGC、FGC、堆内存占用趋势。

  2. 看线程状态

    jstack [pid] > thread_dump.txt

    重点查找 BLOCKEDWAITING 状态的线程,以及是否大量线程卡在 HTTPDB 调用上。

  3. 看接口 RT 分布:通过 APM(如 SkyWalking、Pinpoint)追踪有问题的链路,定位是自身代码还是下游依赖耗时。

  4. 看中间件指标:Redis 超时、MQ 消费堆积、数据库活跃连接数。


告警规则优化建议

避免“狼来了”:告警需要可运行、可处置

告警维度 建议阈值(示例) 说明
FGC 频率 连续 3 次,且间隔 < 5 分钟 比单次触发更有价值
FGC 时长 > 1 秒 结合业务容忍度调整
线程池活跃度 活跃线程 > 80% 且持续 10 分钟 排除瞬时尖峰
错误日志 Error 级别日志 5 分钟内超过 50 条 按接口维度聚合
Open API 接口 RT P99 > 2000ms 持续 3 分钟 关注长尾影响而非均值

核心原则:告警需要基于多指标(如 CPU + GC + 响应时间)叠加,减少误报。


一个完整的实战案例演示

场景:某支付服务在 12:00 告警,报“C2C 转账接口 P99 上升至 5s”。

  1. 紧急止血:先增大下游队列容量,或者切流量到备用机房(若有多活)。
  2. 定位
    • 通过 jstat 查看 GC,发现 FGC 频率升高
    • 通过 jmap dump 分析,发现 大量的 String 对象占据了 70% 堆空间——原来是日志框架误将 HttpServletRequest 的 Body 全部 toString() 打印。
  3. 解决:修改日志策略,只打印必要字段(或压缩日志),增加 JVM 堆内存。
  4. 复盘:告警规则增加“FGC 时长 > 1s 且伴随接口 RT 上升”的组合告警,避免以后再次发生。

处理 Java 告警的关键在于快速定位(JVM + 线程 + 链路),根源修复(代码 + 参数),以及持续优化(压测 + 监控)。

如果你遇到某个具体的报错(OutOfMemoryError: MetaspaceRejectedExecutionException),可以告诉我具体日志,我可以帮你进一步分析。

抱歉,评论功能暂时关闭!