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

历史文章:

  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.
  • 0
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值