作者 | 李昊(可以养肥)
【Arthas 官方社区正在举行征文活动,参加即有奖品拿~点击投稿】
生产环境 CPU 告警:
13:40 收到我们的生产环境服务器绿版 CUP 超负载告警通知。
此时心里只有一个想法,重启大法好,马上登录服务器,执行 top 发现进程 30247 和 28337 占用 CPU 为 200 多和100 多基本占用了 4 核的 3 核,整个过程大概用时 30 秒,维护群依然很平静,运营的电话也没打过来,这时候我断定,这次问题应该影响面很小,用户可能也暂时没有发现,好吧,还有时间做排查。
Arthas排查过程:
- 开启 Arthas 工具找到对应的 30247 运单模块和 28337 支付模块,选择运单模块进入:
java -jar arthas-boot.jar
- 执行 dashboard 命令,线程 35 和 12042 不正常 CUP 占用 49%:
dashboard
- 执行 thread 35 thread 12042 定位代码行:
thread 35
thread 12042
- 查看代码,业务需求为生成一个至少包含 2 个数字的随机字符串,我们使用的统一的工具类方法,该方法中先通过
UUID.randomUUID()
随机出一个 10 位的字符池,然后再从这个字符池中随机需要位数的字符串,如果随机出来的 10 位字符池中都是字母,则二次随机时候就会出现死循环,问题代码如下:
public static String getRandomStr(boolean numberFlag, int length) {
String retStr = "";
String strTable =
numberFlag
? UUID.randomUUID().toString().replaceAll("-", "").substring(0, 10)
: "1234567890abcdefghijkmnpqrstuvwxyz";
int len = strTable.length();
boolean bDone = true;
do {
retStr = "";
int count = 0;
for (int i = 0; i < length; i++) {
double dblR = Math.random() * len;
int intR = (int) Math.floor(dblR);
char c = strTable.charAt(intR);
if (('0' <= c) && (c <= '9')) {
count++;
}
retStr += strTable.charAt(intR);
}
if (count >= 2) {
bDone = false;
}
} while (bDone);
return retStr;
}
- 线下模拟不到二万次
UUID.randomUUID()
前十位会出现一次全字母的情况。
- 最终原因是死循环导致的 CPU 飚高,修复代码,增加是否都是字母的判断,第一次随机出来的 10 位字符池都是字母,则重新随机。
Arthas 常用命令:
安装
curl -O arthas.aliyun.com/arthas-boot…<br />java -jar arthas-boot.jar
基础命令
- help——查看命令帮助信息
- cat——打印文件内容,和 linux 里的 cat 命令类似
- echo–打印参数,和 linux 里的 echo 命令类似
- grep——匹配查找,和 linux 里的 grep 命令类似
- tee——复制标准输入到标准输出和指定的文件,和 linux 里的 tee 命令类似
- pwd——返回当前的工作目录,和 linux 命令类似
- cls——清空当前屏幕区域
- session——查看当前会话的信息
- reset——重置增强类,将被 Arthas 增强过的类全部还原,Arthas 服务端关闭时会重置所有增强过的类
- version——输出当前目标 Java 进程所加载的 Arthas 版本号
- history——打印命令历史
- quit——退出当前 Arthas 客户端,其他 Arthas 客户端不受影响
- stop——关闭 Arthas 服务端,所有 Arthas 客户端全部退出
- keymap——Arthas 快捷键列表及自定义快捷键
jvm相关
- dashboard——当前系统的实时数据面板
- thread——查看当前 JVM 的线程堆栈信息
- jvm——查看当前 JVM 的信息
- sysprop——查看和修改 JVM 的系统属性
- sysenv——查看 JVM 的环境变量
- vmoption——查看和修改 JVM 里诊断相关的 option
- perfcounter——查看当前 JVM 的 Perf Counter 信息
- logger——查看和修改 logger
- getstatic——查看类的静态属性
- ognl——执行 ognl 表达式
- mbean——查看 Mbean 的信息
- heapdump——dump java heap, 类似 jmap 命令的 heap dump 功能
class/classloader相关
- sc——查看 JVM 已加载的类信息
- sm——查看已加载类的方法信息
- jad——反编译指定已加载类的源码
- mc——内存编译器,内存编译 .java 文件为 .class 文件
- redefine——加载外部的 .class 文件,redefine 到 JVM 里
- dump——dump 已加载类的 byte code 到特定目录
- classloader——查看 classloader 的继承树,urls,类加载信息,使用classloader 去 getResource
monitor/watch/trace相关
- monitor 方法执行监控
monitor -c 5 demo.MathGame primeFactors
-c 5 未统计周期默认 120s
- watch 能观察到的范围为:返回值、抛出异常、入参
watch demo.MathGame primeFactors “{params,target,returnObj}” -x 2 -b -s -n 2
-x 2 输出结果的属性遍历深度 -b 方法调用前 -s 方法返回后 -n 2 执行2次
watch demo.MathGame primeFactors “{params[0],throwExp}” -e -x 2
-e表示抛出异常时才触发
- trace 方法内部调用路径,并输出方法路径上的每个节点上耗时
trace demo.MathGame run