一、Pinpoint 是什么
Pinpoint 是一款全链路分析工具,提供了无侵入式的调用链监控、方法执行详情查看、应用状态信息监控等功能。基于Google Dapper论文进行的实现。
核心思想就是在服务各节点彼此调用的时候,记录并传递一个应用级别的标记,这个标记可以用来关联各个服务节点之间的关系。
比如两个节点之间使用 HTTP 作为请求协议的话,那么这些标记就会被加入到HTTP头中,各应用的Agent在进行上报的时候,将该标记以及对应的上下级应用上报到Pinpoint collector中,通过该标记标识请求,并将各个应用串联成完整的调用链路。
Pinpoint的特点如下:
分布式事务跟踪,跟踪跨分布式应用的消息;
自动检测应用拓扑,帮助你搞清楚应用的架构;
水平扩展以便支持大规模服务器集群;
提供代码级别的可见性以便轻松定位失败点和瓶颈;
使用字节码增强技术,添加新功能而无需修改代码。
Pinpoint针对不同的组件提供了丰富的插件,其支持以下模块:
JDK 6+
Tomcat 6/7/8, Jetty 8/9, JBoss EAP 6, Resin 4, Websphere 6/7/8, Vertx 3.3/3.4/3.5
Spring, Spring Boot (Embedded Tomcat, Jetty)
Apache HTTP Client 3.x/4.x, JDK HttpConnector, GoogleHttpClient, OkHttpClient, NingAsyncHttpClient
Thrift Client, Thrift Service, DUBBO PROVIDER, DUBBO CONSUMER
MySQL, Oracle, MSSQL, CUBRID,POSTGRESQL, MARIA
Arcus, Memcached, Redis, CASSANDRA
iBATIS, MyBatis
DBCP, DBCP2, HIKARICP
gson, Jackson, Json Lib
log4j, Logback
二、插件知识和相关的数据结构
pinpoint插件能够在代码级别对感兴趣的方法进行拦截,可以针对业务代码,第三方包等,如记录方法的执行时间,参数,方法返回结果,在RPC调用中插入标识ID以记录调用关系等, pinpoint插件很容易扩展,官方也提供了很多插件,基本覆盖了常用的组件,如hystrix,dubbo等,部署即可用。
1、插件结构
Pinpoint 插件由TraceMetadataProvider 和 ProfilerPlugin 的实现组成。TraceMetadataProvider 实现给 pinpoint agent, collector, web组件提供 ServiceType 和 AnnotationKey,ProfilerPlugin 实现用于agent转换目标类以记录追踪数据。
Pinpoint 插件以 jar 文件的形式部署。Agent 在 plugin 目录下用 ServiceLoader 搜索TraceMetadataProvider 和 ProfilerPlugin 的实现,web 和 collector 在 WEB-INF/lib目录下搜索,ServiceLoader 要求 provider 配置文件存在于 META-INF/services 目录下,所以在 plugin jar 中必须放置以下文件,实现类通过 Java 的服务发现机制进行加载。
2. 介绍下几种关键的类
2.1 TraceMetadataProvider
TraceMetadataProvider 实现类中提供 ServiceTypes 和 AnnotationKeys。
2.1.1 ServiceTypes
每个 Span 和 SpanEvent 都包含一个 ServiceType,这个ServiceType表示跟踪方法所属的库,以及跟踪它的Span和spanevent应该如何处理。
下表显示ServiceType包含哪些属性:
Pinpoint 为了尽量压缩 Agent 到 Collector 数据包的大小,ServiceType 被设计成不是以序列化字符串的形式发送的,而是以整形数字发送的 (code 字段),这就需要建立一个映射关系,将 code 转换成对应的 ServiceType 实例。
这个映射机制就是由 TraceMetadataProvider 负责的。
ServiceType code必须是惟一的。如果要编写一个将被公开共享的插件,就必须联系pinpoint团队来获得分配的ServiceType code。
如果所开发的插件是私有的,那可以从下面的表格中选择一个ServiceType code,如一会我们要展示的示例中一样。
公开的ServiceType Code范围:
私有的ServiceType Code范围:
ServiceType 还有一些属性:
2.1.2、AnnotationKey
Annotation 是包含在 Span 和 SpanEvent 中的更详尽的数据,以键值对的形式存在,键就是 AnnotationKey,值是基本类型,String或者byte[]。
Pinpoint 内置了很多的 AnnotationKey,如果不够用的话也可以通过 TraceMetadataProvider 来自定义。AnnotationKey 的数据结构如下:
2.2、ProfilerPlugin
ProfilerPlugin修改目标库的类来收集跟踪数据。插件的工作原理:
Pinpoint Agent 随 JVM 一起启动;
Agent 加载所有 plugin 目录下的插件;
Agent 调用已加载的插件的; ProfilerPlugin.setup(ProfilerPluginSetupContext) 方法;
在 setup 方法中,插件定义那些需要被转换的类并注册回调TransformerCallback;
目标应用启动;
每当类被加载的时候,Pinpoint Agent 会寻找注册到该类的回调 TransformerCallback;
如果 TransformerCallback 被注册,Agent; 就调用它的 doInTransform 方法;
TransformerCallback 修改目标类的字节码 (例如添加拦截器、字段等);
修改后的代码返回到; JVM,类被加载的时候使用修改后的字节码;
应用程序继续;
当调用到被修改的方法的时候,已注入的拦截器的 before 和 after 方法会被调用;
拦截器记录追踪数据.
最重要的几点可以归结为:1)找出哪些方法值得跟踪。2)注入拦截器来实际跟踪这些方法。
这些拦截器用于提取、存储和传递跟踪数据,然后将其发送给收集器。拦截器甚至可以相互协作,在它们之间共享上下文。
插件还可以通过向目标类添加getter或定制字段来帮助跟踪,以便拦截器在执行期间可以访问它们。
三、字节码注入怎么工作的
由于字节码技术必须处理java字节码,它会增加开发的风险而降低生产效率。此外,研发人员容易犯错误。在Pinpoint中,通过拦截器抽象提高了生产力和可访问性。
Pinpoint中注入必要的代码,通过在类装入时插入应用程序代码来跟踪分布式事务和性能信息。由于跟踪代码直接注入到应用程序代码中,因此提高了性能。
在 Pinpoint中,API截取和数据记录是分离的。如上图,拦截器被注入到我们希望跟踪的方法中,在该方法前后调用before()和after()处理数据记录。
通过字节码指令, Pinpoint Agent可以仅从必要的方法记录数据,从而使分析数据的大小变得紧凑。
下面就根据以上原理来实现一个插件,该插件能够拦截配置的方法。
先定义些常量类型, 设置ServiceType 和 相应的code,AnnotationKey 和相应的code:
定义数据类型类用于读取配置文件中的配置,文件中的配置规则为:
有参数方法package.Clazz.MethodArgs=arg1,arg2
无参数方法package.Clazz.MethodArgs
每行一个配置项:
定义 GeneralConfigMetadataProvider 提供 ServiceType 元数据:
定义针对方法的拦截器 GeneralConfigInterceptor 继承自 SpanEventSimpleAroundInterceptorForPlugin,这里在方法执行后简单记录方法的名称,参数,返回值
最后重要的是我们的插件,传入配置和转换模板 transformTemplate:
META-INF/services目录下添加插件和元数据文件:
com.navercorp.pinpoint.plugin.ProfilerPlugin --com.navercorp.pinpoint.plugin.general.config.GeneralConfigPlugin
com.navercorp.pinpoint.common.trace.TraceMetadataProvider --com.navercorp.pinpoint.plugin.general.config.GeneralConfigMetadataProvider
当以上核心的代码写完后,将插件以jar的形式部署在准备好的 agent 目录中,启动项目,在 project.propertie 中配置要拦截的方法如 com.bestpay.middleware.service.StudentManagerImpl.getAllStudents。在请求的路径中能看到以下的信息。配置我们配置的方法被拦截到了。
至此,我们的小插件讲解就结束了,此为抛砖引玉,Pinpoint 提供了丰富的插件开发 API,如拦截异步方法、调用链跟踪、拦截器之间共享数据等,有兴趣的同学可进一步探索。