Pinpoint Agent加載流程分析

pinpoint 版本:2.0.3-SNAPSHOT
pinpoint利用java agent 特性,提供了一個agent jar包,此jar包會在應用運行之前先運行,agent和應用在同一個進程。pinpoint通過對各個第三方包編寫特定的插件,這些插件在agent運行時被加載,通過ASM對第三方包的類進行修改(Intercetor),應用在運行時使用的第三方包的類即是pinpoint修改後的,從而實現全鏈路追蹤的目的。

Agent加載流程

agent的入口在 com.navercorp.pinpoint.bootstrap.PinpointBootStrap的premain方法,其在pom文件中進行了配置
在這裏插入圖片描述
進入該方法,其會解析agent jar包所在的目錄,然後創建啓動類PinpointStarter並調用start方法,在start方法中,其創建com.navercorp.pinpoint.profiler.DefaultAgent,關鍵在於DefaultAgent的構造方法中的下面這行代碼:

        //在構造函數的這一步裏就對所有的插件進行了加載
        this.applicationContext = newApplicationContext(agentOption);

進入

    protected ApplicationContext newApplicationContext(AgentOption agentOption) {
        Assert.requireNonNull(agentOption, "agentOption");
        ProfilerConfig profilerConfig = Assert.requireNonNull(agentOption.getProfilerConfig(), "profilerConfig");

        ModuleFactoryResolver moduleFactoryResolver = new DefaultModuleFactoryResolver(profilerConfig.getInjectionModuleFactoryClazzName());
        ModuleFactory moduleFactory = moduleFactoryResolver.resolve();
        return new DefaultApplicationContext(agentOption, moduleFactory);
    }

上述代碼中,ModuleFactory默認爲ApplicationContextModuleFactory,這個是Guice依賴注入的module factory,pinpoint通過guice來實現類似spring的依賴自動注入,進入DefaultApplicationContext的構造方法

        //設置guice的依賴注入
        final Module applicationContextModule = moduleFactory.newModule(agentOption);
        this.injector = Guice.createInjector(Stage.PRODUCTION, applicationContextModule);

上面這兩行代碼完成了guice的模塊依賴注入配置,在ApplicationContextModuleFactory中會進行各種依賴配置,如下
在這裏插入圖片描述
我們主要關注 ClassFileTransformer的實現,可以看到它的實現是

bind(ClassFileTransformer.class).toProvider(ClassFileTransformerProvider.class).in(Scopes.SINGLETON);

ClassFileTransformerProvider中通過guice自動注入,注入PluginContextLoadResultPluginContextLoadResult通過PluginContextLoadResultProvider提供,這裏又自動注入了ProfilerPluginContextLoader

/**
 * pinpoint 分析插件加載器,用於加載pinpoint的各種插件,注意只是加載,並沒有轉換(transform)
 * @author HyunGil Jeong
 */
public interface ProfilerPluginContextLoader {

    /**
     * 加載所有插件,因爲pinpoint的分析插件都需要實現ProfilerPlugin接口
     * */
    PluginsSetupResult load(List<ProfilerPlugin> profilerPlugins);
}

ProfilerPluginContextLoader獲取的時候依賴了PluginSetup

    @Inject
    public ProfilerPluginContextLoaderProvider(ProfilerConfig profilerConfig,
                                               @ConfiguredApplicationType ServiceType configuredApplicationType,
                                               PluginSetup pluginSetup,
                                               InstrumentEngine instrumentEngine, BootstrapCore bootstrapCore,
                                               @PluginJars List<PluginJar> pluginJars) {
        this.profilerConfig = Assert.requireNonNull(profilerConfig, "profilerConfig");
        //配置文件配置的應用類別
        this.configuredApplicationType = Assert.requireNonNull(configuredApplicationType, "configuredApplicationType");
        this.pluginSetup = Assert.requireNonNull(pluginSetup, "pluginSetup");
        Assert.requireNonNull(instrumentEngine, "instrumentEngine");
        Assert.requireNonNull(bootstrapCore, "bootstrapCore");
        this.classInjectorFactory = new ClassInjectorFactory(instrumentEngine, bootstrapCore);
        this.pluginJars = Assert.requireNonNull(pluginJars, "pluginJars");
    }

真正引發查找並加載插件位於
DefaultPluginContextLoadResult的構造方法(自動注入的時候被調用),如下:

    public DefaultPluginContextLoadResult(ProfilerPluginContextLoader profilerPluginContextLoader, ClassLoader pluginClassLoader) {
        Assert.requireNonNull(profilerPluginContextLoader, "profilerPluginConfigurer");
        Assert.requireNonNull(pluginClassLoader, "pluginClassLoader");
        ProfilerPluginLoader profilerPluginLoader = new ProfilerPluginLoader();
        List<ProfilerPlugin> profilerPlugins = profilerPluginLoader.load(pluginClassLoader);
        //加載插件的地方
        this.pluginsSetupResult = profilerPluginContextLoader.load(profilerPlugins);
    }

ServiceType加載流程

ServiceType的加載位於ProfilerPluginContextLoaderProvider的構造方法自動注入@ConfiguredApplicationType最終在類TraceMetadataLoaderProvider中進行加載,如下:

    @Override
    public TraceMetadataLoader get() {
        TraceMetadataProviderLoader traceMetadataProviderLoader = new TraceMetadataProviderLoader();
        //這時候的pluginClassLoader已經加載了各個插件URL了
        List<TraceMetadataProvider> traceMetadataProviders = traceMetadataProviderLoader.load(pluginClassLoader);
        TraceMetadataLoader traceMetadataLoader = new TraceMetadataLoader(commonLoggerFactory);
        traceMetadataLoader.load(traceMetadataProviders);
        return traceMetadataLoader;
    }

目前pinpoint對於ServiceType的加載有兩種方式,一種是老的通過java spi的方式,一種是現在推薦的yml格式,在traceMetadataProviderLoader.load(pluginClassLoader);中會加載兩種方式實現的ServiceType

    @Override
    public List<TraceMetadataProvider> load(ClassLoader classLoader) {
        List<TraceMetadataProvider> traceMetadataProviders = new ArrayList<TraceMetadataProvider>();
        
        traceMetadataProviders.addAll(fromMetaFiles(classLoader));
        traceMetadataProviders.addAll(fromServiceLoader(classLoader));
        return traceMetadataProviders;
    }

ProfilerPlugin加載流程

在編寫pinpoint的插件的時候,都會實現ProfilerPlugin接口,代碼List<ProfilerPlugin> profilerPlugins = profilerPluginLoader.load(pluginClassLoader);通過java spi獲取所有實現了此接口的類,然後在代碼 profilerPluginContextLoader.load(profilerPlugins)中,最終在DefaultPluginSetup的setup方法

    @Override
    public PluginSetupResult setupPlugin(ProfilerPluginGlobalContext globalContext, ProfilerPlugin profilerPlugin, ClassInjector classInjector) {
        final ProfilerConfig profilerConfig = globalContext.getConfig();
        final ClassFileTransformerLoader transformerRegistry = new ClassFileTransformerLoader(profilerConfig, dynamicTransformTrigger);
        final DefaultProfilerPluginSetupContext setupContext = new DefaultProfilerPluginSetupContext(globalContext);
        final GuardProfilerPluginSetupContext guardSetupContext = new GuardProfilerPluginSetupContext(setupContext);

        final InstrumentContext instrumentContext = new PluginInstrumentContext(profilerConfig, instrumentEngine, dynamicTransformTrigger, classInjector, transformerRegistry);
        final GuardInstrumentContext guardInstrumentContext = preparePlugin(profilerPlugin, instrumentContext);
        try {
            // WARN external plugin api
            if (logger.isInfoEnabled()) {
                logger.info("{} Plugin setup", profilerPlugin.getClass().getName());
            }
            profilerPlugin.setup(guardSetupContext);
        } finally {
            guardSetupContext.close();
            guardInstrumentContext.close();
        }
        PluginSetupResult setupResult = new PluginSetupResult(setupContext, transformerRegistry);
        return setupResult;
    }

調用我們插件類的setup方法,一般我們還會實現 TransformTemplateAware,這是在這個類的preparePlugin方法進行設置
在這裏插入圖片描述
這樣便執行到了各個插件的setup方法,通過asm將各種transform進行加載

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章