再探Pinpoint Agent (一)
1、Agent作为Pinpoint采集部分,作为数据的来源,在系统中发挥着重要的作用,Pinpoint Agent启动Agent时添加的参数有
-javaagent:$path\pinpoint-agent-1.7.4-SNAPSHOT\pinpoint-bootstrap-1.7.4-SNAPSHOT.jar -Dpinpoint.agentId=111 -Dpinpoint.applicationName=abc
当启动监控程序后,直接进入到PinpointBootStrap类下的premain函数:
public static void premain(String agentArgs, Instrumentation instrumentation) {
if (agentArgs == null) {
agentArgs = "";
}
logger.info(ProductInfo.NAME + " agentArgs:" + agentArgs);
final boolean success = STATE.start(); //检查pinpoint是否已经启动
if (!success) {
logger.warn("pinpoint-bootstrap already started. skipping agent loading.");
return;
}
Map<String, String> agentArgsMap = argsToMap(agentArgs);//Args转Map
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();
}
}
下面主要介绍关键的代码:
final boolean success = STATE.start(); //检查pinpoint是否已经启动
->public boolean start() {
return state.compareAndSet(STATE_NONE, STATE_STARTED);
}
主要通过CAS判断是否已经启。
final ClassPathResolver classPathResolver = new AgentDirBaseClassPathResolver();
public AgentDirBaseClassPathResolver(String classPath) {
this.classPath = classPath;
this.agentPattern = DEFAULT_AGENT_PATTERN;
this.agentCommonsPattern = DEFAULT_AGENT_COMMONS_PATTERN;
this.agentCorePattern = DEFAULT_AGENT_CORE_PATTERN;
this.agentCoreOptionalPattern = DEFAULT_AGENT_CORE_OPTIONAL_PATTERN;
this.annotationsPattern = DEFAULT_ANNOTATIONS;
this.fileExtensionList = getDefaultFileExtensionList();
}
主要是指定相关依赖jar的名称,采用正则表达式,方便版本扩展
lassPathResolver.verify()
->public boolean verify() {
final BootstrapJarFile bootstrapJarFile = new BootstrapJarFile();
// 1st find boot-strap.jar
final boolean agentJarNotFound = this.findAgentJar();
if (!agentJarNotFound) {
logger.warn("pinpoint-bootstrap-x.x.x(-SNAPSHOT).jar not found.");
return false;
}
// 2nd find pinpoint-commons.jar
final String pinpointCommonsJar = getPinpointCommonsJar();
if (pinpointCommonsJar == null) {
logger.warn("pinpoint-commons-x.x.x(-SNAPSHOT).jar not found");
return false;
}
final JarFile pinpointCommonsJarFile = getJarFile(pinpointCommonsJar);
if (pinpointCommonsJarFile == null) {
logger.warn("pinpoint-commons-x.x.x(-SNAPSHOT).jar not found");
return false;
}
bootstrapJarFile.append(pinpointCommonsJarFile);
// 3rd find bootstrap-core.jar
final String bootStrapCoreJar = getBootStrapCoreJar();
if (bootStrapCoreJar == null) {
logger.warn("pinpoint-bootstrap-core-x.x.x(-SNAPSHOT).jar not found");
return false;
}
JarFile bootStrapCoreJarFile = getJarFile(bootStrapCoreJar);
if (bootStrapCoreJarFile == null) {
logger.warn("pinpoint-bootstrap-core-x.x.x(-SNAPSHOT).jar not found");
return false;
}
bootstrapJarFile.append(bootStrapCoreJarFile);
// 4th find bootstrap-core-optional.jar
final String bootStrapCoreOptionalJar = getBootStrapCoreOptionalJar();
if (bootStrapCoreOptionalJar == null) {
logger.info("pinpoint-bootstrap-core-optional-x.x.x(-SNAPSHOT).jar not found");
} else {
JarFile bootStrapCoreOptionalJarFile = getJarFile(bootStrapCoreOptionalJar);
if (bootStrapCoreOptionalJarFile == null) {
logger.info("pinpoint-bootstrap-core-optional-x.x.x(-SNAPSHOT).jar not found");
} else {
bootstrapJarFile.append(bootStrapCoreOptionalJarFile);
}
}
// 5th find annotations.jar : optional dependency
final String annotationsJar = getAnnotationsJar();
if (annotationsJar == null) {
logger.info("pinpoint-annotations-x.x.x(-SNAPSHOT).jar not found");
} else {
JarFile jarFile = getJarFile(annotationsJar);
bootstrapJarFile.append(jarFile);
}
this.bootstrapJarFile = bootstrapJarFile;
return true;
}
由上面的程序可以看出,主要是将boot-strap.jar、pinpoint-commons.jar、bootstrap-core.jar、bootstrap-core-optional.jar、annotations.jar添加到bootstrapJarFile文件中,append方法就是将以上文件添加到一个list集合中
4.添加到BootstrapClassLoader查找的类中
appendToBootstrapClassLoader(instrumentation, bootstrapJarFile);
->instrumentation.appendToBootstrapClassLoaderSearch(jarFile);
5.调用start方法
PinpointStarter bootStrap = new PinpointStarter(agentArgsMap, bootstrapJarFile, classPathResolver, instrumentation);
bootStrap.start()
start方法如下:
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(); //获取插件url
// 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;
}
介绍关键代码:
1.调用provider的setup方法,设置插件的ServiceType
TraceMetadataLoaderService typeLoaderService = new DefaultTraceMetadataLoaderService(pluginJars, loggerFactory);
->loader.load(jarLists);
->load(providers);
->provider.setup(context); //依次执行provider的setup方法
以tomcat插件为例,
@Override
public void setup(TraceMetadataSetupContext context) {
context.addServiceType(TomcatConstants.TOMCAT);
context.addServiceType(TomcatConstants.TOMCAT_METHOD);
}
其中TomcatConstants类下定义的TOMCAT和TOMCAT_METHOD如下:
public static final ServiceType TOMCAT = ServiceTypeFactory.of(1010, "TOMCAT", RECORD_STATISTICS);
public static final ServiceType TOMCAT_METHOD = ServiceTypeFactory.of(1011, "TOMCAT_METHOD");//工厂构建
-> return new DefaultServiceType(code, name, desc, properties);
通过代码可以看出主要是构建ServiceType对象实例
2.主要是查找出刚才插件中创建的ServiceType以及默认ServiceType合并后的一些映射关系
ServiceTypeRegistryService serviceTypeRegistryService = new DefaultServiceTypeRegistryService(typeLoaderService, loggerFactory);
3.主要是查找出刚才插件中创建的AnnotationKey以及默认AnnotationKey合并后的一些映射关系
AnnotationKeyRegistryService annotationKeyRegistryService = new DefaultAnnotationKeyRegistryService(typeLoaderService, loggerFactory);
->this.registry = buildServiceTypeRegistry();
4.声明启动类
AgentClassLoader agentClassLoader = new AgentClassLoader(libUrlList.toArray(new URL[libUrlList.size()]));
final String bootClass = getBootClass();
>BOOT_CLASS = "com.navercorp.pinpoint.profiler.DefaultAgent" //声明启动类
5.创建AgentOption
AgentOption option = createAgentOption(agentId, applicationName, profilerConfig, instrumentation, pluginJars, bootstrapJarFile, serviceTypeRegistryService, annotationKeyRegistryService);
6、使用agentClassLoader启动,其实就是初始化DefaultAgent
Agent pinpointAgent = agentClassLoader.boot(option);
下面是DefaultAgent的初始化方法
static {
// Preload classes related to pinpoint-rpc module.
ClassPreLoader.preload();
}
-> preload(65535);
->serverAcceptor = new PinpointServerAcceptor(); //NIO
public DefaultAgent(AgentOption agentOption, final InterceptorRegistryBinder interceptorRegistryBinder) {
if (agentOption == null) {
throw new NullPointerException("agentOption must not be null");
}
if (agentOption.getInstrumentation() == null) {
throw new NullPointerException("instrumentation must not be null");
}
if (agentOption.getProfilerConfig() == null) {
throw new NullPointerException("profilerConfig must not be null");
}
if (agentOption.getServiceTypeRegistryService() == null) {
throw new NullPointerException("serviceTypeRegistryService must not be null");
}
if (interceptorRegistryBinder == null) {
throw new NullPointerException("interceptorRegistryBinder must not be null");
}
logger.info("AgentOption:{}", agentOption);
this.binder = new Slf4jLoggerBinder();
bindPLoggerFactory(this.binder);
this.interceptorRegistryBinder = interceptorRegistryBinder;
interceptorRegistryBinder.bind();
this.serviceTypeRegistryService = agentOption.getServiceTypeRegistryService();
dumpSystemProperties(); //导出系统变量
dumpConfig(agentOption.getProfilerConfig());
changeStatus(AgentStatus.INITIALIZING); //修改Agent状态
this.profilerConfig = agentOption.getProfilerConfig();
this.applicationContext = newApplicationContext(agentOption, interceptorRegistryBinder);
InterceptorInvokerHelper.setPropagateException(profilerConfig.isPropagateInterceptorException());
}
这段代码中最关键的一句是
this.applicationContext = newApplicationContext(agentOption, interceptorRegistryBinder);
->return new DefaultApplicationContext(agentOption, interceptorRegistryBinder, moduleFactoryProvider);
DefaultApplicationContext构造方法如下:
public DefaultApplicationContext(AgentOption agentOption, final InterceptorRegistryBinder interceptorRegistryBinder, ModuleFactory moduleFactory) {
Assert.requireNonNull(agentOption, "agentOption must not be null");
this.profilerConfig = Assert.requireNonNull(agentOption.getProfilerConfig(), "profilerConfig must not be null");
Assert.requireNonNull(moduleFactory, "moduleFactory must not be null");
this.instrumentation = agentOption.getInstrumentation();
this.serviceTypeRegistryService = agentOption.getServiceTypeRegistryService();
if (logger.isInfoEnabled()) {
logger.info("DefaultAgent classLoader:{}", this.getClass().getClassLoader());
}
//关键的下面两句
final Module applicationContextModule = moduleFactory.newModule(agentOption, interceptorRegistryBinder);
this.injector = Guice.createInjector(Stage.PRODUCTION, applicationContextModule);
//使用injector 获取实例
this.instrumentEngine = injector.getInstance(InstrumentEngine.class);
this.classFileDispatcher = injector.getInstance(ClassFileTransformerDispatcher.class);
this.dynamicTransformTrigger = injector.getInstance(DynamicTransformTrigger.class);
// ClassFileTransformer classFileTransformer = injector.getInstance(ClassFileTransformer.class);
ClassFileTransformer classFileTransformer = wrap(classFileDispatcher);
instrumentation.addTransformer(classFileTransformer, true);
this.spanStatClientFactory = injector.getInstance(Key.get(PinpointClientFactory.class, SpanStatClientFactory.class));
logger.info("spanStatClientFactory:{}", spanStatClientFactory);
this.spanDataSender = newUdpSpanDataSender();
logger.info("spanDataSender:{}", spanDataSender);
this.statDataSender = newUdpStatDataSender();
logger.info("statDataSender:{}", statDataSender);
this.clientFactory = injector.getInstance(Key.get(PinpointClientFactory.class, DefaultClientFactory.class));
logger.info("clientFactory:{}", clientFactory);
this.tcpDataSender = injector.getInstance(EnhancedDataSender.class);
logger.info("tcpDataSender:{}", tcpDataSender);
this.traceContext = injector.getInstance(TraceContext.class);
this.agentInformation = injector.getInstance(AgentInformation.class);
logger.info("agentInformation:{}", agentInformation);
this.serverMetaDataRegistryService = injector.getInstance(ServerMetaDataRegistryService.class);
this.deadlockMonitor = injector.getInstance(DeadlockMonitor.class);
this.agentInfoSender = injector.getInstance(AgentInfoSender.class);
this.agentStatMonitor = injector.getInstance(AgentStatMonitor.class);
}
上面代码关键的两句:
final Module applicationContextModule = moduleFactory.newModule(agentOption, interceptorRegistryBinder);
this.injector = Guice.createInjector(Stage.PRODUCTION, applicationContextModule);
类似Spring依赖注入,使用轻量级谷歌的Guice进行依赖注入,关于Guice的使用可以参考以下文章
https://blog.csdn.net/cnhome/article/details/80627123
期间会调用插件的Plugin类下的setup方法,调用过程如下:
-->PluginContextLoadResult pluginContextLoadResult = this.pluginContextLoadResultProvider.get();//ApplicationServerTypeProvider类下的get方法
--> return new DefaultPluginContextLoadResult(profilerConfig, dynamicTransformTrigger, instrumentEngine, pluginJars); //PluginContextLoadResultProvider类
-->this.setupResultList = load(); //DefaultPluginContextLoadResult类
--> List<SetupResult> load = loader.load(pluginJars);
//ProfilerPluginLoader类
public List<SetupResult> load(URL[] pluginJars) {
List<SetupResult> pluginContexts = new ArrayList<SetupResult>(pluginJars.length);
for (URL pluginJar : pluginJars) {
final JarFile pluginJarFile = createJarFile(pluginJar);
final List<String> pluginPackageList = getPluginPackage(pluginJarFile);
final ClassNameFilter pluginFilterChain = createPluginFilterChain(pluginPackageList);
final List<ProfilerPlugin> original = PluginLoader.load(ProfilerPlugin.class, new URL[] { pluginJar });
List<ProfilerPlugin> plugins = filterDisablePlugin(original);
for (ProfilerPlugin plugin : plugins) {
if (logger.isInfoEnabled()) {
logger.info("{} Plugin {}:{}", plugin.getClass(), PluginConfig.PINPOINT_PLUGIN_PACKAGE, pluginPackageList);
}
logger.info("Loading plugin:{} pluginPackage:{}", plugin.getClass().getName(), plugin);
PluginConfig pluginConfig = new PluginConfig(pluginJar, pluginFilterChain);
final ClassInjector classInjector = new JarProfilerPluginClassInjector(pluginConfig, instrumentEngine);
final SetupResult result = pluginSetup.setupPlugin(plugin, classInjector);
pluginContexts.add(result);
}
}
return pluginContexts;
}
->final SetupResult result = pluginSetup.setupPlugin(plugin, classInjector);
->profilerPlugin.setup(guardSetupContext); //调用setup方法
如tomcat则调用:
@Override
public void setup(ProfilerPluginSetupContext context) {
final TomcatConfig config = new TomcatConfig(context.getConfig());
if (logger.isInfoEnabled()) {
logger.info("TomcatPlugin config:{}", config);
}
if (!config.isTomcatEnable()) {
logger.info("TomcatPlugin disabled");
return;
}
TomcatDetector tomcatDetector = new TomcatDetector(config.getTomcatBootstrapMains());
context.addApplicationTypeDetector(tomcatDetector);
if (shouldAddTransformers(config)) { //判断是否需要添加transformers
logger.info("Adding Tomcat transformers");
addTransformers(config); //添加transformers
} else {
logger.info("Not adding Tomcat transfomers");
}
}
private void addTransformers(TomcatConfig config) {
if (config.isTomcatHidePinpointHeader()) {
addRequestFacadeEditor();
}
addRequestEditor();
addStandardHostValveEditor();
addStandardServiceEditor();
addTomcatConnectorEditor();
addWebappLoaderEditor();
addAsyncContextImpl();
}
private void addRequestEditor() {
transformTemplate.transform("org.apache.catalina.connector.Request", new TransformCallback() {
@Override
public byte[] doInTransform(Instrumentor instrumentor, ClassLoader classLoader, String className, Class<?> classBeingRedefined, ProtectionDomain protectionDomain, byte[] classfileBuffer) throws InstrumentException {
InstrumentClass target = instrumentor.getInstrumentClass(classLoader, className, classfileBuffer);
target.addField(TomcatConstants.TRACE_ACCESSOR);
target.addField(TomcatConstants.ASYNC_ACCESSOR);
// clear request.
InstrumentMethod recycleMethodEditorBuilder = target.getDeclaredMethod("recycle");
if (recycleMethodEditorBuilder != null) {
recycleMethodEditorBuilder.addInterceptor("com.navercorp.pinpoint.plugin.tomcat.interceptor.RequestRecycleInterceptor"); //添加拦截器
}
// trace asynchronous process.
InstrumentMethod startAsyncMethodEditor = target.getDeclaredMethod("startAsync", "javax.servlet.ServletRequest", "javax.servlet.ServletResponse");
if (startAsyncMethodEditor != null) {
startAsyncMethodEditor.addInterceptor("com.navercorp.pinpoint.plugin.tomcat.interceptor.RequestStartAsyncInterceptor"); //添加拦截器
}
return target.toBytecode();
}
});
}
主要通过transform添加拦截器修改类的字节码,已达到监控的目的。
7.DefaultAgent的start函数(Running),主要是定时发送相关信息。
pinpointAgent.start();
--->this.applicationContext.start();
-> this.deadlockMonitor.start();
this.agentInfoSender.start();
this.agentStatMonitor.start();
->scheduler.start();
->schedule(successListener, Integer.MAX_VALUE, IMMEDIATE, sendIntervalMs);
->AgentInfoSendTask task = new AgentInfoSendTask(successListener, retryCount);
timer.scheduleAtFixedRate(task, delay, period);
->boolean isSuccessful = sendAgentInfo();
->dataSender.request(agentInfo, new AgentInfoSenderListener(future)); //定时发送agentInfo
今天就写这么多,下次再接着研究。