一、引言
当你遇到以下问题而束手无策时,Arthas或许可以帮助你:
- 这个类从哪个jar包加载的?为什么会报各种类相关的Exception?
- 我改的代码为何没有执行?难道我没有commit?版本不对?替换JAR再重启?
- 遇到问题无法在线上debug,难道只能通过加日志再重新发布吗?
- 线上遇到某个用户的数据处理有问题,但线上同样无法debug,而线下复现成本巨大,头疼?
- 是否有一个全局的视图来查看系统运行状况?
- 有什么办法可以监控到JVM的实时运行状态?
- 如何快速定位应用的热点,生成火焰图?
二、安装
根据推荐,使用如下命令启动Arthas,
java -jar arthas-boot.jar
java -jar arthas-boot.jar ${pid}
针对docker容器内的安装,需要传入Arthas压缩包
sudo docker cp /home/sunquan-temp/arthas-packaging-3.2.0-bin.zip ff32:/home
gunzip -d arthas-packaging-3.2.0-bin.zip
三、常用命令
- dashboard
针对进程当前内部情况的信息概览,其中包括线程、内存、运行时常量,如图:
四、热部署Java类
本节是介绍复用Arthas如何在不重启的情况下修改源码并使其生效。以一个spring-boot项目为例。
@Api(tags = "用户模块")
@Controller
public class UserController {
@ApiOperation(value = "用户登录", notes = "随边说点啥")
@ApiImplicitParams({
@ApiImplicitParam(name = "mobile", value = "手机号", required = true, paramType = "query"),
@ApiImplicitParam(name = "password", value = "密码", required = true, paramType = "query"),
//针对int类型要固定定义example
@ApiImplicitParam(name = "age", value = "年龄", required = true, paramType = "query", example = "1", defaultValue = "1", dataType = "int")
})
@ApiResponses({
@ApiResponse(code = 200, message = "请求成功"),
@ApiResponse(code = 400, message = "请求参数没填好"),
@ApiResponse(code = 404, message = "请求路径没有或页面跳转路径不对")
})
@ResponseBody
@PostMapping("/login")
public UserLoginVO login(@RequestParam String mobile, @RequestParam String password,
@RequestParam int age) {
System.out.println(mobile + password + age);
UserLoginVO userLoginVO = new UserLoginVO();
userLoginVO.setPassword(password);
userLoginVO.setUsername(mobile);
//System.out.println("success");
return userLoginVO;
}
}
通过请求最终返回:
{
“username”: “18912345675”,
“password”: “bca”
}
此时需要在该方法中显示打印一条success,在不重启服务情况下,通过Arthas步骤如下:
1、修改源码,增加success打印,如果你已经有源码,则可以在已有的源码上修改,如果没有,需要通过jad命名反编译字节码为源码,如
jad --source-only com.zte.sunquan.spring.ReadingListController > F:/1/ReadingListController.java
通过上述命令可以得到源码
2、通过sc查询该代码在当前进程中的类加载器对象实例
sc -d com.zte.sunquan.spring.ReadingListController | grep classLoaderHash
3、使用mc反编译修改后的类文件
mc -c 2513c0f4 E:/01sq-code/demo-for-learing-sq/spring-demo/src/main/java/com/zte/sunquan/spring/UserController.java -d F:/1
ps.该命令中直接使用IDE中的类文件,且在指定目录生成字节码文件
4、使用redefine命令重新加载编码好的新字节码文件
redefine F:/1/com/zte/sunquan/spring/UserController.class
ps.注意修改的JAVA类,不允许新增field/method,以及正在跑的函数,也不能有内部类,如未退出则不生效
结果:
再试触发rest调用,发现控制台果然打印出了success
五、线程查看
通过thread --help可以查看线程相关的查看指令
[arthas@98]$ thread --help
USAGE:
thread [-h] [-b] [-i <value>] [--state <value>] [-n <value>] [id]
SUMMARY:
Display thread info, thread stack
EXAMPLES:
thread
thread 51
thread -n -1
thread -n 5
thread -b
thread -i 2000
thread --state BLOCKED
WIKI:
https://alibaba.github.io/arthas/thread
OPTIONS:
-h, --help this help
-b, --include-blocking-thread Find the thread who is holding a lock that blocks the most number of threads.
-i, --sample-interval <value> Specify the sampling interval (in ms) when calculating cpu usage.
--state <value> Display the thead filter by the state. NEW, RUNNABLE, TIMED_WAITING, WAITING, BLOCKED, TERMINATED is optional.
-n, --top-n-threads <value> The number of thread(s) to show, ordered by cpu utilization, -1 to show all.
<id> Show thread stack
如图,可以查看到当前进程中线程统计和各类状态
通过thread ${tid}可以查看具体的线程栈
一般情况下,关注的是CPU占用多的线程,通过上述方式,可以大致判断出业务执行逻辑处,再进行后序处理。
五、Trace指令
arthas提供的trace指令可以帮助开发人员方便地进行性能优化,如下,可以对指定的方法进行执行耗时采样
由于当前项目应用在docker中,所以记录详细使用步骤供参考
- 将arthas压缩包拷贝至需要监测应用的容器内部
docker cp /sq/arthas-packaging-3.2.0-bin.zip d9c:/opt/arthas
- 解压arthas压缩包
unzip -x arthas-packaging-3.2.0-bin.zip
- 启动arthas
java -jar arthas-boot.jar
通过trace --help可以查看帮助文档和命令示例
EXAMPLES:
trace org.apache.commons.lang.StringUtils isBlank
trace *StringUtils isBlank
trace *StringUtils isBlank params[0].length==1
trace *StringUtils isBlank ‘#cost>100’
trace -E org\.apache\.commons\.lang\.StringUtils isBlank
trace -E com.test.ClassA|org.test.ClassB method1|method2|method3
trace demo.MathGame run -n 5
trace demo.MathGame run --skipJDKMethod false
-E参考是正则匹配,用的比较多,我们使用如下指令,对存储服务的getNodesBy和getLists方法进行监控采样
trace *StoreService getNodesBy | getLists
执行业务逻辑后打印:
arthas为性能计,由于耗时性能采集只支持一层,开发人员可以根据打印,继承trace逐层追踪。如上很明显耗时最多的是getLists(); 继续
trace *StoreServiceImpl getLists