找回密码
立即注册
搜索
热搜: Java Python Linux Go
发回帖 发新帖

2786

积分

0

好友

374

主题
发表于 5 天前 | 查看: 18| 回复: 0

昨天下午,突然收到运维邮件报警,显示数据平台服务器的 CPU 利用率飙升到了 98.94%。最近一段时间,这个数值也持续在 70% 以上。第一反应可能是硬件资源遇到了瓶颈需要扩容,但仔细一想,我们的业务系统并非高并发或 CPU 密集型应用,这个利用率高得有些反常。硬件瓶颈通常不会来得这么快,大概率是某处的业务代码逻辑出了问题。

2、排查思路

2.1 定位高负载进程 pid

我们首先要做的是登录服务器,使用 top 命令来确认实际情况,再根据具体信息进行分析判断。

系统负载平均值监控输出

通过观察 load average,并结合服务器核心数(例如8核)的评判标准,可以确认当前服务器的负载确实过高。

top命令显示的进程CPU占用情况

进一步观察各个进程的资源使用情况,可以发现进程 ID(PID)为 682 的进程占用了极高的 CPU 资源。

2.2 定位具体的异常业务

这时,我们可以使用 pwdx 命令,根据 PID 找到对应业务进程的工作目录,从而定位到项目负责人和具体项目。

pwdx命令定位进程路径

通过命令输出,可以得出结论:这个高 CPU 占用的进程,正是数据平台的 Web 服务。

2.3 定位异常线程及具体代码行

传统的线程问题定位方案一般分为四步:

  1. top oder by with P:按进程负载排序,找到负载最高的 PID。
  2. top -Hp 进程PID:查看指定进程下的所有线程,找到 CPU 占用高的线程 PID。
  3. printf “0x%x ”线程PID:将线程 PID 转换为 16 进制,为后续查找 jstack 日志做准备。
  4. jstack 进程PID | vim +/十六进制线程PID -:使用 jstack 输出线程堆栈,并快速定位到特定线程。

但对于需要争分夺秒的线上问题排查来说,这四步操作还是略显繁琐。值得庆幸的是,之前介绍过的淘宝同学 oldratlee 已将这套流程封装成了一个非常便捷的工具——show-busy-java-threads.sh,可以极大提升这类问题的定位效率。

jstack线程堆栈分析输出

从工具的输出可以清晰看到,一个名为 Timer-8 的线程占用了 83.2% 的 CPU,并且堆栈跟踪指向一个时间工具类中的特定方法:com.bj58.zhuanzhuan.analysis.util.TimestampUtil.TimestampToOdate(TimestampUtil.java:31)。定位到具体方法后,接下来就是审查其代码逻辑,看是否存在性能缺陷。

提示:如果线上情况万分紧急,可以跳过 2.1 和 2.2 步骤,直接使用 show-busy-java-threads.sh 这类工具进行线程分析。本文从多角度分步骤阐述,旨在为大家呈现一套完整、清晰的排查思路。

3、根因分析

经过上述层层排查,最终问题根源锁定在一个时间工具类的方法上,正是它导致了服务器负载和 CPU 使用率的异常飙升。

  • 异常方法逻辑:该方法的功能是将时间戳转换为具体的日期时间格式。
  • 上层调用:在业务逻辑中,需要计算从当天凌晨到当前时间点的每一秒,并调用该方法将每一秒的时间戳都格式化,然后将结果放入一个 Set 集合中返回。
  • 业务场景:该逻辑应用于数据平台的实时报表查询。实时报表会以固定时间间隔进行查询,而单次查询中,这个方法会被调用非常多次(记为 n 次)。

这样一来,问题就显而易见了。假设当前时间是上午 10 点,那么一次查询中,这个方法的计算次数就是 10 * 60 * 60 * n = 36,000 * n 次。更关键的是,随着时间向午夜靠近,单次查询的计算次数还会线性增长。由于实时查询、实时报警等模块会发起大量的查询请求,每个请求又会多次调用这个高开销的方法,最终导致了 CPU 资源的严重浪费和占用。

4、解决方案

定位到问题后,首要思路就是减少不必要的计算,优化这个异常方法。进一步排查业务代码发现,上层逻辑其实并没有使用该方法返回的 Set 集合中的具体日期字符串,而仅仅是获取了 Set 的 size() 值。

确认这一点后,解决方案就变得简单直接:我们可以用一个计算量极小的新方法来替代它——直接计算“当前秒数”与“当天凌晨秒数”的差值。替换上线后,持续观察服务器负载和 CPU 使用率,发现相比异常时段下降了近 30 倍,各项指标均恢复至正常健康状态。至此,问题得以圆满解决。

优化后top命令显示系统恢复正常

5、总结

  • 编码时需兼顾性能:实现业务逻辑是基本要求,但追求更高效、更优雅的实现方式,则体现了工程师更深层次的能力和境界,这也是工程师的核心竞争力所在。
  • 重视代码审查(Code Review):代码完成后,应多进行复盘和审查,思考是否有更好的实现方案,防患于未然。
  • 深究线上问题的每个细节:细节决定成败。技术人员应具备刨根问底的好奇心和追求卓越的精神,这正是我们能够在运维与开发道路上不断成长和提升的关键。面对线上告警,就像本次遇到的Java进程CPU飙高,一套科学、高效的排查流程至关重要。



上一篇:Claude Code源码泄露:顶级AI编程Agent的“底牌”因npm打包失误意外公开
下一篇:后端开发语言选择:Java与C#在性能、生态与职业前景的深度对比
您需要登录后才可以回帖 登录 | 立即注册

手机版|小黑屋|网站地图|云栈社区 ( 苏ICP备2022046150号-2 )

GMT+8, 2026-4-7 17:59 , Processed in 0.914589 second(s), 42 queries , Gzip On.

Powered by Discuz! X3.5

© 2025-2026 云栈社区.

快速回复 返回顶部 返回列表