英文原文:https://naver.github.io/pinpoint/1.8.4/techdetail.html
说明:【】中内容为方便解释自己加的
在这篇文章中,我们描述了Pinpoint的技术细节比如请求追踪(transaction tracing)和字节码插装(bytecode instrumentation),同时说明了应用于Pinpoint agent中的优化方法,它可以修改字节码并记录性能数据。
分布式事务追踪,基于Google的Dapper
基于Google的Dapper,Pinpoint可以追踪到一个事务(transaction)中的分布式请求【这里说白了即指Pinpoint能够追踪到从应用A到应用B的一次分布式请求中的所有数据】
Google Dapper 中分布式事务追踪是如何工作的
分布式追踪系统的目的就是对于分布式系统中的两个节点Node1和Node2,当有一条消息从Node1发送到Node2时,分布式追踪系统能够识别Node1和Node2之间的关系(如图1所示)
图1. 分布式系统中的消息关系
问题在于没有方法能够识别消息之间的关系,比如我们无法辨别从Node1发送的N条消息和Node2接收到的N条消息之间的关系,换句话说,当从Node1发送第X条消息时,你无法在Node2收到的N条消息里分辨出Node1发送的第X条消息。有系统尝试在操作系统或者TCP级别追踪这些消息,然而实现复杂度高,同时性能低,因为需要针对每个协议单独实现。此外,很难准确地跟踪消息。
Google的Dapper提供了一种解决上述问题的简单方法。方案便是当发送消息的时候,在应用级别给这些消息加上标签,从而能够使这些消息关联起来。例如,对于Http请求,它在Http请求头中加入标签信息,并使用这个标签信息追踪消息。
可以查看论文了解Dapper :https://ai.google/research/pubs/pub36356
Pinpoint 基于Google的Dapper中的追踪技术,同时进行了修改,在远程调用的时候,pinpoint会在调用方加入应用级别的标签数据来追踪分布式事务(请求),标签数据由一系列的key组成,在pinpoint中定义为 TraceId
Pinpoint中的数据结构
在pinpoint中,核心的数据结构包括:Span
s,Trace
s 以及 TraceId
s
Span
有子节点,用SpanEvent
【这个就相当于一次方法调用,故一个Span
可能大部分都会有多个SpanEvent
】表示,每个Span
都包含一个TraceId
,每个Span
有一个SpanId
和ParentSpanId
,如果这个Span
为请求的最原始的发起者,ParentSpanId
为-1。Trace
: Span
的集合,由关联的RPC调用组成【这个也就是说在分布式系统中,每个应用代表一个Span
,不同应用的一次全链路的请求表示一个Trace
,即一个Trace会包含多个Span
】,在同一个链路中的Span
s共享同一个TransactionId
,Trace
通过SpanId
s和ParentSpanId
s排序成分层树结构。TraceId
: 一系列key的集合,这些key包括:TransactionId
,SpanId
和ParentSpanId
,TransactionId
代表消息的id,这个id在一次追踪过程中唯一,SpanId
和ParentSpanId
代表远程调用的父子关系。TransactionId
(TxId): 一次事务(请求)中分布式系统发送/接收的消息id,在整个请求关联的所有应用服务中必须唯一SpanId
: 处理接收RPC消息的应用id,在RPC消息到达某个节点时生成ParentSpanId(pSpanId)
: 发起RPC调用的父span的 SpandId
,如果某个节点时整个事务请求的发起者,那么它没有父span,对于这种情况,我们使用 -1
表示这是整个事务请求的根spanGoogle的Dapper和NAVER的Pinpoint的术语的不同处
Pinpoint中的术语 TransactionId
和Google的Dapper中的术语TraceId
意思相同,TraceId
在Pinpoint中表示的是一系列key的集合【前面提到的TransactionId
,SpanId
等】
TraceId如何工作?
下图展示的是TraceId的行为,里面包括了三次RPC请求以及4个节点【这个就相当于一次请求,即一次transaction】
图2. TraceId行为样例
在图2中,TransactionId(TxId)表示的是三次不同的调用通过TransactionId(TxId)关联在一起作为一次事务请求(transaction),然而,TransactionId自身并不能明显的描述RPC之间的关系。为了识别RPC之间的关系,我们需要SpanId以及ParentSpanId(pSpanId)。假设节点是tomcat,你可以想象成 SpanId是处理http请求的线程,parentSpanId代表的是发起这次RPC请求的SpanId【这里假设应用A和B,A调B,A和B都是一个Node,同时A是SpanIdA,B是SpanIdB,因为是A调B,那么B的parentSpanId就是SpanIdA了】
Pinpoint通过TransactionId查找关联的多个Span,同时根据SpanId和ParentSpanId对他们进行层次关系排序。
SpanId和ParentSpanId都是64位的长整型,由于数字是随机产生的,因此可能会有冲突,但是考虑到64位长整型的范围是 -9223372036854775808到9223372036854775807,冲突发生的几率很小。如果真的产生冲突,Pinpoint和Google的Dapper一样,会让开发者知道发生的情况而不是解决冲突。
TransactionId由 agentId,JVM启动时间以及一个序列号组成
Dapper和Zipkin(Twitter的一个分布式追踪平台),随机生成TraceIds(对应Pinpoint中的TransactionIds),认为id冲突是很常见场景。但是,在Pinpoint中,我们努力避免这种冲突,有两个选择:一是id中的数量量小但是冲突的可能性很高;另外一种是id的数据量大但是冲突的可能性低,Pinpoint选择的是后一种【也就是说pinpoint为了使TransactionId的冲突减少,TransactionId的数据会大点】
可能有更好的方法来解决这个问题,我们提出几个想法,比如通过一个中心key服务器来产生key【这里指的应该就是前面的TransactionId】,但是由于性能和网络问题没有实现。目前我们仍然在考虑批量产生key作为替代解决方案。因此在将来,可能会开发出这样的方法,但就目前而言,我们采用了一种简单的方法。在Pinpoint中,TransactionId是可变的数据。
字节码注入,无需修改代码
前面我们解释了分布式事务追踪。一种方法是让开发者修改自己的代码,当发生RPC调用的时候允许开发者增加标记信息,然后修改代码负担比较重。
Twitter的Zipkin使用修改后的类库以及容器(Finagle)来提供分布式追踪的功能,然后它同样需要开发者修改代码,我们想要的是不修改代码就能实现追踪功能,同时提供代码级别的可见性,Pinpoint使用了字节码注入技术(bytecode instrumentation),Pinpoint的Agent干预(拦截)调用RPC的代码以便自动处理标记信息。
克服字节码注入的缺点
如下所说,有两种方式实现分布式追踪。字节码注入是一种自动的方式
方法 | 优点 | 缺点 |
---|---|---|
手动追踪 | 需要较少的开发资源;API可以变得简单因此bug也少 | 开发者需要修改代码;跟踪级别低【这里的跟踪级别高低怎么理解?】 |
自动追踪 | 开发者无需修改代码;可以收集到更精确的数据 | 需要高素质的开发人员可以快速的识别哪些代码需要跟踪,以及在哪些跟踪点需要处理;由于使用了字节码注入等高级开发技巧,bug也会多 |
字节码注入技术会引入更高的难度和风险,但是有很多益处。
尽管字节码注入需要很多的开发资源,但是部署应用的时候几乎不需要(即开发难,部署运行简单)
【中间一段无关痛痒的分析字节码注入方式的好处就不翻译了】
字节码注入的价值
我们使用字节码注入(自动方法)来实现除了上面提到的还有下面一些理由
通过在JVM启动脚本中添加以下三行(与Pinpoint Agent的配置相关联),可以轻松地为应用程序启用Pinpoint:
-javaagent:$AGENT_PATH/pinpoint-bootstrap-$VERSION.jar
-Dpinpoint.agentId=
-Dpinpoint.applicationName=
如果因为Pinpoint导致什么问题,只需要删除启动参数里的配置
字节码注入是如何工作
由于字节码注入需要处理Java字节码,往往会增加开发风险同时降低开发效率,此外,开发人员容易犯错误。在Pinpoint中,我们通过抽象出拦截器提高开发效率和可访问性,Pinpoint在类加载时干预应用程序代码,注入必要的代码来跟踪分布式请求以及性能信息,由于跟踪代码直接注入应用程序代码,因此这提高了性能。
图3. 字节码注入的基本原理
在Pinpoint中,API拦截部分和数据记录部分是分开的。 拦截器被注入到我们想要跟踪的方法中,并调用before()和after()方法来处理数据记录。 通过字节码检测,Pinpoint Agent只从必要的方法记录数据,这使得分析数据的大小变得紧凑。
优化Pinpoint Agent的性能
下面我们描述下如何优化Pinpoint agent的性能
Base 128 Varints
来了解更多关于可变长度编码Pinpoint应用样例
下面是如何在你的应用程序中获取数据的例子,这样你能全面理解上面描述的内容。
图5展示当你的TomcatA和TomcatB安装了Pinpoint你可以看到什么,你可以看到单个节点作为单个请求事务的追踪数据,表示的是分布式事务追踪的流
图5 实践中的pinpoint样例
下面描述了Pinpoint在每个方法中做的事情
总结
Pinpoint是另一个与你的应用程序一起运行的应用程序。 使用字节码检测使Pinpoint看起来像是不需要修改代码。 通常,字节码检测技术使应用程序容易受到风险的影响; 如果Pinpoint出现问题,它也会影响你的应用程序。 但就目前而言,我们并没有摆脱这些威胁,而是专注于提高Pinpoint的性能和设计。 因为我们认为这使得Pinpoint更有价值。 因此,是否使用Pinpoint是由你决定的。
我们仍然需要做大量的工作来改进Pinpoint。 尽管Pinpoint不完整,但它作为一个开源项目被发布了; 我们不断努力开发和改进Pinpoint,以满足你的期望