Arthas原理系列(三):服务端启动流程

本文深入探讨Arthas服务端启动流程,包括如何监听和处理命令行输入,以及命令执行的机制。文章从arthas入口方法出发,详细解析了命令解析、服务端监听和命令执行的关键步骤,揭示了Arthas如何构建服务器环境并执行用户命令。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

历史文章:

  1. OGNL语法规范
  2. 消失的堆栈
  3. Arthas原理系列(一):利用JVM的attach机制实现一个极简的watch命令
  4. Arthas原理系列(二):总体架构和项目入口
  5. Arthas原理系列(三):服务端启动流程
  6. Arthas原理系列(四):字节码插装让一切变得有可能

前言

本篇文章主要讲我们在终端中敲入的命令是如何被 arthas 服务器识别并且解释的。要注意这个过程是 arthas 对所有命令执行过程的抽闲个,对于具体命令的执行过程我会在后面的系列文章中再说。

arthas 服务端的启动

在上一篇文章中,我们跟踪了整个 arthas 工程的入口方法:com.taobao.arthas.agent334.AgentBootstrap#main,在这个方法中,最重要的一个步骤就是启动过了一个绑定线程

private static synchronized void main(String args, final Instrumentation inst) {
   
   
    try {
   
   
        // 1. 程序运行前的校验,
        // arthas如果已经存在,则直接返回
        // 入参中必须要包含arthas core等
        // 这些代码细节不会影响我们对主流程的理解,因此暂时删除
        final ClassLoader agentLoader = getClassLoader(inst, arthasCoreJarFile);
        Thread bindingThread = new Thread() {
   
   
            @Override
            public 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) {
   
   
            // ignore
        }
        throw new RuntimeException(t);
    }

bind这个线程的运行时会调用com.taobao.arthas.agent334.AgentBootstrap#bind,这个方法的详细代码如下:

private static void bind(Instrumentation inst, ClassLoader agentLoader, String args) throws Throwable {
   
   
        /**
         * <pre>
         * ArthasBootstrap bootstrap = ArthasBootstrap.getInstance(inst);
         * </pre>
         */
        Class<?> bootstrapClass = agentLoader.loadClass(ARTHAS_BOOTSTRAP);
        Object bootstrap = bootstrapClass.getMethod(GET_INSTANCE, Instrumentation.class, String.class).invoke(null, inst, args);
        boolean isBind = (Boolean) bootstrapClass.getMethod(IS_BIND).invoke(bootstrap);
        if (!isBind) {
   
   
            String errorMsg = "Arthas server port binding failed! Please check $HOME/logs/arthas/arthas.log for more details.";
            ps.println(errorMsg);
            throw new RuntimeException(errorMsg);
        }
        ps.println("Arthas server already bind.");
    }

这段方法用反射的方法调用了com.taobao.arthas.core.server.ArthasBootstrap的静态方法getInstance,并且把从main方法中解析到参数再传到这个getInstance中。

getInstance从这个名字看就是返回一个ArthasBootstrap的实例,事实上代码的逻辑也是这样的,其中最关键的就是ArthasBootstrap的构造函数函数:

private ArthasBootstrap(Instrumentation instrumentation, Map<String, String> args) throws Throwable {
   
   
        this.instrumentation = instrumentation;

        String outputPath = System.getProperty("arthas.output.dir", "arthas-output");
        arthasOutputDir = new File(outputPath);
        arthasOutputDir.mkdirs();

        // 1. initSpy()
        // 加载SpyAPI这个类
        initSpy(instrumentation);
        // 2. ArthasEnvironment
        // 初始化arthas运行的环境变量
        initArthasEnvironment(args);
        // 3. init logger
        loggerContext = LogUtil.initLooger(arthasEnvironment);

        // 4. init beans
        // 初始化结果渲染和历史命令管理的相关类
        initBeans();

        // 5. start agent server
        // 启动server,开始监听
        bind(configure);

        // 注册一些钩子函数
        executorService = Executors.newScheduledThreadPool(1, new ThreadFactory() {
   
   
            @Override
            public Thread newThread(Runnable r) {
   
   
                final Thread t = new Thread(r, "arthas-command-execute");
                t.setDaemon(true);
                return t;
            }
        });

        shutdown = new Thread("as-shutdown-hooker") {
   
   

            @Override
            public void run() {
   
   
                ArthasBootstrap.this.destroy();
            }
        };

        transformerManager = new TransformerManager(instrumentation);
        Runtime.getRuntime().addShutdownHook(shutdown);
    }

在这个构造函数中,最重要的就是com.taobao.arthas.core.server.ArthasBootstrap#bind这个方法

private void bind(Configure configure) throws Throwable {
   
   

    // 无关紧要的一些前置操作,先删除掉

    try {
   
   
        // 关于arthas tunnel server,请参考:
        // https://arthas.aliyun.com/doc/tunnel.html
        if (configure.getTunnelServer() != null) {
   
   
            tunnelClient = new TunnelClient();
            tunnelClient.setAppName(configure.getAppName());
            tunnelClient.setId(configure.getAgentId());
            tunnelClient.setTunnelServerUrl(configure.getTunnelServer());
            tunnelClient.setVersion(ArthasBanner.version());
            ChannelFuture channelFuture = tunnelClient.start();
            channelFuture.await(10, TimeUnit.SECONDS);
        }
    } catch (Throwable t) {
   
   
        logger().error("start tunnel client error", t);
    }

    try {
   
   
        // 将一些非常关键的参数包装成ShellServerOptions对象
        ShellServerOptions options = new ShellServerOptions()
                        .setInstrumentation(instrumentation)
                        .setPid(PidUtils.currentLongPid())
                        .setWelcomeMessage(ArthasBanner.welcome());
        
### 关于 Arthas启动方法 Arthas 是一款强大的 Java 诊断工具,其启动过程涉及多个方面,包括服务端初始化、配置加载以及类增强机制。以下是有关 Arthas 启动的核心信息: #### 1. **Arthas 启动方式** Arthas 支持多种启动方式,常见的有以下几种: - 使用 `attach` 方式连接目标 JVM 实例。 ```bash java -jar arthas-boot.jar ``` 此命令会自动检测本地可用的 JVM 列表并提供交互界面让用户选择要附加的目标进程[^2]。 - 如果已知目标 PID,则可以直接通过如下命令快速启动: ```bash java -jar arthas-boot.jar [PID] ``` #### 2. **Arthas 配置加载顺序** 在 Arthas 初始化过程中,它会按照一定的优先级从不同来源读取配置参数。这些来源及其优先级依次为: - 命令行参数 - 环境变量 (System Env) - 系统属性 (System Properties) - 默认配置文件 (`arthas.properties`) 如果需要调整此优先级逻辑,可以通过设置 `arthas.config.overrideAll=true` 来反转默认的行为。 #### 3. **Arthas 类增强机制** Arthas 的核心功能之一是对目标类进行动态增强。这种增强依赖于 Java Instrumentation API,并通过将 `arthas-spy.jar` 添加至 Bootstrap ClassLoader 中完成。具体来说,在 ArthasBootstrap 单例初始化阶段即完成了这一操作,这也是实现诸如监控方法执行时间等功能的基础[^3]。 当不再需要某些增强时,可利用 `reset` 命令将其移除。例如: ```bash reset Test # 还原指定类 reset *List # 还原所有以 List 结尾的类 reset # 还原所有被增强过的类 ``` #### 4. **常见启动问题及解决方案** ##### a) JDK 不完整导致无法正常工作 部分情况下,由于使用的 JDK 版本缺失必要组件(如 tools.jar),可能导致 Arthas 启动失败。此时应确保安装的是完整的 Oracle JDK 或 OpenJDK 并重新尝试启动[^4]。 ##### b) 已经运行的应用程序与新安装的 JDK 不匹配 即使能够成功启动 Arthas,但如果应用程序仍在旧版 JDK 上运行而 Arthas 使用新版 JDK 执行,则可能出现兼容性问题。解决办法是重启整个应用使其切换到新的 JDK 环境下运行[^4]。 --- ### 示例代码片段展示如何手动触发 ArthasBootstrap 初始化 对于开发者而言了解内部原理有助于更好地排查潜在问题。下面给出一段简化后的伪代码用于说明 ArthasBootstrap 的主要职责所在: ```java public class CustomArthasBootstrap { public static void main(String[] args) throws Exception { // 创建 ArthasCore 环境 ArthasCore arthasCore = new ArthasCore(); // 将 spy jar 加入 BootStrapClassLoader File agentJarFile = new File("path/to/arthas-spy.jar"); URL url = agentJarFile.toURI().toURL(); Instrumentation instrumentation = ... ; // 获取 Instrumentation 对象实例 instrumentation.appendToBootstrapClassLoaderSearch(new JarFile(agentJarFile)); // 开始监听客户端请求 AsServer asServer = new AsServer(arthasCore); asServer.start(); } } ``` ---
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值