Arthas源码解析
Arthas源码解析:
首先是arthas的工程结构:
arthas-agent:javaagent包,需要将此包与被监控程序通过javaagent一起启动,也可以通过虚拟机loadagent来植入增强代码
arthas-boot:arthas启动主程序,arthas推荐的启动方式
arthas-client:telnet连接工程
arthas-client:arthas核心包,里面也有主方法可以启动arthas
arthas-spy:间谍程序,该包由启动类加载器加载,其他工程包由arthas自定义类加载器加载
一、从arthas-core中的Arthas看起:
1.Arthas.java中的主方法是启动方法,会执行到attachAgent(),我们从这个方法看起,这里的configure中有参数为被监控程序的pid
拿到pid后通过VirtualMachine.attach来链接到目标虚拟机
2.连接到虚拟机后判断jdk版本,设置agent包路径,然后通过 virtualMachine.loadAgent(agent包路径, 参数)来远程加载agent,
这里的参数会传到preagent方法中的args中。
二、对目标虚拟机执行loadagent后,目标虚拟机会加载agent程序,通过绝对路径找到arthas-agent包,执行agentmain方法(如果通过-javaagent
参数进行增强,则是执行premain方法),方法最终执行main方法,main方法主要执行了一些初始化工作
1.首先通过传入参数找到agentjar和spyjar,通过Instrumention的appendToBootstrapClassLoaderSearch方法将spyjar加入到被监控程序的
启动类加载器搜索路径中,然后定义自己的类加载器,加载agentjar(这里的agentjar就是arthas-core包)
2.接着初始化间谍程序,通过自定义的classloader找到com.taobao.arthas.core.advisor.AdviceWeaver类,然后获取它的methodOnBegin,
methodOnReturnEnd,methodOnThrowingEnd...等方法,然后将他们植入(赋值)给spy,之后就是新启一个现成来进行绑定操作
3.绑定操作最终执行的是com.taobao.arthas.core.server.ArthasBootstrap的bind方法,首先通过相关的配置,参数对ShellServerImpl进行
初始化,接着设置预置命令BuiltinCommandPack,后面是两种监听方式,一种是telnet,一种是http也就是websocket,最后有一行
//监听
shellServer.listen(new BindHandler(isBindRef));就是注册监听器了。我们来看telnet的TelnetTermServer中有一个listen方法,这里就是监听命令了
4.最后执行UserStatUtil.arthasStart();这只是一个统计,可以不用管
三、接下来就是监听命令以及处理命令了
1.在 shellServer.listen(new BindHandler(isBindRef))中,可以看到初始化了一个TermServerListenHandler,初始化后赋值给TermServer,
方法内部通过启动一个netty telnet端口来进行监听,Helper.loadKeymap()这个方法主要是在项目目录inputrc文件里加载对应的快捷键以及对应的
处理类name标识,返回个映射对象,对命令行界面快捷键指示处理需要。
2.new TermImpl构造方法中,首先静态对象readlineFunctions是所有的Function接口的子类,Helper.loadServices方法里面的 ServiceLoader.load
其实就是根据META-INF下的Servides文件夹下的文件来读取实现类,接着实现DefaultTermStdinHandler,EventHandler以及对应的赋值,结合term
框架可以对相应的快捷键进行处理
3.回到TermServer.listen方法中,有初始化实例TermServerTermHandler ,启动监听后的termhandler.handle方法,就是该实例的hanle方法,
而该handler中的handle方法执行了handleTerm,这里对session进行了初始化,包括欢迎语,然后readline就是等待用户输入了,
4.当用户输入help时, 最中会调用ShellLineHandler.handle方法,这里做了前置的文件检查及解析,help命令顺利到了createjob这一步,一层层的封装,
然后创建实例化CommandProcess,这里要注意的是,找到command对应的processhandler赋值给ProcessImpl的属性了,这里就埋下伏笔,为后面路由
找到HelpCommand具体流程:createJob() -> createProcess() -> getCommand() 创建完job后,对job启动
这里启动后的执行流程为 job.run() -> process.run(foreground) -> ArthasBootstrap.getInstance().execute(task)(这里的task是关键
Runnable task = new CommandProcessTask(process); 最后调用的是CommandProcessTask的run方法) ->handler.handle(process)(这里的handler
是processhandler) -> process() ->instance.process(process)(这里的instance就是具体的命令实现了)
四、详细命令解析
--后续补充