天天看点

Pinpoint Agent启动过程分析

​ Pinpoint是一款全链路分析工具,提供了无侵入式的调用链监控、方法执行详情查看、应用状态信息监控等功能。目前公司通过开发pinpoint来满足链路分析、中间件增强、环境隔离等需求,图1为pinpoint的基本结构。

Pinpoint Agent启动过程分析

​ 总的来说,pinpoint-agent通过在各种中间件方法上埋点来采集信息,数据上报到pinpoint-collector,由pinpoint-collector接收并持久化到hbase,pinpoint-web负责渲染,在pinpoint-collector和pinpoint-web之间还会维护一条长连接,负责传输实时数据。

​ 本文主要分析pinpoint-agent的启动过程,pinpoint的版本为1.7.2,与目前最新版有一定差异(当前最新版为1.9.0)。

Pinpoint Agent启动过程

  1. 启动入口为PinpointBootStrap.premain方法
  2. 设置启动状态,解析启动参数,查找核心jar文件,使用BootstrapClassLoader加载核心jar中的类
  3. 实例化PinpointStarter,调用start方法
public static void premain(String agentArgs, Instrumentation instrumentation) {
        if (agentArgs == null) {
            agentArgs = "";
        }
        logger.info(ProductInfo.NAME + " agentArgs:" + agentArgs);
        final boolean success = STATE.start();
        if (!success) {
            logger.warn("pinpoint-bootstrap already started. skipping agent loading.");
            return;
        }
        Map<String, String> agentArgsMap = argsToMap(agentArgs);
        final ClassPathResolver classPathResolver = new AgentDirBaseClassPathResolver();
        if (!classPathResolver.verify()) {
            logger.warn("Agent Directory Verify fail. skipping agent loading.");
            logPinpointAgentLoadFail();
            return;
        }
        BootstrapJarFile bootstrapJarFile = classPathResolver.getBootstrapJarFile();
        appendToBootstrapClassLoader(instrumentation, bootstrapJarFile);
        PinpointStarter bootStrap = new PinpointStarter(agentArgsMap, bootstrapJarFile, classPathResolver, instrumentation);
        if (!bootStrap.start()) {
            logPinpointAgentLoadFail();
        }
    }
           
  1. 读取agentId和applicationName,扫描plugin目录下的jar文件
  2. 加载plugin目录下jar包的TraceMetadataProvider实现类,并执行setup方法,这个方法主要向上下文注册serviceType和AnnotationKey
  3. 实例化ServiceTypeRegistry和AnnotationKeyRegistry
  4. 解析并加载pinpoint.config配置,创建AgentClassLoader和AgentOption,实例化DefaultAgent,创建ApplicationContext,使用Guice框架进行依赖注入,大部分类在这个阶段被注册到DI容器
  5. 调用DefaultAgent的start方法,启动死锁监控线程、agent数据上报线程
  6. 注册ShutdownHook,JVM准备退出时执行DefaultAgent的stop方法,关闭应用上下文
boolean start() {
        final IdValidator idValidator = new IdValidator();
        final String agentId = idValidator.getAgentId();
        if (agentId == null) {
            return false;
        }
        final String applicationName = idValidator.getApplicationName();
        if (applicationName == null) {
            return false;
        }
        URL[] pluginJars = classPathResolver.resolvePlugins();
        // TODO using PLogger instead of CommonLogger
        CommonLoggerFactory loggerFactory = StdoutCommonLoggerFactory.INSTANCE;
        TraceMetadataLoaderService typeLoaderService = new DefaultTraceMetadataLoaderService(pluginJars, loggerFactory);
        ServiceTypeRegistryService serviceTypeRegistryService = new DefaultServiceTypeRegistryService(typeLoaderService, loggerFactory);
        AnnotationKeyRegistryService annotationKeyRegistryService = new DefaultAnnotationKeyRegistryService(typeLoaderService, loggerFactory);
        String configPath = getConfigPath(classPathResolver);
        if (configPath == null) {
            return false;
        }
        // set the path of log file as a system property
        saveLogFilePath(classPathResolver);
        savePinpointVersion();
        try {
            // Is it right to load the configuration in the bootstrap?
            ProfilerConfig profilerConfig = DefaultProfilerConfig.load(configPath);
            // this is the library list that must be loaded
            List<URL> libUrlList = resolveLib(classPathResolver);
            AgentClassLoader agentClassLoader = new AgentClassLoader(libUrlList.toArray(new URL[libUrlList.size()]));
            final String bootClass = getBootClass();
            agentClassLoader.setBootClass(bootClass);
            logger.info("pinpoint agent [" + bootClass + "] starting...");
            AgentOption option = createAgentOption(agentId, applicationName, profilerConfig, instrumentation, pluginJars, bootstrapJarFile, serviceTypeRegistryService, annotationKeyRegistryService);
            Agent pinpointAgent = agentClassLoader.boot(option);
            pinpointAgent.start();
            registerShutdownHook(pinpointAgent);
            logger.info("pinpoint agent started normally.");
        } catch (Exception e) {
            // unexpected exception that did not be checked above
            logger.warn(ProductInfo.NAME + " start failed.", e);
            return false;
        }
        return true;
    }
           

总结:

简单来说,pinpoint agent的启动过程为:

PinpointBootStrap.premain->

解析bootstrap jar、agent lib、plugin等jar路径->

将pinpoint核心类交给BootstrapClassLoader加载->

new PinpointStarter().start->

通过反射实例化DefaultAgent->new ApplicationContext()->

Guice.createInjector(),使用Guice框架创建并维护pinpoint中的bean,在这个阶段会加载pinpoint中的所有plugin,并执行plugin的初始化过程->

pinpointAgent.start->启动deadlockMonitor、agent数据上报等线程->

添加shutdownHook,jvm退出时关闭agent。

           

​ 本文简单梳理了pinpoint的启动过程,总得来说比较易懂(对比spring、netty等),如果有时间,后面可能会写如何写一个简单的pinpoint插件来增强使用的框架或中间件,也可能会分析一些常用的中间件pinpoint插件。