arthas源码学习

版权声明:本文为博主原创文章,转载请注明出处。 https://blog.csdn.net/u010597819/article/details/88554000

启动arthas-bootstrap

  1. 启动,如果没有参数启动则自动列举当前系统的java进程列表,等待用户选择
  2. 选择要attach的进程,并进行attach
  3. 拼接启动arthas-core的命令行,包含agent参数指定为arthas-agent.jar
  4. 启动arthas-core.jar
  5. 如果仅仅是attach则退出
  6. 如果不是仅attach,则启动arthas-client,入口:com.taobao.arthas.client.TelnetConsole.Main

启动arthas-core

// "${JAVA_HOME}"/bin/java \
// ${opts} \
// -jar "${arthas_lib_dir}/arthas-core.jar" \
// -pid ${TARGET_PID} \
// -target-ip ${TARGET_IP} \
// -telnet-port ${TELNET_PORT} \
// -http-port ${HTTP_PORT} \
// -core "${arthas_lib_dir}/arthas-core.jar" \
// -agent "${arthas_lib_dir}/arthas-agent.jar"
例如:
java -jar ${HOME}/.arthas/lib/3.1.0/arthas/arthas-core.jar
 -pid 7320 
 -target-ip 127.0.0.1 
 -telnet-port 3658 
 -http-port 8563 
 -core "${HOME}/.arthas/lib/3.1.0/arthas/arthas-core.jar" 
 -agent "${HOME}/.arthas/lib/3.1.0/arthas/arthas-agent.jar"
  1. core入口Arthas.main,创建Arthas
  2. attach代理agent,attachAgent
  3. 遍历当前系统的虚拟机列表VirtualMachine.list,获取参数中指定要attach的pid进程
  4. attach目标虚拟机:VirtualMachine.attach
  5. 判断两个jvm的jdk版本,如果不一致打印warn日志
  6. 为指定目标jvm进程加载agent代理
virtualMachine.loadAgent(configure.getArthasAgent(),
		configure.getArthasCore() + ";" + configure.toString());
C:\Users\Gallant\.arthas\lib\3.1.0\arthas\arthas-agent.jar=C:\Users\Gallant\.arthas\lib\3.1.0\arthas\arthas-core.jar;;telnetPort=3658;httpPort=8563;ip=127.0.0.1;arthasAgent=C:\\Users\\Gallant\\.arthas\\lib\\3.1.0\\arthas\\arthas-agent.jar;sessionTimeout=1800;arthasCore=C:\\Users\\Gallant\\.arthas\\lib\\3.1.0\\arthas\\arthas-core.jar;javaPid=7320;

启动arthas-agent

  1. 在为目标进程attach代理后,jvm回调指定的agent-class类的agentmain方法(同样启动时回调的是premain-class类的premain方法),回调AgentBootstrap的main方法
  2. 加载spy的jar包至BootstrapClassLoader并获取agent jar的classloader
  3. 初始化spy间谍
  4. 加载AdviceWeaver类
  5. 获取AdviceWeaver类的methodOnBegin、methodOnReturnEnd、methodOnThrowingEnd、methodOnInvokeBeforeTracing、methodOnInvokeAfterTracing、methodOnInvokeThrowTracing、resetArthasClassLoader方法
  6. 与Spy绑定
  7. 绑定bind
  8. 将args参数反序列化为Configure对象
  9. 获取要监控的进程pid(javaPid)
  10. 获取ArthasBootstrap实例调用判断是否已经绑定,没有则绑定
  11. ArthasBootstrap绑定bind

arthas-core绑定

  1. ArthasBootstrap绑定bind
  2. 创建shellserver(ShellServerImpl)并绑定端口
  3. 创建内建命令包BuiltinCommandPack
  4. 将内建命令包与shellserver绑定,作为resolver注册至服务
  5. 根据配置的ip及端口注册服务:TelnetTermServer、HttpTermServer
  6. shellServer启动监听:listen
  7. 遍历注册的服务注册handler(termHandler=TermServerTermHandler)并启动监听即:TelnetTermServer、HttpTermServer
  8. 假定为telnet服务模式
  9. 根据ip端口创建netty服务NettyTelnetTtyBootstrap,启动入参为factory:提供accept实现为termHandler.accept,accept入参为new TermImpl
bootstrap.start(new Consumer<TtyConnection>() {
    @Override
    public void accept(final TtyConnection conn) {
        termHandler.handle(new TermImpl(Helper.loadKeymap(), conn));
    }
}).get(connectionTimeout, TimeUnit.MILLISECONDS);
  1. 启动服务start委派telnet:NettyTelnetBootstrap.start启动
  2. 创建事件handler:TelnetChannelHandler
  3. 绑定ip端口并添加监听
  4. 事件处理handler:TelnetChannelHandler,factory工厂get属性为:TelnetTtyConnection
  5. TelnetChannelHandler读取数据channelRead,调用当前连接实例读取数据:NettyTelnetConnection.receive
  6. 委派状态handle数据,当前为DATA状态,委派当前session(即当前Connection实例)读取数据append,超过缓存就flush数据,绑定的handle读取数据onData
  7. 当前绑定的handler为factory工厂get获取(TelnetTtyConnection),TelnetTtyConnection.onData
  8. decoder写入数据(BinaryDecoder.write),将数据读取至本地ByteBuffer并回调onChar.accept(ReadBuffer.accept)
  9. ReadBuffer调用readHandler读取数据(TtyEventDecoder.accept),readerHandler在Connection连接open打开时设置为eventDecoder(TtyEventDecoder)
  10. TtyEventDecoder接收数据accept,eventHandler、readHandler读取数据,eventHandler、readHandler在TermImpl构造方法内设置为echoHandler:DefaultTermStdinHandler,EventHandler
  11. EventHandler读取指令根据指定调用termImpl实现的方法
  12. 读取标准输入数据DefaultTermStdinHandler.accept,调用term打印到标准输出,并将指令放入ReadLine实例绑定的队列
  13. 将指令放入事件队列:EventQueue.append
  14. 客户端指令读取完成
  15. TelnetTtyConnection连接打开时会check校验Accept回调handler.accept,也就是匿名实现的Consumer.accept
  16. term句柄(TermServerTermHandler)handle客户端实现termImpl,即直接委派shellServer handle处理termImpl
  17. 根据Term创建session(ShellImpl)
  18. 为session设置欢迎页面
  19. 设置session的关闭handle
  20. 初始化session
  21. session读取命令行readline,委派term执行,term委派ReadLine实例执行
  22. 从队列EventQueue中读取指令并将指令封装为KeyEvent对象
  23. 交互实例Interaction处理事件handle
  24. 根据KeyEvent事件获取对应的方法处理,例如完成处理Complete
  25. 即调用CompletionHandler处理数据
  26. CompletionHandler处理数据accept
  27. 读取命令行
  28. 解析命令行为CliToken列表
  29. CommandManagerCompletionHandler处理CompletionAdaptor
  30. 委派commandManager处理complete
  31. 处理命令completeSingleCommand、completeCommands
  32. Completion处理命令,即CompletionAdaptor.complete,适配器委派Completion处理complete
  33. 将指标输出至标准输出
  34. Accept处理
  35. RequestHandler处理接收请求,委派给lineHandler处理:ShellLineHandler.handle
  36. 根据命令行创建Job任务并执行
  37. ShellImpl根据命令行创建Job
  38. JobController创建Job
  39. 创建Process并绑定对应的Command,commandManager根据命令行创建对应的Command
  40. JobImpl绑定Process
  41. 执行Job,为Process设置term与session并执行
  42. 将process封装为CommandProcessTask提交至线程池执行,委派CommandProcessImpl handle process
  43. 即Command的processHandler处理:new ProcessImpl(command, remaining, command.processHandler(), ProcessOutput);
  44. 例如:MonitorCommand,封装为AnnotatedCommandImpl,processHandle为:ProcessHandler
  45. ProcessHandler处理命令CommandProcess,委派AnnotatedCommandImpl处理process
  46. 根据命令class创建实例并处理CommandProcess,如:MonitorCommand.process
  47. MonitorCommand命令委派父类处理enhance,即对相应的class进行enhance增强,即将对应的监听织入(例如:MonitorAdviceListener),Enhancer.enhance
  48. NettyTelnetTtyBootstrap启动完成后TermServerListenHandler来handle启动事件,如果启动失败,关闭server
  49. 如果server启动成功,则为shellServer启动定时任务监听session是否关闭;回调监听handler返回成功状态

arthas-core之enhance增强

  1. 创建Enhance实例
  2. 将enhance添加至Instrumentation
  3. Instrumentation修改类定义retransform
  4. 回调自定义的Enhancer.transform进行增强
  5. 调用ASM API对指定类增强处理,自定义类修改实现AdviceWeaver
  6. ASM回调visitMethod方法对指定方法增强,向方法中织入字节码:通过Method.invoke方法回调增强动作:ON_BEFORE_METHOD、ON_RETURN_METHOD、ON_THROWS_METHOD、BEFORE_INVOKING_METHOD、AFTER_INVOKING_METHOD、THROW_INVOKING_METHOD

猜你喜欢

转载自blog.csdn.net/u010597819/article/details/88554000