Arthas应用场景
约 798 字大约 3 分钟
Arthas应用场景
1. ognl获取bean
SpringContextUtil,通常代码中会有类似这样的工具类用来获取 bean 实例
@Component
public class SpringContextUtil implements ApplicationContextAware {
private static ApplicationContext applicationContext;
@Override
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
SpringContextUtil.applicationContext = applicationContext;
}
public static Object getBean(String beanName) {
return applicationContext.getBean(beanName);
}
public static <T> T getBean(Class<T> clazz) {
return applicationContext.getBean(clazz);
}
}
UserController
@RestController
public class UserController {
private static final Logger LOGGER = LoggerFactory.getLogger(UserController.class);
@GetMapping("/user/{id}")
public User getUser(@PathVariable Integer id) {
if (null == id) {
throw new IllegalArgumentException("id can not be null");
}
if (id < 1) {
throw new IllegalArgumentException("id must be greater than 1");
}
return new User(id, "zhangsan");
}
}
使用 arthas 连接 spring 应用,执行如下操作:
查找全类名
sc *SpringContextUtil查找类加载器
sc -d *SpringContextUtil | grep classLoaderHash使用ognl表达式获取bean,并调用方法
> ognl -c 18b4aac2 '@com.soulballad.usage.arthasdemo.util.SpringContextUtil@getBean("userController").getUser(2)'

2. watch观测方法调用
# 查看 UserController 下所有方法的 参数、对象、返回值
watch com.soulballad.usage.arthasdemo.web.UserController * '{params,target,returnObj}'

watch 支持方法调用前、调用后、异常抛出等多个场景观测,同时还可以在第四个参数中使用条件进行过滤,比如:
watch com.soulballad.usage.arthasdemo.web.UserController * '{returnObj}' 'params[0]>10'
watch com.soulballad.usage.arthasdemo.web.UserController * '{returnObj}' '#cost>10'
3. 热更新
步骤:使用jad反编译 -> 修改文件 -> 使用mc重新编译修改后的文件->使用redefine加载重新编译后的类
上述 UserController 访问 user/0,会出现如下错误:
There was an unexpected error (type=Internal Server Error, status=500).
id must be greater than 1
现对其进行热更新
反编译 UserController
# --source-only 只输出源码 jad --source-only com.soulballad.usage.arthasdemo.web.UserController > UserController.java修改编译后的文件
package com.soulballad.usage.arthasdemo.web; import com.soulballad.usage.arthasdemo.model.User; import com.soulballad.usage.arthasdemo.util.SpringContextUtil; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.PathVariable; import org.springframework.web.bind.annotation.RestController; @RestController public class UserController { private static final Logger LOGGER = LoggerFactory.getLogger(UserController.class); @GetMapping(value={"/user/{id}"}) public User getUser(@PathVariable Integer id) { if (null == id) { throw new IllegalArgumentException("id can not be null"); } if (id < 1) { // throw new IllegalArgumentException("id must be greater than 1"); return new User(id, "lisi"+id); } return new User(id, "zhangsan"); } }重新编译
# 使用mc重新编译修改后的文件,这里需要使用 -c 指定类加载器 sc -d com.soulballad.usage.arthasdemo.web.UserController | grep classLoaderHash mc -c 18b4aac2 UserController.java编译完成会出现一个路径,这个路径就是编译后class文件的位置
使用redefine重新加载
# redefine 后面使用上一步的路径,需要将 \ 转成 / redefine ../UserController.class
更新后结果
4. 更新日志级别
查找类加载器
sc -d *UserController | grep classLoaderHash
查看更新前日志级别
ognl -c 18b4aac2 '@com.soulballad.usage.arthasdemo.web.UserController@LOGGER'

更新日志级别为 DEBUG
ognl -c 18b4aac2 '@com.soulballad.usage.arthasdemo.web.UserController@LOGGER.setLevel(@ch.qos.logback.classic.Level@DEBUG)'
查看更新后日志级别

5. tt获取spring上下文
执行 tt 命令来记录 RequestMappingHandlerAdapter#invokeHandlerMethod 的请求
tt -t org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter invokeHandlerMethod
然后访问 user/1,arthas 会记录访问时间片(time fragment)

可以用 tt 命令的 -i 参数来指定index,并且用 -w 参数来执行ognl表达式来获取spring context:
tt -i 1000 -w 'target.getApplicationContext()'

可以从 applicationContext 中获取 bean,触发方法调用
tt -i 1000 -w 'target.getApplicationContext().getBean("userController").getUser(2)'

6. 链接
- Arthas: https://github.com/alibaba/arthas
- https://alibaba.github.io/arthas/tt.html
- https://alibaba.github.io/arthas/ognl.html
- https://alibaba.github.io/arthas/redefine.html
- https://alibaba.github.io/arthas/watch.html


