携手创作,一起生长!这是我参与「日新方案 8 月更文挑战」的第21天,点击检查活动详情

实操案例

排查函数调用反常

经过curl 恳求接口只能看到回来反常,可是看不到详细的恳求参数和仓库信息。

shell@Alicloud:~$ curl http://localhost:61000/user/0
{"timestamp":1655435063042,"status":500,"error":"Internal Server Error","exception":"java.lang.IllegalArgumentException","message":"id < 1","path":"/user/0"}

检查UserController的 参数/反常

在Arthas里履行:

watch com.example.demo.arthas.user.UserController * '{params, throwExp}'
  1. 第一个参数是类名,支撑通配
  2. 第二个参数是函数名,支撑通配 拜访 curl http://localhost:61000/user/0 ,watch指令会打印调用的参数和反常

再次经过curl 调用能够在arthas里面检查到详细的反常信息。

Java 诊断工具 Arthas-实操案例
把获取到的成果打开,能够用-x参数:

watch com.example.demo.arthas.user.UserController * '{params, throwExp}' -x 2

回来值表达式

在上面的比方里,第三个参数是回来值表达式,它实践上是一个ognl表达式,它支撑一些内置目标:

  • loader
  • clazz
  • method
  • target
  • params
  • returnObj
  • throwExp
  • isBefore
  • isThrow
  • isReturn

比方回来一个数组:

watch com.example.demo.arthas.user.UserController * '{params[0], target, returnObj}'

条件表达式

watch指令支撑在第4个参数里写条件表达式,比方: 当拜访 user/1 时,watch指令没有输出 当拜访 user/101 时,watch会打印出成果。

Java 诊断工具 Arthas-实操案例

当反常时捕获

watch指令支撑-e选项,表示只捕获抛出反常时的恳求:

watch com.example.demo.arthas.user.UserController * "{params[0],throwExp}" -e

依照耗时进行过滤

watch指令支撑按恳求耗时进行过滤,比方:

watch com.example.demo.arthas.user.UserController * '{params, returnObj}' '#cost>200'

热更新代码

这个也是真的秀。

Java 诊断工具 Arthas-实操案例
拜访 http://localhost:61000/user/0 ,会回来500反常:

shell@Alicloud:~$ curl http://localhost:61000/user/0
{"timestamp":1655436218020,"status":500,"error":"Internal Server Error","exception":"java.lang.IllegalArgumentException","message":"id < 1","path":"/user/0"}

经过热更新代码,修正这个逻辑。

jad反编译UserController

jad --source-only com.example.demo.arthas.user.UserController > /tmp/UserController.java

jad反编译的成果保存在 /tmp/UserController.java文件里了。 再打开一个Terminal 窗口,然后用vim来编辑/tmp/UserController.java:

vim /tmp/UserController.java

比方当 user id 小于1时,也正常回来,不抛出反常:

@GetMapping(value={"/user/{id}"})
public User findUserById(@PathVariable Integer id) {
    logger.info("id: {}", (Object)id);
    if (id != null && id < 1) {
        return new User(id, "name" + id);
        // throw new IllegalArgumentException("id < 1");
    }
    return new User(id.intValue(), "name" + id);
    }

sc查找加载UserController的ClassLoader

[arthas@1266]$ sc -d *UserController | grep classLoaderHash
 classLoaderHash   19469ea2

classLoaderHash 是19469ea2,后边需求运用它。

mc

保存好/tmp/UserController.java之后,运用mc(Memory Compiler)指令来编译,而且经过-c或许–classLoaderClass参数指定ClassLoader:

mc –classLoaderClass org.springframework.boot.loader.LaunchedURLClassLoader /tmp/UserController.java -d /tmp

[arthas@1266]$ mc --classLoaderClass org.springframework.boot.loader.LaunchedURLClassLoader /tmp/UserController.java -d /tmp
Memory compiler output:
/tmp/com/example/demo/arthas/user/UserController.class
Affect(row-cnt:1) cost in 2879 ms.

也能够经过mc -c /tmp/UserController.java -d /tmp,运用-c参数指定ClassLoaderHash:

mc -c 19469ea2 /tmp/UserController.java -d /tmp

redefine

再运用redefine指令从头加载新编译好的UserController.class:

[arthas@1266]$ redefine /tmp/com/example/demo/arthas/user/UserController.class
redefine success, size: 1, classes:
com.example.demo.arthas.user.UserController

热修正代码成果

redefine成功之后,再次拜访 user/0 ,成果正常

shell@Alicloud:~$ curl http://localhost:61000/user/0
{"id":0,"name":"name0"}

动态更新应用Logger Level

查找UserController的ClassLoader

[arthas@1266]$ sc -d *UserController | grep classLoaderHash
 classLoaderHash   19469ea2

用ognl获取logger

ognl –classLoaderClass org.springframework.boot.loader.LaunchedURLClassLoader ‘@com.example.demo.arthas.user.UserController@logger’

[arthas@1266]$ ognl --classLoaderClass org.springframework.boot.loader.LaunchedURLClassLoader '@com.example.demo.arthas.user.UserController@logger'
@Logger[
    serialVersionUID=@Long[5454405123156820674],
    FQCN=@String[ch.qos.logback.classic.Logger],
    name=@String[com.example.demo.arthas.user.UserController],
    level=null,
    effectiveLevelInt=@Integer[20000],
    parent=@Logger[Logger[com.example.demo.arthas.user]],
    childrenList=null,
    aai=null,
    additive=@Boolean[true],
    loggerContext=@LoggerContext[ch.qos.logback.classic.LoggerContext[default]],
]

能够知道UserController@logger实践运用的是logback。能够看到level=null,则阐明实践终究的level是从root logger里来的。

单独设置UserController的logger level

ognl --classLoaderClass org.springframework.boot.loader.LaunchedURLClassLoader '@com.example.demo.arthas.user.UserController@logger.setLevel(@ch.qos.logback.classic.Level@DEBUG)'

再次获取UserController@logger,能够发现已经是DEBUG了。

修正logback的全局logger level

经过获取root logger,能够修正全局的logger level:

ognl --classLoaderClass org.springframework.boot.loader.LaunchedURLClassLoader '@org.slf4j.LoggerFactory@getLogger("root").setLevel(@ch.qos.logback.classic.Level@DEBUG)'

获取Spring Context,在获取 bean,再调用函数

Java 诊断工具 Arthas-实操案例

运用tt指令获取到spring context

tt即 TimeTunnel,它能够记载下指定方法每次调用的入参和回来信息,并能对这些不同的时间下调用进行观测。官方tt阐明:arthas.aliyun.com/doc/tt.html

tt -t org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter invokeHandlerMethod

拜访user/1,

 curl http://localhost:61000/user/1

能够看到tt指令捕获到了一个恳求:

Java 诊断工具 Arthas-实操案例
输入 q 或许 Ctrl + C 退出上面的 tt -t指令。

运用tt指令从调用记载里获取到spring context

tt -i 1000 -w 'target.getApplicationContext()'

获取spring bean,并调用函数

tt -i 1000 -w ‘target.getApplicationContext().getBean(“helloWorldService”).getHelloMessage()’

成果如下:

[arthas@1266]$ tt -i 1000 -w 'target.getApplicationContext().getBean("helloWorldService").getHelloMessage()'
@String[Hello World]
Affect(row-cnt:1) cost in 1 ms.

排查HTTP恳求回来401

恳求接口没有权限的时分一般就回来401 Unauthorized。 401通常是被权限管理的Filter阻拦了,那么到底是哪个Filter处理了这个恳求,回来了401?

盯梢一切的Filter函数

开端trace:

trace javax.servlet.Filter *

能够在调用树的最深层,找到AdminFilterConfig$AdminFilter回来了401

+---[3.806273ms] javax.servlet.FilterChain:doFilter()
|   `---[3.447472ms] com.example.demo.arthas.AdminFilterConfig$AdminFilter:doFilter()
|       `---[0.17259ms] javax.servlet.http.HttpServletResponse:sendError()

经过stack获取调用栈

上面是经过trace指令来获取信息,从成果里,咱们能够知道经过stack盯梢HttpServletResponse:sendError(),相同能够知道是哪个Filter回来了401 履行:

stack javax.servlet.http.HttpServletResponse sendError 'params[0]==401'

拜访能够看到如下仓库信息:

Java 诊断工具 Arthas-实操案例

查找Top N线程

检查一切线程信息

thread

检查详细线程的栈

检查线程ID 2的栈:

thread 2

检查CPU运用率top n线程的栈

thread -n 3

检查5秒内的CPU运用率top n线程栈

thread -n 3 -i 5000

查找线程是否有阻塞

thread -b

更多运用检查: Github地址: github.com/alibaba/art… 文档地址: arthas.aliyun.com/doc/

本文内容到此结束了,

如有收获欢迎点赞保藏关注✔️,您的鼓励是我最大的动力。

如有错误❌疑问欢迎各位大佬指出。

主页:共饮一杯无的博客汇总‍

保持酷爱,奔赴下一场山海

Java 诊断工具 Arthas-实操案例