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进行加载

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
要跟踪 Nginx 请求,您可以使用 PinpointAgent 模块和 Nginx 的模块 ngx_http_upstream_module。具体步骤如下: 1. 安装 Pinpoint Agent 您可以去 Pinpoint 官网下对应语言的 Agent,然后按照官方文档的说明进行安装和配置。如果您使用的是 Java 语言,可以参考官网的 Java Agent 使用指南。 2. 安装 Nginx 如果您还没有安装 Nginx,可以去 Nginx 官网下对应版本的 Nginx,并按照官方文档的说明进行安装。 3. 配置 Nginx 在 Nginx 的配置文件中增以下配置: ``` http { upstream backend { server 127.0.0.1:8080; } server { listen 80; location / { proxy_pass http://backend; proxy_set_header X-Real-IP $remote_addr; proxy_set_header Pinpoint-Sampled true; proxy_set_header Pinpoint-Traceid $http_pinpoint_traceid; proxy_set_header Pinpoint-Spanid $http_pinpoint_spanid; proxy_set_header Pinpoint-Pspanid $http_pinpoint_pspanid; } } } ``` 其中,`upstream` 块定义了后端服务的地址和端口,`server` 块定义了监听的端口和请求的转发规则。在请求头中增了 `Pinpoint-Sampled`、`Pinpoint-Traceid`、`Pinpoint-Spanid`、`Pinpoint-Pspanid` 四个字段,用于在 Pinpoint 中跟踪请求。 4. 启动 Nginx 和 Pinpoint Agent 启动 Nginx 和 Pinpoint Agent,然后在 Pinpoint 的 Web 界面中就可以看到 Nginx 的请求信息了。 需要注意的是,Pinpoint 只能跟踪到通过 Agent 执行的代码的请求信息,如果您的应用程序中有使用到其他模块或第三方库,需要确保这些模块也支持 Pinpoint,否则 Pinpoint 将无法跟踪到这些请求的信息。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

zlp1992

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值