内容简介:对原生的pinpoint进行了一些二次开发,大致流程如下pinpoint默认数据collector是用的netty方式,agent是netty client端,进行抽样采集,发送数据到netty server端collector,collector在进行处理写入到hbase,包括agent本身的一些信息,内存,线程信息还有trace信息,但是这样性能就会损失很多,如果100%采集的话,会对应用和服务器都会造成很大负荷是,所有需要进行改造
架构
对原生的pinpoint进行了一些二次开发,大致流程如下
agent 收集数据
pinpoint默认数据collector是用的netty方式,agent是netty client端,进行抽样采集,发送数据到netty server端collector,collector在进行处理写入到hbase,包括agent本身的一些信息,内存,线程信息还有trace信息,但是这样性能就会损失很多,如果100%采集的话,会对应用和服务器都会造成很大负荷是,所有需要进行改造
我们将agent的netty通信方式改成了打日志的形式,在pinpoint spi获取对应实例的时候写死数据发送方式, 比如ApiMetaDataServiceProvider这个povider,判断config是storm的方式,直接使用了自定义的dataSender
@Override public ApiMetaDataService get() { final EnhancedDataSender enhancedDataSender; if (PinpointConstants.AGENT_SENDER_TYPE_LOGGING.equals(profilerConfig.getAgentSenderType())) { enhancedDataSender = new StormEnhanceLoggingDataSender(); } else { enhancedDataSender = this.enhancedDataSenderProvider.get(); } return new DefaultApiMetaDataService(agentId, agentStartTime, enhancedDataSender); }
agent加载初始化
核心类入口是PinpointBootStrap的premain方法,这个是agent的标准
每个应用会配置pinpoint的一些启动参数,例如agentId和日志位置,之后在启动过程中会加载bootstrap-*.jar的这些jar包,还有通过日志目录进行截取,获取到插件的对应路径,加载插件的所有jar,并添加到bootstrap的classloader中,并通过spi的方式加载插件META-INF中配置的对应的类,加载其中的setup方法,将一些常量放到上下文中去,数据都加载到了TraceMetadataLoader中的annotationKeys和serviceTypeInfos这两个list集合中
java -Dpinpoint.log=/Users/walis/work/dwb/dapoint/logs -Dpinpoint.agentId=rider-fund-service -Dpinpoint.applicationName=rider-fund-service -javaagent:/Users/walis/work/dwb/dapoint/agent/target/pinpoint-agent-1.7.2-1.0.0-SNAPSHOT/pinpoint-bootstrap-1.7.2-1.0.0-SNAPSHOT.jar -jar rider-service.jar
对象实例化
pinpoint这货使用了很多spi和ioc方式,guice,spring ioc,类似serviceLoader的方式,有点多,很多重要的类都在spi中进行初始化了, 不去细看的话,很多类什么时候初始化还真不好找。
-
guice
pinpoint-profile模块中的 ApplicationContextModule 这个类中加载大多的核心的provider
``` configure(){
bindTraceComponent() bindDataTransferComponent() bindServiceComponent() bindAgentInformation() bindAgentStatComponent()
... } ```
- spring ioc
主要是collector模块中的applicationContext-collector.xml, 这边是collector接收到agent发送的数据进行处理一些核心类,包括hbase的存储 例如
核心类DefaultAgent启动
agent的start方法调用了DeafultApplicationContext,这个context类加载所有guice module生成,然后获取重要的核心类,比如AgentInfoSender和AgentStatMonitor,然后调用这两个类的start方法,启动agent数据打点
* AgentInfoSender
AgentInfoSender内部会创建一个schedule,定时发送agent的信息,agent的信息主要是每个应用的id,ip,名称,端口,进程号,vm版本,agent版本, jvm信息的话都是从RuntimeMxBean获取的
* AgentStatsMonitor
agentStatusMonitor收集各种agent相关的信息,比如cpu,jvm内存,trace响应时间等数据,AgentStatMetricCollector接口对应很多实现类,一部分调用的是metric包下面的包,像内存,cpu这些数据都是从RuntimeMBean中去拿的数据,
trace 过程
trace的过程主要涉及到traceId,span和spanEvent,接着就是spansender
traceId默认生成方式是agentId + agent启动时间 + 自增的id
一次调用是由很多span组成,最终形成一个id它们公用一个traceId
SpanRecorder 添加跟span相关联的spanEvent,包括服务的ip,参数信息,类型,返回值或者异常等等
trace的大致过程类似如下:
createTrace -> currentTraceObject -> traceBlockBegin -> traceBlockEnd -> close
close结束后,就会调用对应sender将span信息进行打点或者发送给对应的collector
字节码织入
plugin结构
-
ProfilerPlugin
每个插件需要实现ProfilerPlugin, TransformTemplateAware接口,提供设置servicetype,annotationKey
覆盖setup方法,设置需要处理的class,对特定拦截对象比如方法,添加拦截器,进行字节码植入,这个调用了ASMMethod的addScopedInterceptor方法,有兴趣的可以去看下,底层还是到asm的insertBefore方法
例如
``` for (InstrumentMethod m : target.getDeclaredMethods(MethodFilters.name("fromJson"))) {
m.addScopedInterceptor("com.navercorp.pinpoint.plugin.gson.interceptor.FromJsonInterceptor", GSON_SCOPE);
}
```
-
TraceMetadataProvider 每个插件需要实现TraceMetadataProvider,覆盖setup方法,设置serviceTye和annotationKey
-
Config 因为插件不一定开启,还要附件的其他一些自定义的配置项,这些都是放到pinpoint.config中去配置的
-
Intercepter
实现AroundInterceptor1接口,在before方法和after后植入代码,进行trace操作
获取字节码处理引擎
InstrumentEngineProvider, 默认使用的是asm, 生成ASMEngine类,getClass方法返回
ASMClass,这个类中的所有方法是对asm的封装,便于更方便使用的,每个ProfilerPlugin实现类都有会实现TransformCallback接口,最终调用的的都是ASMClass的方法
ProfilePlugin加载
PluginContextLoadResultProvider在创建DefaultPluginContextLoadResult实例的时候,会找到所有plugin的jar,加载实现ProfilerPlugin接口,调用其setUp方法和设置
jdk加载注册
按照jdk标准加载agent里面的代码的话需要实现ClassFileTransformer接口,实现transform方法,然后在jvm应用启动,然后在类的字节码载入jvm前会调用,这个方法,从而实现修改原来方法的功能
pinpoint实现了是通过ClassFileTransformerDispatcher这个最上层的类,TransformTemplate的transform方法会将抽象出来的模板类TransformCallback,ClassFileTransformerLoader会将将这个callback包装为MatchableClassFileTransformerGuardDelegate(同样实现了jdk标准的ClassFileTransformer接口)然后,添加到自己的一个内部集合中去,之后ClassFileTransformerDispatcher会获取这个集合做后续处理。
数据发送
核心数据结构
pinpoint涉及到的数据结构体都在thrift module下面,主要是Pinpoint.thift和Trace.thrift这两个文件
主要有TJvmInfo,TAgentInfo,TJvmGc,TJvmGcDetailed,TCpuLoad,TTransaction,TActiveTraceHistogram,TActiveTrace,TResponseTime,TAgentStat,TAgentStatBatch,TSpan,TSpanChunk,TApiMetaData等
发送实现
数据离不开agent和span,所有的sender都围绕这几个核心
数据发送调用provider产生的这些dataSender,以下几个3个核心的sender类的provider,由guice进行实例化
- SpanDataSenderProvider (span采集打点)
- StatDataSenderProvider (agent状态信息采集打点)
- AgentInfoSenderProvider (agent的信息采集打点)
数据聚合
所有sender打印的日志都写在pinpoint.log文件下,具体文件的位置是jar包启动的环境变量, 然后,运维会通过firebeats将日志进行实时搜集,发送到kafka, 由jstorm的topology消费,在将数据写入到hbase,自己基于pinpoint的tcp的receiver进行改造,数据写入到hbase
数据存储
数据存储包含agent的和span的数据,直接写入到hbase的,没有使用pheonix,使用的是pinpoint自己封装的 工具 进行操作,原生的hbase的增删改查可读性不是很好的,没有标准的 sql 语法来的自然清晰。
为了能通过一些查询条件和服务名称和调用的方法查询,在stom处理的时候,将这些信息写入到es,只保留近7天的数据,像这种多条件查询的话还是es性能好点.
原有界面进行二次开发,加上这些条件,然后通过agentId,spanId,transactionId去查询链路信息, transactionId可以拿到所有相关的span,然后对所有的span根据关系串成一个属性结构的CallTreeNode
public class CallTreeNode { private CallTreeNode parent; private CallTreeNode child; private CallTreeNode sibling; private SpanAlign value; public CallTreeNode(final CallTreeNode parent, SpanAlign value) { this.parent = parent; this.value = value; }
以上所述就是小编给大家介绍的《pinpoint 使用和相关源码解析》,希望对大家有所帮助,如果大家有任何疑问请给我留言,小编会及时回复大家的。在此也非常感谢大家对 码农网 的支持!
猜你喜欢:- ReactNative源码解析-初识源码
- Spring源码系列:BeanDefinition源码解析
- Spring源码分析:AOP源码解析(下篇)
- Spring源码分析:AOP源码解析(上篇)
- 注册中心 Eureka 源码解析 —— EndPoint 与 解析器
- 新一代Json解析库Moshi源码解析
本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们。
Perl语言编程
[美] Larry Wall、Tom Christiansen、Jon Orwant / 何伟平 / 中国电力出版社 / 2001-12 / 129.00元
这不仅仅是一本关于Perl的书籍,更是一本独一无二的开发者自己介绍该语言及其文化的书籍。Larry Wall是Perl的开发者,他就这种语言的未来发展方向提出了自己的看法。Tom Christiansen是最早的几个拥护者之一,也是少数几个在错综复杂的中游刃有余的人之一。Jon Orwant是《Perl Journal》的主编,该杂志把Perl社区组合成了一个共同的论坛,以进行Perl新的开发。一起来看看 《Perl语言编程》 这本书的介绍吧!