1.agent启动

agent启动、加载、拦截织入

所有的代码分析基于pinpoint 1.6.2

pinpoint agent使用方式为-javaagent:$PINPOINT_PATH/pinpoint-bootstrap-{version}.jar,故从javaagent开始。

一、jvm会调用PinpointBootStrap.premain方法,此方法做了如下操作:
  1. 将状态标志为启动(即同一jvm只能启动一次)
  2. 校验核心jar包
    1. 从classpath查找pinpoint-bootstrap-{version}.jar
    2. 解析pinpoint-bootstrap-{version}.jar所在文件夹$PINPOINT_PATH
    3. 从$PINPOINT_PATH/boot下查找核心jar,包含如下jar:
      • pinpoint-commons.jar
      • pinpoint-bootstrap-core.jar
      • pinpoint-bootstrap-core-optional.jar
      • pinpoint-annotations.jar
  3. 将核心jar包加入到jvm的BootstrapClassLoader中(因为classpath中只指定了pinpoint-bootstrap-{version}.jar,但是没有指定其他的jar,所以需要load进来)
  4. 调用PinpointStarter的start方法
二、PinpointStarter start主要做了如下操作:
  1. 获取pinpoint.agentId,格式:[a-zA-Z0-9\._\-]+ 最大24位

  2. 获取pinpoint.applicationName与pinpoint.agentId约束一样

  3. 获取$PINPOINT_PATH/plugin下面所有的jar

  4. 采用java通用的ServiceLoader.load(TraceMetadataProvider, PluginLoaderClassLoader)方式,从plugin下加载TraceMetadataProvider的实现类。

    1. 这里,使用的classloader是pinpoint自己的PluginLoaderClassLoader,其父classloader为AppClassLoader, 为了避免污染业务应用类。
    2. 而service类型是TraceMetadataProvider,关于java通用ServiceLoader的介绍参见
    3. 调用所有的plugin的TraceMetadataProvider的setup实现

    代码如下:

    TraceMetadataLoaderService typeLoaderService = new DefaultTraceMetadataLoaderService(pluginJars, loggerFactory);
    
  5. 将pinpoint系统默认的ServiceType和各个plugin的ServiceType注册到pinpoint中(实际持有类为ServiceTypeRegistryService)

    代码如下:

    ServiceTypeRegistryService serviceTypeRegistryService = new DefaultServiceTypeRegistryService(typeLoaderService, loggerFactory);
    
  6. 将plugin的AnnotationKey注册到pinpoint中

    1.TraceMetadataProvider 是pinpoint的插件开发时必须实现的接口,它提供的trace的基本信息。

    2.ServiceType代表的是哪个lib需要被trace。

    3.AnnotationKey用于定义参数,返回结果啥的

    可以参考dubbo的具体实现:

    public class DubboTraceMetadataProvider implements TraceMetadataProvider {
        @Override
        public void setup(TraceMetadataSetupContext context) {
            context.addServiceType(DubboConstants.DUBBO_PROVIDER_SERVICE_TYPE);
            context.addServiceType(DubboConstants.DUBBO_CONSUMER_SERVICE_TYPE);
            context.addAnnotationKey(DubboConstants.DUBBO_ARGS_ANNOTATION_KEY);
            context.addAnnotationKey(DubboConstants.DUBBO_RESULT_ANNOTATION_KEY);
        }
    }
    

    pinpoint官方定义参考:https://naver.github.io/pinpoint/plugindevguide.html

  7. 加载pinpoint.config

  8. 使用自己实现的ParallelCapablePinpointURLClassLoader来并行加载$PINPOINT_PATH/lib下的jar,同样不会污染业务应用类。

  9. 将上面所有的信息封装为AgentOption,传递给DefaultAgent用于初始化。

  10. 调用DefaultAgent的start方法

三、关于classloader

pinpoint启动过程中涉及到好几处的classloader,而jvm是典型的双亲委派模型:

System Classloader -> Extensions Classloader -> Bootstrap Classloader

这里来看一下pinpont的classloader的整个结构图:

在这里插入图片描述

  1. pinpoint-bootstrap.jar为启动类,由System Classloader加载,它负责加载其他的jar包。
  2. $PINPOINT_PATH/boot有Bootstrap Classloader加载,以便在整个jvm中使用
  3. plugin和lib只是pinpoint自己追踪使用,不会对应用类可见。
  4. 这里有两点值得注意一下(上图中标红的虚线),pinpoint加载外部jar的两种方式。
四、DefaultAgent

继续梳理主流程,DefaultAgent在lib包中,其初始化的时候主要初始化了一个DefaultApplicationContext对象,

并且会调用start方法,也是调用DefaultApplicationContext的start,那么下面主要来说DefaultApplicationContext。

其初始化流程中主要步骤如下:

  1. 创建ApplicationContextModule,此类中初始化了很多的对象,这里不再赘述。

  2. 调用jdk的Instrumentation.addTransformer将DefaultClassFileTransformerDispatcher(这个对象后面再说)设置进去

    该方法addTransformer就是注册了一个转换器。其作用就是加载class的时候,会经过该转换器转换。

  3. 初始化agentinfo发送器

  4. 初始化agentStat监视器

start方法如下:

  1. agentinfo发送器启动

    其发送频率为启动时及之后每5分钟一次,主要发送的内容有ip,hostname,agentid,applicationName,pid,启动时间,serverType,jvm版本,agentVersion等静态信息

    采用TCP发送

  2. agentStat监视器启动

    收集频率频率为5秒一次,收集6次发送一次,故30秒发送一次。

    这里的agentStat是指应用的状态:包括gc,cpuload,事务量(trace量),活跃Trace量,数据源统计。

    采用UDP发送

    详细的类图参考如下:
    在这里插入图片描述

五、DefaultClassFileTransformerDispatcher

从上面的流程可以知道只有一个可以改变字节码的地方,就是DefaultClassFileTransformerDispatcher。

那么,它就是代码织入的关键了。下面来分析一下它的核心transform方法的具体实现:

  1. 过滤

    1. classloader为pinpoint自己定义的,不转换
    2. class在com.navercorp.pinpoint包下的不转换
  2. 动态转换:暂不清楚(貌似没用)

  3. 过滤:java或javax包下的不转换

  4. 根据classname获取ClassFileTransformer,并进行转换(jdk的ClassFileTransformer.transform)

    这里的根据classname获取ClassFileTransformer从哪来的?这就需要看该类的初始化方法了。

该类的初始化方法有如下代码:

this.transformerRegistry = createTransformerRegistry(pluginContextLoadResult);

而this.transformerRegistry中持有了Map<String, ClassFileTransformer>属性,故这些ClassFileTransformer都来自于PluginContextLoadResult,这里通过一个时序图来看一下ClassFileTransformer是如何构造到PluginContextLoadResult中的:

在这里插入图片描述

上面一个大概的流程就是会load plugin下的所有ProfilerPlugin的实现,并为其注入TransformTemplate,然后调用其setup方法,这样所有的ProfilerPlugin的实现类即可将自己的TransformCallback设置到ClassFileTransformerLoader中,供真正transform的时候进行回调,进而改变字节码。

接着上面的核心transform方法,继续分析接下来的流程,调用jdk的ClassFileTransformer.transform后:

这里已经知道,所谓的ClassFileTransformer实际的实现为MatchableClassFileTransformerGuardDelegate(即时序图的最后一个类)。

  1. 其transform方法实现为

    final GuardInstrumentor guard = new GuardInstrumentor(this.profilerConfig, this.instrumentContext);
    try {
        // WARN external plugin api
        return transformCallback.doInTransform(guard, loader, className, classBeingRedefined, protectionDomain, classfileBuffer);
    } catch (InstrumentException e) {
        throw new PinpointException(e);
    } finally {
        guard.close();
    }
    

    即回调各个ProfilerPlugin的TransformCallback进行类转换。

  2. 接着看具体的回调方法,各个ProfilerPlugin的回调方法都需要拿到当前的类

    InstrumentClass target = instrumentor.getInstrumentClass(loader, className, classfileBuffer)
    

    此时的instrumentor其实是PluginInstrumentContext,那么来看他的getInstrumentClass实现

  3. PluginInstrumentContext.getInstrumentClass

    InstrumentEngine instrumentEngine = getInstrumentEngine();
    return instrumentEngine.getClass(this, classLoader, className, classFileBuffer);
    

    它将实现委托给了InstrumentEngine,默认的Engine为ASMEngine。

  4. ASMEngine底层使用ASM来对二进制字节码进行操作,实现了方法级别的前置和后置代码织入。

到这里,整个agent的启动及代码加载和拦截织入分析完毕

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
"agent"指的是Ambari Agent,它是Ambari框架中的一个组件,用于在集群节点上执行命令和任务。它负责与Ambari Server通信,并从服务器接收指令以执行相应的操作。 中提到了编辑ambari-agent.ini配置文件,并在其中的[security]部分添加了一行代码[force_https_protocol=PROTOCOL_TLSv1_2],然后重启agent。通过这个配置,可以强制Ambari Agent使用TLS v1.2协议进行通信,以提高通信的安全性。 中提到了一个名为com.intellij.rt.debugger.agent.CaptureAgent的问题记录,但是没有提供具体的解决方法。 综合来看,"agent"一词在这里指的是Ambari Agent,用于管理和执行集群节点上的任务和命令。它是与Ambari Server进行通信的重要组件。在问题中,是在操作Ambari Agent的配置文件以及解决问题记录时提到了"agent"一词。<span class="em">1</span><span class="em">2</span><span class="em">3</span> #### 引用[.reference_title] - *1* *3* [Ambari Agent 启动失败无法发送心跳参数.](https://blog.csdn.net/zhanglong_4444/article/details/104632756)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_1"}}] [.reference_item style="max-width: 50%"] - *2* [com.intellij.rt.debugger.agent.CaptureAgent](https://blog.csdn.net/xuexi_gan/article/details/118252220)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_1"}}] [.reference_item style="max-width: 50%"] [ .reference_list ]
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值