• 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. 链接

  • 相关阅读:
    JDK自带keytool工具配置HTTPS加密协议
    利用 GOST 搭建加密中转隧道(UDP+TCP)
    Linux登录报错-bash: /etc/profile: Permission denied
    转:Windows server 2008 R2 更新补丁失败进入恢复模式
    转:Windows server 2008R2更新补丁后进入系统恢复
    转:CENTOS创建IP白名单
    centos6 离线升级openssh防止断开
    关闭oracle一直等待
    Java并发容器
    Java对象序列化
  • 原文地址:https://www.cnblogs.com/col-smile/p/12918023.html
Copyright © 2020-2023  润新知