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 应用,执行如下操作:

  1. 查找全类名

    sc *SpringContextUtil
    
  2. 查找类加载器

    sc -d *SpringContextUtil | grep classLoaderHash
    
  3. 使用ognl表达式获取bean,并调用方法

    > ognl -c 18b4aac2 '@com.soulballad.usage.arthasdemo.util.SpringContextUtil@getBean("userController").getUser(2)'
    

image-20200519154539999


2. watch观测方法调用

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

image-20200519155222425

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

现对其进行热更新

  1. 反编译 UserController

    # --source-only 只输出源码
    jad --source-only com.soulballad.usage.arthasdemo.web.UserController > UserController.java
    
  2. 修改编译后的文件

    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");
        }
    }
    
  3. 重新编译

    # 使用mc重新编译修改后的文件,这里需要使用 -c 指定类加载器
    sc -d com.soulballad.usage.arthasdemo.web.UserController | grep classLoaderHash
    mc -c 18b4aac2 UserController.java
    

    编译完成会出现一个路径,这个路径就是编译后class文件的位置

    image-20200519160730384

  4. 使用redefine重新加载

    # redefine 后面使用上一步的路径,需要将  转成 /
    redefine ../UserController.class
    

    image-20200519160948601

  5. 更新后结果

    image-20200519161238976


4. 更新日志级别

查找类加载器

sc -d *UserController | grep classLoaderHas

查看更新前日志级别

ognl -c 18b4aac2 '@com.soulballad.usage.arthasdemo.web.UserController@LOGGER'

image-20200519161959204

更新日志级别为 DEBUG

ognl -c 18b4aac2 '@com.soulballad.usage.arthasdemo.web.UserController@LOGGER.setLevel(@ch.qos.logback.classic.Level@DEBUG)'

查看更新后日志级别

image-20200519163825883


5. tt获取spring上下文

执行 tt 命令来记录 RequestMappingHandlerAdapter#invokeHandlerMethod 的请求

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

然后访问 user/1,arthas 会记录访问时间片(time fragment)

image-20200519164201326

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

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

image-20200519164620433可以从 applicationContext 中获取 bean,触发方法调用

tt -i 1000 -w 'target.getApplicationContext().getBean("userController").getUser(2)'

image-20200519164751963


6. 链接

原文地址:https://www.cnblogs.com/col-smile/p/12918023.html