[TOC]
背景
最近正式环境出现一起事故,业务端经过一些列的业务逻辑之后,使用wkhtmltopdf
工具进行html
转换为pdf
,但出现生成完成之后(正常结束,并未发生异常)业务端再次获取这个pdf
文件时,出现文件不存在问题。
排查
经过一系列的排查,最终锁定在wkhtmltopdf
工具是否正常生成pdf
文件,请看如下代码,这里通过wkhtmltopdf
提供的工具类pdf.saveAsDirect(ftpDir + "/" + path);
进行pdf
的生成,但并未对返回的结果进行判断,所以猜测这里根本没有生成pdf
文件。
1 | private boolean uploadToPdfDirect(WrapperConfig config, String sourceString, String path) { |
根据猜测希望将代码修改为:(本想借助Arthas
将代码修改以下代码,但由于线上一直产生数据,事故面积越来越大,来不及研究Arthas
的使用,只能走繁琐的流程,进行发包替换正式环境的包)
1 | private boolean uploadToPdfDirect(WrapperConfig config, String sourceString, String path) { |
正题
经过漫长的发版流程等待,等到正式环境替换包已经是大半夜。经过测试,确实是pdf
文件未生成(如果会使用Arthas
,这个时候应该就已经解决问题)。这里要说明下,到目前为止只知道这种方式会生成失败,至于在什么情况下生成失败就不得而知了,因为开发环境、测试环境、预发布环境都是正常生成。痛定思痛,决定学习一下Arthas
的使用。
1.使用
简单的使用流程:
找到需要进行添加代码的类全路径后进行反编译到某个文件夹中。
jad –source-only com.xxx.service.v3.xxxService > /tmp/xxxService.java
这里是由
.class
反编译为.java
文件,所以阅读上没有那么友好,其实刚出现问题的时候就想到使用Arthas
,但是被这个反编译出来的代码劝退了,反编译出来的代码甚至有指令重排,不太敢修改这个反编译文件。修改这个反编译文件
vim /tmp/UserController.java
这里实际上是要先退出
arthas
,第一次使用的时候还以为arthas
命令行可以直接操作文件,实际上应该退出arthas
使用linux
命令的方式进行文件修改。查找这个类对应的类加载器
sc -d *xxxService | grep classLoadHash
classLoadHash 6bc26251
这里得到这个类的加载器的哈希值为
6bc26251
使用这个类加载器将修改后的文件编译为
.class
文件mc -c 6bc26251 /tmp/xxxService.java -d /tmp
这时会在
/tmp
文件夹下生成一个以包结构为文件路径的.class
文件。进行热更新
redefine /tmp/com/xxx/service/v3/xxxService.class
当看到提示
redefine success, size: 1
说明替换成功,就可以进行具体的测试。
以上这种方式,出现问题的概率极大,因为要修改反编译文件,且反编译文件好像进行了一些指令重排,导致阅读上比较困难,实际上以上的前四步骤就是为了得到修改之后的.class
文件,那么实际上我们可以借助于idea
进行处理。
- 找到线上代码的标签,拉取修改文件分支。
- 检出这个分支,进行特定文件的修改后,直接使用
idea
工具进行文件的编译。这时就可以通过target
文件夹获取到对应修改文件的.class
文件。 - 将这个
.class
文件上传到对应服务器。 - 直接热更新这个文件。(也就是上述的第5步)
2.示例
- 在某个接口的服务层添加一个日志打印信息。
1 | public RequireFileVo getFiles(ParamVo ParamVo, Integer pageNum, Integer pageSize) { |
通过
idea
对整个服务进行编译后,获取到该文件的.class
文件。上传到服务器中,由于服务是运行在
docker
中,所以上传到服务器后移动文件到docker
与宿主机的挂载目录中。从挂载目录移动到简单目录。(测试的时候使用的挂载目录路径比较长,就移动到简单一点的目录比如
/tmp
)使用
redefine
命令后看到redefine success, size: 1
表示成功测试,通过
postman
调用1
22021-11-18 10:43:30.491 [TraceId=92aeb19c3e8ce62b,SpanId=92aeb19c3e8ce62b,ParentSpanId=] [http-nio-20065-exec-5] INFO c.b.g.b.s.v.s.xxxService-pageSize:10
其他基础功能
- reset:重置增强类,此命令会将
arthas
增强过的类都重置为增强之前的情况。 - quit:退出,
quit
只是退出archas
的session
,实际上目标进程arthas server
还在运行中。 - stop:彻底退出
arthas
,使用此命令后,实际上会执行一次reset
命令。
注意:要区别于quit
和stop
两个退出之间的区别。
通过线程
id
查看线程的具体信息,比如查看线程id
为1的线程的具体信息:thread 1
1.watch:查看类里面的某个方法的返回值和入参(实际上只能看参数类型, 是否有值, 并不能看值是多少)
命令 + 类完全限定名 + 需要检测的方法 + 表达式 + (额外参数)
watch com.xxx.xxx.service.v3.xxxService getFiles “{params, returnObj}” -x 2
1 | 额外参数: |
1 | 表达式核心变量列表: |
2.trace:方法内部的调用路径,并输出方法路径上的每个节点耗时
命令 + 类完全限定名 + 需要检测的方法
trace com.xxx.xxx.service.v3.xxxService getFiles
3.heapdump:生成快照信息
heapdump (–live) 生成文件路径/xxx.hprof
–live:只dump
活着的对象
我是使用jdk
自带的jvisualvm
来打开xx.hprof
文件进行分析。jdk
自带的jvisualvm
在~/home/bin
目录下。顺便记录下mac
系统查看jdk home
所在位置的方式:执行/usr/libexec/java_home -V
4.sysprop:查看Sysetm properties
信息
可以指定单个
sysprop @appId
或者使用grep
进行过滤
sysprop | grep @appId
再或者直接添加一个新的键值对
sysprop timeout 50
5.sysenv:查看环境变量(使用上和sysprop
一致)
注意:系统变量通过System.getProperty()
,环境变量通过System.getEnv()
。系统变量可以通过-D
的方式在启动时添加,环境变量
6.jvm:可以查看jvm
虚拟机的一些信息
7.sc:可以查看已经加载的类,如果类是接口,可以获取到已加载的所有实现类,通过-d
参数可以打印类加载的具体信息。并且支持通配符查找类。
sc -d xxx
8.sm:查找具体的函数
最后
文档地址[https://arthas.aliyun.com/doc/]
git地址(https://github.com/alibaba/arthas)
使用教程地址(https://arthas.aliyun.com/doc/arthas-tutorials.html?language=cn&id=arthas-advanced)