arthas启动-attach流程

上一篇文档写到arthas启动的两种方式, 无论通过执行java -jar arthas-boot.jar 或者执行./as.sh 最终的效果都是会执行java -jar arthas-core.jar的逻辑, 本文我们着重看看启动arthas-core的逻辑实现。 本文的理解需要对java agent技术有一定的了解,有需要的请自行google查询一下。

大家应该都知道,启动java -jar xx.jar 不指定mainclass的时候取的是jar包中的META-INF/MANIFEST.MF文件。我们解压arthas-core.jar可以看到main-class是com.taobao.arthas.core.Arthas, 我们启动arthas-core.jar 是启动com.taobao.arthas.core.Arthas的main函数

该类功能主要做的就是使用agent技术,将arthas-agent attach到对应jvm进程中, 具体实现如下:

Arthas#parse方法

上篇文章我们看到,最后启动的命令行是 java -jar arthas-core.jar -core arthas-core.jar -agent arthas-agent.jar -pid pid,自然而然第一个阶段需要将传入的参数列表解析成Arthas类所需要的,此处的实现比较通用的实现方法,具体实现如下:

private Configure parse(String[] args) {// 处理参数信息Option pid = new TypedOption<Long>().setType(Long.class).setShortName("pid").setRequired(true);Option core = new TypedOption<String>().setType(String.class).setShortName("core").setRequired(true);Option agent = new TypedOption<String>().setType(String.class).setShortName("agent").setRequired(true);Option target = new TypedOption<String>().setType(String.class).setShortName("target-ip");Option telnetPort = new TypedOption<Integer>().setType(Integer.class).setShortName("telnet-port");Option httpPort = new TypedOption<Integer>().setType(Integer.class).setShortName("http-port");Option sessionTimeout = new TypedOption<Integer>().setType(Integer.class).setShortName("session-timeout");Option username = new TypedOption<String>().setType(String.class).setShortName("username");Option password = new TypedOption<String>().setType(String.class).setShortName("password");Option tunnelServer = new TypedOption<String>().setType(String.class).setShortName("tunnel-server");Option agentId = new TypedOption<String>().setType(String.class).setShortName("agent-id");Option appName = new TypedOption<String>().setType(String.class).setShortName(ArthasConstants.APP_NAME);Option statUrl = new TypedOption<String>().setType(String.class).setShortName("stat-url");//构造cli解析器CLI cli = CLIs.create("arthas").addOption(pid).addOption(core).addOption(agent).addOption(target).addOption(telnetPort).addOption(httpPort).addOption(sessionTimeout).addOption(username).addOption(password).addOption(tunnelServer).addOption(agentId).addOption(appName).addOption(statUrl);CommandLine commandLine = cli.parse(Arrays.asList(args));// 组装config对象Configure configure = new Configure();configure.setJavaPid((Long) commandLine.getOptionValue("pid"));configure.setArthasAgent((String) commandLine.getOptionValue("agent"));configure.setArthasCore((String) commandLine.getOptionValue("core"));if (commandLine.getOptionValue("session-timeout") != null) {configure.setSessionTimeout((Integer) commandLine.getOptionValue("session-timeout"));}if (commandLine.getOptionValue("target-ip") != null) {configure.setIp((String) commandLine.getOptionValue("target-ip"));}if (commandLine.getOptionValue("telnet-port") != null) {configure.setTelnetPort((Integer) commandLine.getOptionValue("telnet-port"));}if (commandLine.getOptionValue("http-port") != null) {configure.setHttpPort((Integer) commandLine.getOptionValue("http-port"));}configure.setUsername((String) commandLine.getOptionValue("username"));configure.setPassword((String) commandLine.getOptionValue("password"));configure.setTunnelServer((String) commandLine.getOptionValue("tunnel-server"));configure.setAgentId((String) commandLine.getOptionValue("agent-id"));configure.setStatUrl((String) commandLine.getOptionValue("stat-url"));configure.setAppName((String) commandLine.getOptionValue(ArthasConstants.APP_NAME));return configure;}

整体这个地方的逻辑比较简单,就是将启动的arthas-core的参数列表转换为configure的对象。其实无论arthas-boot的启动或者 arthas-core的启动逻辑中都需要解析参数的,大体实现逻辑一致

Arthas#attachAgent方法

参数解析后,就需要进去attach的关键步骤,这几个步骤是我们平时实现jvm启动后attach的通用步骤。

1、通过传入的进程ID找到对应的找到虚拟的机的描述信息

2、调用VirtualMachineDescriptor#attach方法找到对应的虚拟机信息

3、调用VirtualMachine#loadAgent加载arthas实现的agent

    private void attachAgent(Configure configure) throws Exception {VirtualMachineDescriptor virtualMachineDescriptor = null;for (VirtualMachineDescriptor descriptor : VirtualMachine.list()) {String pid = descriptor.id();if (pid.equals(Long.toString(configure.getJavaPid()))) {virtualMachineDescriptor = descriptor;break;}}VirtualMachine virtualMachine = null;try {if (null == virtualMachineDescriptor) { // 使用 attach(String pid) 这种方式virtualMachine = VirtualMachine.attach("" + configure.getJavaPid());} else {virtualMachine = VirtualMachine.attach(virtualMachineDescriptor);}Properties targetSystemProperties = virtualMachine.getSystemProperties();String targetJavaVersion = JavaVersionUtils.javaVersionStr(targetSystemProperties);String currentJavaVersion = JavaVersionUtils.javaVersionStr();if (targetJavaVersion != null && currentJavaVersion != null) {if (!targetJavaVersion.equals(currentJavaVersion)) {AnsiLog.warn("Current VM java version: {} do not match target VM java version: {}, attach may fail.",currentJavaVersion, targetJavaVersion);AnsiLog.warn("Target VM JAVA_HOME is {}, arthas-boot JAVA_HOME is {}, try to set the same JAVA_HOME.",targetSystemProperties.getProperty("java.home"), System.getProperty("java.home"));}}String arthasAgentPath = configure.getArthasAgent();//convert jar path to unicode stringconfigure.setArthasAgent(encodeArg(arthasAgentPath));configure.setArthasCore(encodeArg(configure.getArthasCore()));virtualMachine.loadAgent(arthasAgentPath,configure.getArthasCore() + ";" + configure.toString());} finally {if (null != virtualMachine) {virtualMachine.detach();}}}

上面加载的agent的目录,对应的arthas-agent.jar的目录,为什么需要传入arthas-agent.jar, 以及arthas-agent.jar 有什么要求。想理解这个就需要去搜索了解java agent技术。 本文描述下agent所需要的配置项,我们看arthas项目的agent子module的pom.xml 可以看到manifest配置项:

1、Premain-Class ,定义jvm启动过程中加载的agent类, 通常可以使用java -javaagent:agentpath=args ..... 调用的方式,在程序启动过程中触发Premain-Class对应的class中的premain方法

2、Agent-Class,   如果jvm已经启动的情况下,可以使用动态attach的agent的方式,配置的class文件中重写agentmain方法,使用VirtualMachine.loadAgent(agent, args)的方式

3、Can-Redefine-Classes, 加载agent后是否可以redefine class, redefine的实现见java.lang.instrument.Instrumentation的定义

4、Can-Retransform-Classes, 加载的agent是否可以Retransform class, retransform的定义见java.lang.instrument.Instrumentation的定义

 <manifestEntries><Premain-Class>com.taobao.arthas.agent334.AgentBootstrap</Premain-Class><Agent-Class>com.taobao.arthas.agent334.AgentBootstrap</Agent-Class><Can-Redefine-Classes>true</Can-Redefine-Classes><Can-Retransform-Classes>true</Can-Retransform-Classes></manifestEntries>

从上面的manifest中结合启动方式可以看到,执行完VirtualMachine#loadAgent之后相当于,对应的进程中会去加载agent,同时调用com.taobao.arthas.agent334.AgentBootstrap#premain函数。

AgentBootstrap#agentmain方法

我们看AgentBootstrap的代码可以看到,无论是agentmain/premain函数实现都是通过调用其中的实现的main函数实现的, 其中的基本流程如下:

1、检测arthas是否已经加载过了, 通过Class.forName("java.arthas.SpyAPI"), 抛出ClassNotFoundException的话代表未初始化过arthas

2、解析参数,从上一步我们看到。传入的参数其实是 arthas-core.jar的路径+";"+configure.toString的信息,此处需要获取到arthas-core.jar的路径

  • 1: 如果arthas-core.jar在args中可以获取到,并且文件存在的话就使用获取的文件
  • 2: 如果参数中不包含arthas-core.jar的信息时,尝试从arthas-agent.jar的目录中查找是否存在arthas-core.jar的文件

3、执行bind函数, bind函数的实现比较简单,就是反射创建com.taobao.arthas.core.server.ArthasBootstrap的对象(构造函数中会进行一些列初始话操作),通过调用其isBind方法判断是否初始初始化成功

 public static void agentmain(String args, Instrumentation inst) {main(args, inst);}private static synchronized void main(String args, final Instrumentation inst) {try {Class.forName("java.arthas.SpyAPI"); // 加载不到会抛异常if (SpyAPI.isInited()) {ps.println("Arthas server already stared, skip attach.");ps.flush();return;}} catch (Throwable e) {}try {ps.println("Arthas server agent start...");// 传递的args参数分两个部分:arthasCoreJar路径和agentArgs, 分别是Agent的JAR包路径和期望传递到服务端的参数if (args == null) {args = "";}args = decodeArg(args);String arthasCoreJar;final String agentArgs;int index = args.indexOf(';');if (index != -1) {arthasCoreJar = args.substring(0, index);agentArgs = args.substring(index);} else {arthasCoreJar = "";agentArgs = args;}File arthasCoreJarFile = new File(arthasCoreJar);if (!arthasCoreJarFile.exists()) {//... 从arthas-agent.jar的目录中查找arthas-code.jar}if (!arthasCoreJarFile.exists()) {return;}final ClassLoader agentLoader = getClassLoader(inst, arthasCoreJarFile);Thread bindingThread = new Thread() {@Overridepublic void run() {try {bind(inst, agentLoader, agentArgs);} catch (Throwable throwable) {throwable.printStackTrace(ps);}}};bindingThread.setName("arthas-binding-thread");bindingThread.start();bindingThread.join();} catch (Throwable t) {t.printStackTrace(ps);try {if (ps != System.err) {ps.close();}} catch (Throwable tt) {}throw new RuntimeException(t);}
}

疑问

在arthas其中的attach流程整体代码相对简单不复杂,不知道大家有没有考虑过Arthas#attachAgent调用过程中获取的VirtualMachineDescriptor, VirtualMachine。 在java中如何可以通过一个jvm进程获取到另外一个jvm进程的信息,在VirtualMachine#loadAgent的时候启动arthas-core.jar的jvm进程应该会和要目标jvm之间进行通信的。 通信是如何实现的?

大家可以先考虑考虑,下章节我们简单分析下其实现,同时明确下为何上节说的两种问题:

1、启动arthas时一定要求启动arthas的用户和jvm启动的用户一致

2、要求/tmp/hsperfdata_{user}目录下的进程描述文件存在, 以及/tmp/.java{pid}文件的相关内容

arthas启动-attach流程相关推荐

  1. arthas启动-attach深入理解

    arthas启动-attach深入理解 上篇文章我们描述了arthas attach的流程,最后遗留了三个问题,attach过程中获取VirtualMachineDescriptor,VirtualM ...

  2. mysql启动关闭步骤_MYSQL的连接管理与启动关闭流程

    1.mysql启动关闭流程 1.启动数据库 1.systemctl start mysqld 2./etc/init.d/mysqld start 3.mysqld_safe --defaults-f ...

  3. Activiti启动某个流程失败,页面报500

    现象:Activiti启动某个流程失败,页面报500,错误日志如下. 2017-06-19 10:50:09 [org.activiti.engine.impl.interceptor.Command ...

  4. 【Arthas】Arthas Command处理流程

    1.概述 转载:Arthas Command处理流程 2.开篇 这篇文章主要是为了分析Arthas的命令的执行过程,整体过程包括任务的创建和任务的执行. arthas的命令都是实现统一的接口,对外通过 ...

  5. 天猫回应“双11数据造假”:已启动司法流程;小米折叠手机专利曝光;ASP.NET感染勒索软件|极客头条...

    整理 | 郭芮 出品 | CSDN(ID:CSDNnews) 快来收听极客头条音频版吧,智能播报由标贝科技提供技术支持. 「极客头条」-- 技术人员的新闻圈! CSDN 的读者朋友们早上好哇,「极客头 ...

  6. Java虚拟机启动整体流程和基础学习(内容很多,不可快餐阅读),推理+源码论证

    不啰嗦,直接看: ISA指令强关联CPU平台,那么汇编语言也一定强关联CPU平台,而C语言又是对汇编最简单的抽象,也就一定会耦合不同CPU平台的代码,这也就是意味着,在写C/C++项目的时候,必须要考 ...

  7. [4G5G基础学习]:流程 - 4G LTE 核心网的Attach流程

    版权声明:本文为CSDN博主「文火冰糖的硅基工坊」的原创文章:[4G&5G专题-78]:流程 - 4G LTE 核心网的Attach流程 , 转载链接 第1章 L3层信令架构 1.1 RAN协 ...

  8. Android 中的WiFi学习笔记(转载)----WIFI启动 代码流程走读---网络连接流程

    Android的WiFi 我们通常看到WiFi的守护进程wpa_supplicant在我们的ps的进程列表中,这个就是我们的wifi守护进程.wpa_supplicant在external/wpa_s ...

  9. 全志 android 编译,全志A20启动代码流程分析 ——Android

    现在的CPU都固化了内部 ROM,内部 ROM中有一般都有一段程序,一般有如下几个功能: 1,初始化,部分外设,如USB,SDCARD 2,初始化DDR(内存)和NandFlash 3,加载boot( ...

最新文章

  1. 加快modelsim仿真速度的方法(原创)
  2. 小米笔记本服务器系统,小米笔记本Pro GTX版
  3. 【机器学习】次梯度(subgradient)方法
  4. ZK Web框架思想
  5. 邮件发送类_10 分钟实现 Spring Boot 发生邮件功能
  6. 新手入坑自动驾驶,从 JetBot 机器人小车开始,能学到什么?
  7. 团队项目(MVP-----------新能源汽车无线充电管理系统)个人任务(5)
  8. FFmpeg 内容介绍 音视频解码和播放
  9. 常用浏览器内核!IE,Chrome ,Firefox,Safari,Opera 等内核
  10. dos攻击的服务器修复,被dos攻击怎么解决
  11. 打印自身源代码的程序
  12. Unity3D播放ogv格式的视频
  13. 讲述 Android 开发代号的故事:Cupcake - Donut - Eclair - Flan - Gingerbread
  14. CNN模型之DenseNet总结
  15. 回溯法之递归回溯和迭代回溯
  16. 基于51单片机篮球计分系统
  17. 对 emwin 窗口创建的认识
  18. HTML界的“苏炳添”——详解Canvas优越性能和实际应用
  19. 操作系统第一章 --导论
  20. 如何设置电脑减少服务器响应时间,win7电脑如何缩短系统响应时间?

热门文章

  1. cas服务器中如何存储用户信息,CAS3.5.2 Server登录后返回用户信息详细解决方案
  2. java导出数据为乱码_传参导出Excel表乱码问题解决方法
  3. 网络安全领域专家(更新中...)
  4. 城域网条件下网络信息安全策略探讨
  5. 美国三大航空公司均取消国内航班改签费
  6. dell 双系统 ubuntu20.04
  7. 银汇通无线pos机简介
  8. 疾病研究:晚上再也不吃沙县...这是不可能滴
  9. 分享一个安卓屏幕翻译软件
  10. 白手起家的他,创立公司,身价达到数千万