arthas源码刨析:arthas-core (2)

news2024/11/26 2:49:12

文章目录

  • attach JVM
  • agent
  • **ArthasBootstrap**

arthas-core的启动可以从上一篇做参考
在这里插入图片描述
参考 pom,即启动是调用的 Arthas 的 main 方法
在这里插入图片描述

attach JVM

JVM提供了 Java Attach 功能,能够让客户端与目标JVM进行通讯从而获取JVM运行时的数据,甚至可以通过Java Attach 加载自定义的代理工具,实现AOP、运行时class热更新等功能。

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);
            }
           ……
        try {
             virtualMachine.loadAgent(arthasAgentPath,
                     configure.getArthasCore() + ";" + configure.toString());
         } catch (IOException e) {
    }

VirtualMachine.list() 相当于 jps 的功能:
在这里插入图片描述
attach是依靠这条代码:

virtualMachine = VirtualMachine.attach(virtualMachineDescriptor);

之后就是 loadAgent 并传入 core 和 configure 参数。

agent

agent 的代码很简单,只有2个类:
在这里插入图片描述
在这里插入图片描述

核心就是 AgentBootstrap ,对于 javaagent来说核心的启动入口:

    public static void premain(String args, Instrumentation inst) {
        main(args, inst);
    }

    public static void agentmain(String args, Instrumentation inst) {
        main(args, inst);
    } 

神奇不,在 arthas-core 里传入的 -core 参数实际上是透传给agent的。整的很绕。 通过 bind 方法进行线程绑定,这一步和 arthas-boot 很像。

private static synchronized void main(String args, final Instrumentation inst) {
        // 尝试判断arthas是否已在运行,如果是的话,直接就退出
        try {
            Class.forName("java.arthas.SpyAPI"); // 加载不到会抛异常
            if (SpyAPI.isInited()) {
                ps.println("Arthas server already stared, skip attach.");
                ps.flush();
                return;
            }
        } catch (Throwable e) {
            // ignore
        }
        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()) {
                ps.println("Can not find arthas-core jar file from args: " + arthasCoreJarFile);
                // try to find from arthas-agent.jar directory
                CodeSource codeSource = AgentBootstrap.class.getProtectionDomain().getCodeSource();
                if (codeSource != null) {
                    try {
                        File arthasAgentJarFile = new File(codeSource.getLocation().toURI().getSchemeSpecificPart());
                        arthasCoreJarFile = new File(arthasAgentJarFile.getParentFile(), ARTHAS_CORE_JAR);
                        if (!arthasCoreJarFile.exists()) {
                            ps.println("Can not find arthas-core jar file from agent jar directory: " + arthasAgentJarFile);
                        }
                    } catch (Throwable e) {
                        ps.println("Can not find arthas-core jar file from " + codeSource.getLocation());
                        e.printStackTrace(ps);
                    }
                }
            }
            if (!arthasCoreJarFile.exists()) {
                return;
            }

            /**
             * Use a dedicated thread to run the binding logic to prevent possible memory leak. #195
             */
            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);
        }
    }

ArthasBootstrap

bind 有调用了 core 里面 ArthasBootstrap.getInstance

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.");
    }

而在 ArthasBootstrap中 我们用的 terminal,也就是 ShellServer, 就在 6. start agent server

private ArthasBootstrap(Instrumentation instrumentation, Map<String, String> args) throws Throwable {
        this.instrumentation = instrumentation;
        initFastjson();
        // 1. initSpy()
        initSpy();
        // 2. ArthasEnvironment
        initArthasEnvironment(args);
        String outputPathStr = configure.getOutputPath();
        if (outputPathStr == null) {
            outputPathStr = ArthasConstants.ARTHAS_OUTPUT;
        }
        outputPath = new File(outputPathStr);
        outputPath.mkdirs();
        // 3. init logger
        loggerContext = LogUtil.initLogger(arthasEnvironment);
        // 4. 增强ClassLoader
        enhanceClassLoader();
        // 5. init beans
        initBeans();
        // 6. start agent 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);
    }

ShellServerImpl 的 bind 这段实现中,BuiltinCommandPack 完成了命名的声明并与 cli 输入内容进行命名绑定。并用 Netty 开启server。

 /**
     * Bootstrap arthas server
     *
     * @param configure 配置信息
     * @throws IOException 服务器启动失败
     */
    private void bind(Configure configure) throws Throwable {

        long start = System.currentTimeMillis();

        if (!isBindRef.compareAndSet(false, true)) {
            throw new IllegalStateException("already bind");
        }

        // init random port
        if (configure.getTelnetPort() != null && configure.getTelnetPort() == 0) {
            int newTelnetPort = SocketUtils.findAvailableTcpPort();
            configure.setTelnetPort(newTelnetPort);
            logger().info("generate random telnet port: " + newTelnetPort);
        }
        if (configure.getHttpPort() != null && configure.getHttpPort() == 0) {
            int newHttpPort = SocketUtils.findAvailableTcpPort();
            configure.setHttpPort(newHttpPort);
            logger().info("generate random http port: " + newHttpPort);
        }
        // try to find appName
        if (configure.getAppName() == null) {
            configure.setAppName(System.getProperty(ArthasConstants.PROJECT_NAME,
                    System.getProperty(ArthasConstants.SPRING_APPLICATION_NAME, null)));
        }

        try {
            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 options = new ShellServerOptions()
                            .setInstrumentation(instrumentation)
                            .setPid(PidUtils.currentLongPid())
                            .setWelcomeMessage(ArthasBanner.welcome());
            if (configure.getSessionTimeout() != null) {
                options.setSessionTimeout(configure.getSessionTimeout() * 1000);
            }

            this.httpSessionManager = new HttpSessionManager();
            if (IPUtils.isAllZeroIP(configure.getIp()) && StringUtils.isBlank(configure.getPassword())) {
                // 当 listen 0.0.0.0 时,强制生成密码,防止被远程连接
                String errorMsg = "Listening on 0.0.0.0 is very dangerous! External users can connect to your machine! "
                        + "No password is currently configured. " + "Therefore, a default password is generated, "
                        + "and clients need to use the password to connect!";
                AnsiLog.error(errorMsg);
                configure.setPassword(StringUtils.randomString(64));
                AnsiLog.error("Generated arthas password: " + configure.getPassword());

                logger().error(errorMsg);
                logger().info("Generated arthas password: " + configure.getPassword());
            }

            this.securityAuthenticator = new SecurityAuthenticatorImpl(configure.getUsername(), configure.getPassword());

            shellServer = new ShellServerImpl(options);

            List<String> disabledCommands = new ArrayList<String>();
            if (configure.getDisabledCommands() != null) {
                String[] strings = StringUtils.tokenizeToStringArray(configure.getDisabledCommands(), ",");
                if (strings != null) {
                    disabledCommands.addAll(Arrays.asList(strings));
                }
            }
            BuiltinCommandPack builtinCommands = new BuiltinCommandPack(disabledCommands);
            List<CommandResolver> resolvers = new ArrayList<CommandResolver>();
            resolvers.add(builtinCommands);

            //worker group
            workerGroup = new NioEventLoopGroup(new DefaultThreadFactory("arthas-TermServer", true));

            // TODO: discover user provided command resolver
            if (configure.getTelnetPort() != null && configure.getTelnetPort() > 0) {
                logger().info("try to bind telnet server, host: {}, port: {}.", configure.getIp(), configure.getTelnetPort());
                shellServer.registerTermServer(new HttpTelnetTermServer(configure.getIp(), configure.getTelnetPort(),
                        options.getConnectionTimeout(), workerGroup, httpSessionManager));
            } else {
                logger().info("telnet port is {}, skip bind telnet server.", configure.getTelnetPort());
            }
            if (configure.getHttpPort() != null && configure.getHttpPort() > 0) {
                logger().info("try to bind http server, host: {}, port: {}.", configure.getIp(), configure.getHttpPort());
                shellServer.registerTermServer(new HttpTermServer(configure.getIp(), configure.getHttpPort(),
                        options.getConnectionTimeout(), workerGroup, httpSessionManager));
            } else {
                // listen local address in VM communication
                if (configure.getTunnelServer() != null) {
                    shellServer.registerTermServer(new HttpTermServer(configure.getIp(), configure.getHttpPort(),
                            options.getConnectionTimeout(), workerGroup, httpSessionManager));
                }
                logger().info("http port is {}, skip bind http server.", configure.getHttpPort());
            }

            for (CommandResolver resolver : resolvers) {
                shellServer.registerCommandResolver(resolver);
            }

            shellServer.listen(new BindHandler(isBindRef));
            if (!isBind()) {
                throw new IllegalStateException("Arthas failed to bind telnet or http port! Telnet port: "
                        + String.valueOf(configure.getTelnetPort()) + ", http port: "
                        + String.valueOf(configure.getHttpPort()));
            }

            //http api session manager
            sessionManager = new SessionManagerImpl(options, shellServer.getCommandManager(), shellServer.getJobController());
            //http api handler
            httpApiHandler = new HttpApiHandler(historyManager, sessionManager);

            logger().info("as-server listening on network={};telnet={};http={};timeout={};", configure.getIp(),
                    configure.getTelnetPort(), configure.getHttpPort(), options.getConnectionTimeout());

            // 异步回报启动次数
            if (configure.getStatUrl() != null) {
                logger().info("arthas stat url: {}", configure.getStatUrl());
            }
            UserStatUtil.setStatUrl(configure.getStatUrl());
            UserStatUtil.setAgentId(configure.getAgentId());
            UserStatUtil.arthasStart();

            try {
                SpyAPI.init();
            } catch (Throwable e) {
                // ignore
            }

            logger().info("as-server started in {} ms", System.currentTimeMillis() - start);
        } catch (Throwable e) {
            logger().error("Error during start as-server", e);
            destroy();
            throw e;
        }
    }

参考:

https://blog.csdn.net/tianjindong0804/article/details/128423819

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2063360.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

算法-矩阵置零(73)

leetcode题目链接 这道题因为要求在O&#xff08;1&#xff09;的空间复杂度下面完成&#xff0c;所以最好的情况就是利用矩阵本身有的元素进行代码编写&#xff0c;而不另外开辟空间。 所以思路如下&#xff1a; 1.遍历第一行第一列&#xff0c;观察是否需要置0&#xff0c…

【面试实战】2024-08-22-面试总结

文章目录 1. 讲讲内存管理2. 什么是智能指针&#xff1f;有哪些种类&#xff1f;分别应用于哪些场景&#xff1f; 1. 讲讲内存管理 栈和堆的区别&#xff1a; ①栈和堆都是用来存储程序数据的内存区域。栈上的内存区域是有限的&#xff0c;栈用来存储局部变量和函数的调用信息。…

史上最强座舱AI芯登场,座舱「百模大战」爆发前夜

作者 |张马也 编辑 |德新 一场座舱大模型的竞赛&#xff0c;正在拉开序幕。 8月&#xff0c;英特尔在深圳举办AI座舱产品暨车载独立显卡发布会上。 会上&#xff0c;其发布了首款锐炫车载独立显卡ARC A760-A&#xff0c;这在已经高度内卷的座舱市场&#xff0c;又投下一颗重…

微软发布 Phi-3.5 系列模型,涵盖端侧、多模态、MOE;字节 Seed-ASR:自动识别多语言丨 RTE 开发者日报

开发者朋友们大家好&#xff1a; 这里是 「RTE 开发者日报」 &#xff0c;每天和大家一起看新闻、聊八卦。我们的社区编辑团队会整理分享 RTE&#xff08;Real-Time Engagement&#xff09; 领域内「有话题的 新闻 」、「有态度的 观点 」、「有意思的 数据 」、「有思考的 文…

DatePicker 两个日期选择框的时间范围设置不可选日期

思路 截至时间的不可选择日期&#xff0c;通过监听开始时间&#xff0c;生成

tp5php7.4配置sqlserver问题汇总

先修改database.php文件 查看php版本选择sqlserver扩展 通过百度网盘分享的文件&#xff1a;sqlserver 链接&#xff1a;https://pan.baidu.com/s/1zrIV8VWQZM9miLpyH01Aww?pwdxdgx 提取码&#xff1a;xdgx 通过我的分享链接复制自己需要的dll到php的ext下 在php.ini里添加扩…

电子看板助力线缆工厂生产数字化改善

在当今数字化时代&#xff0c;线缆工厂面临着日益激烈的市场竞争和不断提高的客户需求。为了提高生产效率、降低成本、提升产品质量&#xff0c;越来越多的线缆工厂开始引入电子看板系统&#xff0c;实现生产数字化改善。 一、线缆工厂生产面临的挑战 1、生产过程复杂 线缆生产…

【与C++的邂逅】--- 类和对象(中)

Welcome to 9ilks Code World (๑•́ ₃ •̀๑) 个人主页: 9ilk (๑•́ ₃ •̀๑) 文章专栏&#xff1a; 与C的邂逅 本篇博客我们将学习类和对象中&#xff0c;认识类的六个默认成员函数以及实现日期类。下图为本节思维导图。 &#x1f3e0; 类的6个默认成员函…

云渲染助力:轻松应对UE5的高电脑配置需求

UE5以其革命性的图形和模拟技术&#xff0c;不断推动游戏和视觉艺术的边界&#xff0c;但对高端硬件的依赖往往成为创意实现的瓶颈。幸运的是&#xff0c;云渲染的出现打破了这一限制。通过云端的强大计算资源&#xff0c;云渲染为艺术家和开发者提供了灵活、可扩展的解决方案&…

个人理解—uboot启动过程(2)BL1低级初始化

lowlevel_init看名字就知道是关于初级方面的初始化&#xff0c;其中可用将其干的事情分为11个步骤&#xff1a; &#xff08;1&#xff09;push {lr} 也就是lr压栈。 &#xff08;2&#xff09;检测复位状态&#xff1a;如冷上电、热启动、睡眠等。冷上电要初始化DDR后才能使用…

无线电子产品前端射频设计注意事项

1 引言 1.1 编写目的 整理带无线的产品RF部分对外壳及PCBA设计注意事项&#xff0c;主要目的是为设计者提供一些参考&#xff0c;提高无线产品的设计质量和效率、保证可生产性。 1.2 背景 RF布局是射频电路设计极为重要的步骤和环节&#xff0c;关系到射频信号能否有效的工…

房产系统架构开发小程序分析

房产系统架构开发小程序在当前市场中具有显著的优势和潜力。以下是对房产小程序的分析&#xff1a; 用户需求满足&#xff1a;房产小程序通过提供楼盘信息查询、VR看房体验、购房流程指南等功能&#xff0c;满足用户对房产信息的需求&#xff0c;并提供更加便捷的用户体验 。…

第4章 汇编语言和汇编软件

第4章 汇编语言和汇编软件 该章主要介绍了汇编语言和汇编语言编译器的安装和使用。 汇编语言程序 该小节主要介绍了为什么要有汇编语言和汇编语言程序的一些基础写法。 书中有提到CPU有不同的架构&#xff0c;汇编语言有不同的风格&#xff0c;那么不同的CPU架构和不同的汇…

线上陪玩APP开发功能分析

随着电子竞技和在线娱乐的兴起&#xff0c;线上陪玩APP作为一种新兴的服务模式&#xff0c;逐渐受到广大游戏爱好者的青睐。开发一款高效、便捷、用户友好的线上陪玩APP&#xff0c;需要综合考虑市场调研、功能规划、技术选型、用户体验及安全性等多个方面。以下是对线上陪玩AP…

matter模组有无源测试事例

测试一款matter模组的硬件性能 1.1 天线阻抗、电压驻波比测试 主要测试&#xff1a;PCB板载天线设计效率及板材PCB铜面的平整度等 1.2 模组有源数据测试 主要测试&#xff1a;模组的阻抗匹配、频偏等情况 1.3 模组传输能量精度 主要测试&#xff1a;矢量误差等数据 1.4 模…

PCL “libvtkCommonCore-9.1.so.9.1.0: undefined reference to...@GLIBCXX_3.4.30”

1.问题描述&#xff1a; 完成 PCL、VTK 搭建后(https://mp.csdn.net/mp_blog/creation/editor/139858438)&#xff0c;笔者运行PCL项目程序中&#xff0c;遇到下面错误&#xff1a; [build] /usr/bin/ld: /usr/lib/x86_64-linux-gnu/libvtkCommonCore-9.1.so.9.1.0: undefine…

云计算产业链图谱_产业链全景图_云计算行业市场分析

在产业数字化转型的背景下&#xff0c;云计算作为信息技术的重要组成部分&#xff0c;正逐渐成为各行业数字化、智能化转型的关键支撑。受益于5G、大数据、物联网、人工智能等技术的快速发展&#xff0c;云计算产业规模持续扩大&#xff0c;市场需求不断增长。云计算作为一种新…

【握奇数据招聘(北森)-注册/登录安全分析报告】

前言 由于网站注册入口容易被黑客攻击&#xff0c;存在如下安全问题&#xff1a; 1. 暴力破解密码&#xff0c;造成用户信息泄露 2. 短信盗刷的安全问题&#xff0c;影响业务及导致用户投诉 3. 带来经济损失&#xff0c;尤其是后付费客户&#xff0c;风险巨大&#xff0c;造…

顺序表的基本操作代码

seqlist.h&#xff1a; #pragma once #include<assert.h> #include<stdio.h> #include<stdlib.h> typedef int type; typedef struct Seqlist { type* data; int size; int capacity; }sl; //初始化顺序表 void initialize(sl* ps); //销毁线性表…

网页,html,Web端实现RTSP/RTMP实时推流视频和播放

随着技术的不断发展&#xff0c;实时流传输已经成为许多应用的重要组成部分。RTSP&#xff08;Real-Time Streaming Protocol&#xff09;作为一种实时流媒体传输协议&#xff0c;广泛应用于视频监控、直播等领域。然而&#xff0c;在Web端实现RTSP实时推流视频播放却面临一些挑…