Bootstrap

一篇学会Arthas的基本使用及常用指令

  • 下载安装

  • 下载arthas的jar包
  • 将jar包下载后放到指定的文件夹中
  • 启动与结束

  • win+r打开命令行
  • 使用命令运行arthas对应的jar包
    • java -jar D:\application\arthas\arthas-boot.jar
  • 会列出所有的java服务
  • 选择要监控的服务,输入服务列表索引数字后回车,就能正常启动了
  • 如果回车后报端口已经被使用,说明之前arthas服务绑定监控的服务后没有正常结束就退出了,绑定关系依然存在。
  • 启动之后如果需要结束arthas服务则使用命令quit或者stop
    • quit:结束arthas但是不会解除arthas与java服务的绑定
    • stop:结束arthas并解除java服务绑定
  • 启动之后可以直接访问Arthas Console
    • 使用和终端类似
  • 基本命令的使用

  • dashboard 
  • 全局监控
  • 可以概览程序的 线程、内存、GC、运行环境信息。
  • thread
  • 线程监控
  • 使用 thread查看所有线程信息,同时会列出每个线程的 CPU 使用率。
  • 可以用来检查哪些线程占用CPU较高
  • 使用“thread 【线程id】”来查看该线程的信息
  • 使用“thread -n [显示的线程个数]”来查看thread -n [显示的线程个数]
    • 可以根据列出的线程信息判断具体哪个方法占用的cpu较高
  • 使用“thread | grep pool”查看线程池里线程信息。
    • 可以看到所有线程的状态
      • 运行中、阻塞、等待等
  • 使用“thread -b”如果服务中存在死锁,则可以直接定位到死锁的位置
  • jad
  • 可以使用jad将对应的代码进行发编译查看环境中的代码
  • jad 【全限定类名】
  • sc
  • 使用 **sc -d -f ** 命令查看类的字段信息。
  • sm
  • 使用 sm 命令查看类的方法信息。
  • ognl
  • 在Arthas中可以通过ognl表达式在java程序的运行阶段获取java类的静态方法、调用静态方法、new出对象操作成员属性和方法等操作。
  • 这些能力可以用于排查线上一些奇奇怪怪的问题,比如感觉线上某个静态属性值不对,可以通过ognl表达式获取对应的静态属性值查看,又或者在Spring项目中注入对象都是单例的,可以通过getBean(name)的方式获取到具体的单例对象,然后对这个单例对象进行操作,同时也能调用对象中的一些方法,在不提供http接口的情况下实现某些特定的线上测试。
  • 例子
  • 获取静态属性ognl表达式
    • ognl '@com.example.OgnlDemo01@s1'
  • 调用静态方法
    • 无参:ognl '@com.example.OgnlDemo01@printS12()'
    • 有参:ognl '@com.example.OgnlDemo01@setS12("my-s1","my-s2")'
  • 使用ognl表达式操作对象的非静态属性和非静态方法
    • 要操作非静态属性或方法前一定是对象已经被new出来了,我们通过一个入口找到这个被创建的对象从而操作对象的属性和方法,就比如Spring中可以通过上下文对象获取到一个指定的对象,从而进行操作,又或者通过ognl表达式直接创建一个对象,然后对这个对象进行操作,又或者使用静态属性对象操作。
    • 测试代码: 
  • public class OgnlDemo02 {
    
        public static String s1="s1-public-static-v";
    
        public String s2="s2-public-v";
    
        private String s3="s3-private-v";
    
        public static OgnlDemo02 ognlDemo02=new OgnlDemo02();
    
    
    
        public String getS1() {
    
            return OgnlDemo02.s1;
    
        }
    
        public void setS1(String s1) {
    
            OgnlDemo02.s1 = s1;
    
        }
    
        public String getS2() {
    
            return s2;
    
        }
    
        public void setS2(String s2) {
    
            this.s2 = s2;
    
        }
    
        public String getS3() {
    
            return s3;
    
        }
    
        public void setS3(String s3) {
    
            this.s3 = s3;
    
        }
    
    }
    • 获取非静态属性ognl表达式
      • 通过静态属性ognlDemo02对象直接获取公有非静态属性值(这种方法没法获取静态属性)
      • 通过静态属性ognlDemo02对象直接获取私有非静态属性值(这种方法没法获取静态属性)
      • 通过静态属性ognlDemo02对象的成员方法获取对应属性值
    • 调用非静态方法ognl表达式
  • 使用ognl表达式创建一个对象,并对这个对象进行操作
    • 测试代码
  • public class User {
    
        private Long uid;
    
        private String nickName;
    
       
    
        public User(Long uid, String nickName) {
    
            this.uid = uid;
    
            this.nickName = nickName;
    
        }
    
        public Long getUid() {
    
            return uid;
    
        }
    
        public void setUid(Long uid) {
    
            this.uid = uid;
    
        }
    
        public String getNickName() {
    
            return nickName;
    
        }
    
        public void setNickName(String nickName) {
    
            this.nickName = nickName;
    
        }
    
    }
    • ognl表达式
      • 创建一个对象
  • # 创建一个User对象什么也不做
    
    [arthas@27880]$ ognl 'new com.kerwin.arthas.demo.User(100,"kerwin")'                                             
    
    @User[                                                                                                                       
    
    uid=@Long[100],                                                                                                          
    
    nickName=@String[kerwin],                                                                                            
    
    ]
    
    # 创建一个User对象赋值给user,后续可以在别的地方对创建出来的对象进行操作
    
    [arthas@27880]$ ognl '#user = new com.kerwin.arthas.demo.User(100,"kerwin")'                                             
    
    @User[                                                                                                                       
    
    uid=@Long[100],                                                                                                          
    
    nickName=@String[kerwin],                                                                                            
    
    ]
      • 调用创建出来的对象中的成员方法
  • [arthas@27880]$ ognl 'new com.kerwin.arthas.demo.User(10001,"kerwin").getNickName()'                                     
    
    @String[kerwin]                                                                                          
    
    ]
    
    [arthas@27880]$ ognl '#user = new com.kerwin.arthas.demo.User(10001,"kerwin").getNickName()'                                     
    
    @String[kerwin]                                                                                          
    
    ]
    
    [arthas@27880]$ ognl '#user = new com.kerwin.arthas.demo.User(10001,"kerwin"),#user.getNickName()'                                     
    
    @String[kerwin]                                                                                          
    
    ]
    • 方法复杂入参ognl表达式
      • 测试代码
  • public class OgnlDemo03 {
    
        public static OgnlDemo03 ognlDemo03 = new OgnlDemo03();
    
        private User user;
    
        private static User staticUser;
    
        private static List<String> lists;
    
        private static Map<String,String> maps;
    
    
    
        public User setUser(User user){
    
            this.user = user;
    
            return user;
    
        }
    
        public static User setStaticUser(User user){
    
            OgnlDemo03.staticUser = user;
    
            return user;
    
        }
    
        public static User getMyUser(){
    
           return new User(10002L,"kerwin2");
    
        }
    
        public static User changeUser(User user){
    
            return new User(user.getUid(),user.getNickName()+"---changeUser");
    
        }
    
        public static List<String> setLists(List<String> lists){
    
            OgnlDemo03.lists = lists;
    
            return lists;
    
        }
    
        public static Map<String,String> setMaps(Map<String,String> maps){
    
            OgnlDemo03.maps = maps;
    
            return maps;
    
        }
    
    }
      • 自定义对象入参
        • 创建一个User对象将这个对象作为参数传入静态方法中
  • [arthas@24600]$ ognl '#user = new com.kerwin.arthas.demo.User(10001,"kerwin"),@com.kerwin.arthas.demo.OgnlDemo03@setStaticUser(#user)'                                                                                                             
    
    @User[                                                                                                                       
    
    uid=@Long[10001],                                                                                                        
    
    nickName=@String[kerwin],                                                                                            
    
    ]
        • 创建一个User对象,将这个对象作为参数传入静态属性ognlDemo03对象setUser(user)方法中
  • [arthas@24600]$ ognl '#user = new com.kerwin.arthas.demo.User(10001,"kerwin"),@[email protected](#user)'                                                                                                        
    
    @User[                                                                                                                       
    
    uid=@Long[10001],                                                                                                        
    
    nickName=@String[kerwin],                                                                                            
    
    ]
        • 创建一个User对象,在创建一个OgnlDemo03对象,将创建的User对象传入OgnlDemo03对象的setUser(user)方法
  • [arthas@24600]$ ognl '#user = new com.kerwin.arthas.demo.User(10001,"kerwin"),new com.kerwin.arthas.demo.OgnlDemo03().setUser(#user)'                                                                                                              
    
    @User[                                                                                                                       
    
    uid=@Long[10001],                                                                                                        
    
    nickName=@String[kerwin],                                                                                            
    
    ]
        • 调用getMyUser()方法获取User对象作为changeUser(user)的入参
  • # {#user1,#user2} 代表将user1、user2这两个对象作为数组输出在控制台,因为这里没有加-x 2默认展开层级为1所以输出的是对象内存地址
    
    [arthas@18904]$ ognl '#user1 = @com.kerwin.arthas.demo.OgnlDemo03@getMyUser(),#user2 = @com.kerwin.arthas.demo.OgnlDemo03@changeUser(#user1),{#user1,#user2}'                                                                                      
    
    @ArrayList[                                                                                                                  
    
    @User[com.kerwin.arthas.demo.User@30833e75],                                                                             
    
    @User[com.kerwin.arthas.demo.User@70ca5419],                                                                         
    
    ]
    
    # 加上-x 2 可以展开数组内部对象                                                                                         
    
    [arthas@18904]$ ognl '#user1 = @com.kerwin.arthas.demo.OgnlDemo03@getMyUser(),#user2 = @com.kerwin.arthas.demo.OgnlDemo03@changeUser(#user1),{#user1,#user2}' -x 2                                                                                 
    
    @ArrayList[                                                                                                                  
    
    @User[                                                                                                                       
    
    uid=@Long[10002],                                                                                                        
    
    nickName=@String[kerwin2],                                                                                           
    
    ],                                                                                                                       
    
    @User[                                                                                                                       
    
    uid=@Long[10002],                                                                                                        
    
    nickName=@String[kerwin2---changeUser],                                                                              
    
    ],                                                                                                                   
    
    ]
      • 数组入参
  • [arthas@18904]$ ognl '@com.kerwin.arthas.demo.OgnlDemo03@setLists({"k1","k2","k3"})'                                     
    
    @ArrayList[                                                                                                                  
    
    @String[k1],                                                                                                             
    
    @String[k2],                                                                                                             
    
    @String[k3],                                                                                                         
    
    ]
      • Map入参
  • [arthas@18904]$ ognl '#map = #{"id":10003L,"nickName":"k3"},@com.kerwin.arthas.demo.OgnlDemo03@setMaps(#map)'            
    
    @LinkedHashMap[                                                                                                              
    
    @String[id]:@Long[10003],                                                                                                
    
    @String[nickName]:@String[k3],                                                                                       
    
    ]
  • 实践操作:使用ognl表达式获取Spring上下文中对象,并进行操作
    • 测试代码
  • @Service
    
    public class OgnlDemoService {
    
        private String description;
    
    
    
        public String getDescription() {
    
            return description;
    
        }
    
        public void setDescription(String description) {
    
            this.description = description;
    
        }
    
    }
    • 自定义一个获取Spring上下文中对象的工具类
  • @Component
    
    public class SpringApplicationContext implements ApplicationContextAware {
    
    
    
        private static ApplicationContext applicationContext;
    
    
    
        @Override
    
        public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
    
            SpringApplicationContext.applicationContext = applicationContext;
    
        }
    
    
    
        /**
    
         * 通过class获取Bean
    
         */
    
        public static <T> T getBean(Class<T> clazz) {
    
            return applicationContext.getBean(clazz);
    
        }
    
        /**
    
         * 通过name获取 Bean.
    
         */
    
        public static Object getBean(String name) {
    
            return applicationContext.getBean(name);
    
        }
    
    }
    • 获取Spring上下文中的OgnlDemoService对象
  • # 通过beanName获取
    
    [arthas@30000]$ ognl '@com.example.test_mybatis.controller.SpringApplicationContext@getBean("ognlDemoService")'                      
    
    @OgnlDemoService[                                                                                                            
    
    description=null,                                                                                                    
    
    ]
    
    # 通过class获取                                                                                                                  
    
    [arthas@30000]$ ognl '#OgnlDemoServiceClass [email protected]_mybatis.service.OgnlDemoService@class,@com.kerwin.arthas.utils.Sp
    
    ringApplicationContext@getBean(#OgnlDemoServiceClass)'                                                                   
    
    @OgnlDemoService[                                                                                                            
    
    description=null,                                                                                                    
    
    ]
    • 操作OgnlDemoService对象中变量和方法
  • [arthas@30000]$ ognl '@com.example.test_mybatis.controller.SpringApplicationContext@getBean("ognlDemoService").description'          
    
    null                                                                                                                     
    
    [arthas@30000]$ ognl '@com.example.test_mybatis.controller.SpringApplicationContext@getBean("ognlDemoService").setDescription("HelloW orld")'                                                                                                                  
    
    null                                                                                                                     
    
    [arthas@30000]$ ognl '@com.example.test_mybatis.controller.SpringApplicationContext@getBean("ognlDemoService").getDescription()'     
    
    @String[Hello World]
  • trace
  • 使用 trace 命令可以跟踪统计方法耗时,经常用于排查运行较慢、耗时较长的场景
  • 测试代码
  • package com.example.test_mybatis.controller;
    
    
    
    import com.example.test_mybatis.service.UserServiceImpl;
    
    import lombok.extern.slf4j.Slf4j;
    
    import org.springframework.beans.factory.annotation.Autowired;
    
    import org.springframework.web.bind.annotation.GetMapping;
    
    import org.springframework.web.bind.annotation.RestController;
    
    
    
    import java.util.HashMap;
    
    
    
    @RestController
    
    @Slf4j
    
    public class UserController {
    
    
    
        @Autowired
    
        private UserServiceImpl userService;
    
    
    
        @GetMapping(value = "/user")
    
        public HashMap<String, Object> getUser(Integer uid) throws Exception {
    
            // 模拟用户查询
    
            userService.get(uid);
    
            HashMap<String, Object> hashMap = new HashMap<>();
    
            hashMap.put("uid", uid);
    
            hashMap.put("name", "name" + uid);
    
            return hashMap;
    
        }
    
    }
    
    
    package com.example.test_mybatis.service;
    
    
    
    import lombok.extern.slf4j.Slf4j;
    
    import org.springframework.stereotype.Service;
    
    
    
    @Service
    
    @Slf4j
    
    public class UserServiceImpl {
    
    
    
        public void get(Integer uid) throws Exception {
    
            check(uid);
    
            service(uid);
    
            redis(uid);
    
            mysql(uid);
    
        }
    
    
    
        public void service(Integer uid) throws Exception {
    
            int count = 0;
    
            for (int i = 0; i < 10; i++) {
    
                count += i;
    
            }
    
            log.info("service  end {}", count);
    
        }
    
    
    
        public void redis(Integer uid) throws Exception {
    
            int count = 0;
    
            for (int i = 0; i < 10000; i++) {
    
                count += i;
    
            }
    
            log.info("redis  end {}", count);
    
        }
    
    
    
        public void mysql(Integer uid) throws Exception {
    
            long count = 0;
    
            for (int i = 0; i < 10000000; i++) {
    
                count += i;
    
            }
    
            log.info("mysql end {}", count);
    
        }
    
    
    
        public boolean check(Integer uid) throws Exception {
    
             if (uid == null || uid < 0) {
    
                 log.error("uid不正确,uid:{}", uid);
    
                 throw new Exception("uid不正确");
    
             }
    
             return true;
    
         }
    
    }
  • 在arthas中使用命令 trace com.example.test_mybatis.controller.UserController getUser
    • 可以开启对这个方法的监控,当请求“/get”之后arthas会打印方法中的耗时情况。
    • 但是一般只有业务方法才会监控,可以增加--skipJDKMethod false后就可以监控jdk中的函数了(将属性放在trace后面)

  • 继续跟踪耗时高的方法,然后再次访问。
  • 很清楚的看到是 com.UserServiceImpl的 mysql方法耗时是最高的。
  • monitor
  • 使用 monitor 命令监控统计方法的执行情况。
    • 每5秒统计一次 com.UserServiceImpl 类的 get 方法执行情况。
    • monitor -c 5 com.UserServiceImpl get
  • watch 
  • 观察方法信息
  • 常用方法
  • # 查看入参和出参
    
    $ watch com.Arthas addHashSet '{params[0],returnObj}'
    
    # 查看入参和出参大小
    
    $ watch com.Arthas addHashSet '{params[0],returnObj.size}'
    
    # 查看入参和出参中是否包含 'count10'
    
    $ watch com.Arthas addHashSet '{params[0],returnObj.contains("count10")}'
    
    # 查看入参和出参,出参 toString
    
    $ watch com.Arthas addHashSet '{params[0],returnObj.toString()}'
  • 可以对传入的参数进行操作
  • stack
  • 使用stack查看方法的调用情况
    • stack com.UserServiceImpl mysql
  • tt 
  • tt 命令方法执行数据的时空隧道,记录下指定方法每次调用的入参和返回信息,并能对这些不同的时间下调用进行观测 。
  • 使用 tt 命令记录方法执行的详细情况
    • 是否异常、是否有返回、消耗的时间
  • 开始记录方法调用信息:tt -t com.UserServiceImpl check
  • 查看记录的方法调用信息: tt -l
  • 查看调用记录的详细信息(-i 指定 INDEX): tt -i 1001
  • 重新发起调用,使用指定记录,使用 -p 重新调用。
    • tt -i 1001 -p
;