pinpoint 版本:2.0.3-SNAPSHOT
pinpoint利用java agent 特性,提供了一个agent jar包,此jar包会在应用运行之前先运行,agent和应用在同一个进程。pinpoint通过对各个第三方包编写特定的插件,这些插件在agent运行时被加载,通过ASM对第三方包的类进行修改(Intercetor),应用在运行时使用的第三方包的类即是pinpoint修改后的,从而实现全链路追踪的目的。
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自动注入,注入PluginContextLoadResult
,PluginContextLoadResult
通过PluginContextLoadResultProvider
提供,这里又自动注入了ProfilerPluginContextLoader
/**
* pinpoint 分析插件加载器,用于加载pinpoint的各种插件,注意只是加载,并没有转换(transform)
* @author HyunGil Jeong
*/
public interface ProfilerPluginContextLoader {
/**
* 加载所有插件,因为pinpoint的分析插件都需要实现ProfilerPlugin接口
* */
PluginsSetupResult load(List profilerPlugins);
}
而ProfilerPluginContextLoader
获取的时候依赖了PluginSetup
@Inject
public ProfilerPluginContextLoaderProvider(ProfilerConfig profilerConfig,
@ConfiguredApplicationType ServiceType configuredApplicationType,
PluginSetup pluginSetup,
InstrumentEngine instrumentEngine, BootstrapCore bootstrapCore,
@PluginJars List 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 profilerPlugins = profilerPluginLoader.load(pluginClassLoader);
//加载插件的地方
this.pluginsSetupResult = profilerPluginContextLoader.load(profilerPlugins);
}
ServiceType的加载位于ProfilerPluginContextLoaderProvider
的构造方法自动注入@ConfiguredApplicationType
最终在类TraceMetadataLoaderProvider
中进行加载,如下:
@Override
public TraceMetadataLoader get() {
TraceMetadataProviderLoader traceMetadataProviderLoader = new TraceMetadataProviderLoader();
//这时候的pluginClassLoader已经加载了各个插件URL了
List 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 load(ClassLoader classLoader) {
List traceMetadataProviders = new ArrayList();
traceMetadataProviders.addAll(fromMetaFiles(classLoader));
traceMetadataProviders.addAll(fromServiceLoader(classLoader));
return traceMetadataProviders;
}
在编写pinpoint的插件的时候,都会实现ProfilerPlugin
接口,代码List
通过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进行加载