一、APM(应用性能管理)
1.1 什么是APM?
APM (Application Performance Management) 即应用性能管理(应用性能监控)
APM主要是针对企业 关键业务的IT应用性能和用户体验的监测、优化,提高企业IT应用的可靠性和质量。
旨在确保最终用户获得高质量的体验,降低IT总拥有成本(TCO)
TCO (Total Cost of Ownership ),即总拥有成本,包括产品采购到后期使用、维护的成本。 这是一种公司经常采用的技术评价标准。
1.2 APM介绍
目前市面的系统基本都是参考 Google 的 Dapper(大规模分布式系统的跟踪系统)来做的。
跟踪业务请求的处理过程,完成对应用系统在前后端处理、服务端调用的性能消耗跟踪,提供可视化的界面来展示对跟踪数据的分析。
通过汇聚业务系统各处理环节的实时数据,分析业务系统各事务处理的交易路径和处理时间,实现对应用的全链路性能监测。
APM工具与传统的性能监控工具的区别在于,不仅仅提供一些零散的资源监控点和指标,其主要关注在系统内部执行、系统间调用的性能瓶颈分析,这样更有利于定位到问题的具体原因。
APM致力于检测和诊断应用性能问题,从而能提供应用预期的服务水平。
1.2.1 APM三大特征
- 多级应用性能监控:覆盖通讯协议1-7层,通过事务处理过程监控、模拟等手段实现端到端应用监测。
- 应用性能故障快速定位:对应用系统各个组件进行监测,迅速定位系统故障,并进行修复或提出修复建议。
- 应用性能全面优化:精确分析各组件占用系统资源的情况,并根据应用系统性能要求给出专家建议。
1.2.2 APM的发展历程
目前APM的发展主要经历了前面的三个阶段:
第一阶段:以网络监控基础设施为主,主要监控主机 的CPU 使用率、I/O、内存资源、网速等,主要以各类网络管理系统(NMS)和各种系统监控工具为代表。
第二阶段:以监控各种基础组件为主,随着互联网的快速发展,为了降低应用开发难度,各种基础组件(如数据库、中间件等)开始大量涌现,所以这个时期应用性能管理主要是监控和管理各种基础组件的性能。
第三阶段:以监控应用本身的性能为主, IT 运维管理的复杂度开始出现爆炸性的增长,应用性能管理的重点也开始聚焦于应用本身的性能与管理上。
第四节阶段属于正在发展的阶段:
云计算方兴未艾,而DevOps以及微服务的兴起对传统APM产生了很大的冲击,传统厂商也在做一些革新,也做一些微服务方面的尝试和云计算方面的尝试。
随着Machine Learning、AI的技术的兴起,对定位故障、定位问题,也会起到一些帮助,基于大数据的分析的手段也会有一些帮助,目前市场上正在初步尝试阶段。
2016年Gartner对APM的定义分为三个维度
- DEM-Digital experience monitoring:数字体验监控,浏览器及移动设备用户体验监控及利用主动拨测的实现的业务可用性及性能监控。
- ADTD-Application discovery, tracing and diagnostics:应用自动发现、追踪和故障诊断,自动发现应用之间的逻辑关系,自动建模、应用组件的深入监控及性能关联分析。
- AA-Application analytics:应用分析,通过机器学习,进行针对JAVA及.NET等应用的根源分析。
1.2.3 DevOps
DevOps(Development和Operations的组合词)是一种重视“软件开发人员(Dev)”和“IT运维技术人员(Ops)”之间沟通合作的文化、运动或惯例。
DevOps可以简洁的理解为“开发团队与运营团队之间更具协作性、更高效的关系”。
是两个相关趋势碰撞中出现的新术语
更多信息见:https://zh.wikipedia.org/wiki/DevOps
1.2.4 APM 的核心思想
在应用服务各节点相互调用的时候,从中记录并传递一个应用级别的标记,这个标记可以用来关联各个服务节点之间的关系。
比如两个应用服务节点之间使用 HTTP 作为传输协议的话,那么这些标记就会被加入到 HTTP 头中。
可见如何传递这些标记是与应用服务节点之间使用的通讯协议有关的,常用的协议就相对容易加入这些内容,一些按需定制的可能就相对困难些,这一点也直接决定了实现分布式追踪系统的难度。
1.2.5 为什么要使用APM
随着公司业务的与日俱增,各个系统也越来越复杂,服务间的调用,服务的依赖,以及分析服务的性能问题也越棘手,因此引入服务追踪系统尤为重要。
现有的APM,基本都是参考Google的Dapper的体系来做的。通过跟踪请求的处理过程,来对应用系统在前后端处理、服务端调用的性能消耗进行跟踪(每个请求的完整调用链路,收集调用链路上每个服务的性能数据),方便工程师能够快速定位问题。
1.2.6 好的APM应满足的条件
总的来说,一个优秀的APM系统应该满足以下五个条件
-
低消耗,高效率:被跟踪的系统为跟踪所付出的系统资源代价要尽量小,现在主流的APM对于系统资源的消耗在2.5%-5%左右,但是这个数值应该越小越好,因为在大规模的分布式系统下,一个单节点的资源是无法把控的,可能是超强配置,也可能是老爷机,只跑几个小服务,但是本身性能已经十分吃紧了,如果这时候跟踪应用再一跑,很可能这个节点就挂掉了,得不偿失。
-
低侵入性,足够透明:作为跟踪系统,侵入性是不可能不存在的,关键这种侵入性要在哪个层面,如何在越底层的层面上侵入,对于开发者的感知和需要配合跟踪系统的工作就越少,如果在代码层面就需要进行侵入,那对于本身业务就比较复杂的应用来说,代码就更加冗余复杂了,也不利于开发者快节奏的开发。
-
灵活的延展性:不能随着微服务和集群规模的扩大而使分布式跟踪系统瘫痪,要能够充分考虑到未来分布式服务的规模,跟踪系统至少要在未来几年内完全吃得消。
-
跟踪数据可视化和迅速反馈:要有可视化的监控界面,从跟踪数据收集、处理到结果的展现尽量做到快速,就可以对系统的异常状况作出快速的反应
-
持续的监控: 要求分布式跟踪系统必须是7x24小时工作的,否则将难以定位到系统偶尔抖动的行为
2、Dapper的介绍以及基本原理
Dapper–Google生产环境下的分布式跟踪系统。
Dapper的英文论文:http://static.googleusercontent.com/media/research.google.com/zh-CN//pubs/archive/36356.pdf
中文论文:https://bigbully.github.io/Dapper-translation/ (建议一定要看下)
分布式调用跟踪系统实际上是随着微服务才火起来的一个概念,Google早在很多年前(2000年初便已经使用自研容器)已经微服务化了,所以他的分布式跟踪理论目前是最成熟的。
分布式跟踪系统出现的原因简单的说是因为在分布式系统中一次请求中会包含很多的RPC,迫切需要一些可以帮助理解系统行为和分析性能问题的工具,需要断定具体哪个服务拖了后腿。
根据论文来看,先看一个例子:
A~E分别表示五个服务,用户发起一次请求到A,然后A分别发送RPC请求到B和C,B处理请求后返回,C还要发起两个RPC请求到D和E,和D和E交互之后再返还给A,由A来响应最初的请求。
对于这样一个请求,简单实用的分布式跟踪的实现,就是为服务器上每一次你发送和接收动作来收集跟踪标识符(message identifiers)和时间戳(timestamped events)。也就是将这些记录与特定的请求进行关联到一起。
2.1 如何将每个服务的日志与每一条记录与特定的请求关联到一起
目前学术界与工业界有如下两种方案,将一些记录与某个特定的请求关联到一起
1.黑盒方案(black box)
黑盒方案假定需要跟踪的除了上述信息之外没有额外的信息,这样就可以使用统计回归技术来推断两者之间的关系。
日志还是一样的记录,只是通过机器学习的方法来关联记录与特定的请求。
以一条特定请求RequestX为变量,通过黑盒(也就是机器学习的模型,比如回归分析)从A的日志中找出一条记录与之对应,同理可以找出B、C、D、E等等的相关记录。
黑盒方案的优势就是不需要改变现有日志记录方法,但是缺点非常明显,由于依赖统计推理,黑盒方案的精度往往不高(可以经过大量的数据学习和训练,进行提高精度),实际使用中效果不好。
(比如Project5,WAP5和Sherlock)
2.基于标注的方案(annotation-based)
基于标注的方案依赖于应用程序或中间件明确地标记一个所有服务全局ID,借此将一串请求关联起来。
比如对RequestX来说,赋予一个标志符1000,后续相关各个服务都会将标识符1000与记录一起打在日志里。
这种方法的优势就是比较精确,目前google、twitter、淘宝等都采用这种方式。
具体的做法就是根据请求中的TraceID来获取Trace这个实例,各种编程语言有各自的方式。获取到Trace实例后就可以调用Recorder(记录器)来记录Span了,记录值先直接以日志的形式存在本地,然后跟踪系统会启动一个Collector(收集器) Daemon(守护线程)来收集日志,然后整理日志写入数据库。 解析的日志结果建议放在BigTable(Cassandra、HDFS等)这类稀疏表的数据库里。因为每个Trace携带的Span可能不一样,最终的记录是每一行代表一个Trace,这一行的每一列代表一个Span。
但是基于标注的方案最主要的缺点也很明显,需要代码植入。(所以如何减少代码侵入是最大的问题)
对于减少代码的侵入性,建议将核心跟踪代码做的很轻巧,然后把它植入公共组件中,比如线程调用、控制流以及RPC库。
2.2 跟踪树和span
分布式跟踪系统要做的就是记录每次发送和接受动作的标识符和时间戳,将一次请求涉及到的所有服务串联起来,只有这样才能搞清楚一次请求的完整调用链。
在Dapper中使用Trace表示对一次请求完整调用链的跟踪,将两个服务例如上面的服务A和服务B的请求/响应过程叫做一次span。
可以看出每一次跟踪Trace都是一个树型结构,span可以体现出服务之间的具体依赖关系。
每个跟踪树Trace都要定义一个全局唯一的TraceID,推荐用64位的整数表示,在这个跟踪中的所有Span都将获取到这个TraceID。 每个Span都有一个ParentSpanID和它自己的SpanID。上图那个例子中A服务的ParentSpanID为空,SpanID为1;然后B服务的ParentSpanID为1,SpanID为2;C服务的ParentSpanID也为1,SpanID为3,以此类推。
在Dapper跟踪树结构中,树节点是整个架构的基本单元,而每一个节点又是对span的引用。节点之间的连线表示的span和它的父span直接的关系。虽然span在日志文件中只是简单的代表span的开始和结束时间,他们在整个树形结构中却是相对独立的.
图2:5个span在Dapper跟踪树种短暂的关联关系
在上图中说明了span在一个大的跟踪过程中是什么样的。Dapper记录了span名称,以及每个span的ID和父ID,以重建在一次追踪过程中不同span之间的关系。如果一个span没有父ID被称为root span。所有span都挂在一个特定的跟踪上,也共用一个跟踪id(traceID在图中未示出)。所有这些ID用全局唯一的64位整数标示。在一个典型的Dapper跟踪中,我们希望为每一个RPC对应到一个单一的span上,而且每一个额外的组件层都对应一个跟踪树型结构的层级。
图3:在图2中所示的一个单独的span的细节图
span除了记录ParentSpanID和自己的SpanID外,还会记录自己请求其他服务的时间和响应时间。由于客户端和服务器上的时间戳来自不同的主机,必须考虑到时间偏差,为了解决这个问题需要约定一个前提,即RPC客户端必须发出请求后,服务端才能收到,即如果服务端的时间戳比客户端发出请求的时间戳还靠前,那么就按请求时间来算,响应时间也是如此。(RPC客户端发送一个请求之后,服务器端才能接收到,对于响应也是一样的(服务器先响应,然后客户端才能接收到这个响应)),这样一来,服务器端的RPC就有一个时间戳的一个上限和下限。
从上图可以首先能看出来这个span是一次”Hello.Call”的RPC,SpanID是5,ParentSpanID是3,TraceID是100。 我们重点看一下Client Send, Server Recv, Server Send, Client Recv即CS, SR, SS, CR。
- CS:客户端发送时间
- SR:服务端接收时间
- SS: 服务端发送时间
- CR: 客户端接收时间
通过收集这四个时间戳,就可以在一次请求完成后计算出整个Trace的执行耗时和网络耗时,以及Trace中每个Span过程的执行耗时和网络耗时。
- 服务调用耗时 = CR - CS
- 服务处理耗时 = SS - SR
- 网络耗时 = 服务调用耗时 - 服务处理耗时
span的开始时间和结束时间,以及任何RPC的时间信息都通过Dapper在RPC组件库的植入记录下来。如果应用程序开发者选择在跟踪中增加他们自己的注释(如图中“foo”的注释)(业务数据),这些信息也会和其他span信息一样记录下来。
2.3 如何实现应用级透明?
在google的环境中,所有的应用程序使用相同的线程模型、控制流和RPC系统,既然不能让工程师写代码记录日志,那么就只能让这些线程模型、控制流和RPC系统来自动帮助工程师记录日志了。
举个例子,几乎所有的google进程间通信是建立在一个用C++和JAVA开发的RPC框架上,dapper把跟踪植入这个框架,span的ID和跟踪的ID会从客户端发送到服务端,这样工程师也就不需要关心应用实现层次。
Dapper跟踪收集的流程
分为3个阶段:
- 各个服务将span数据写到本机日志上;
- dapper守护进程进行拉取,将数据读到dapper收集器里;
- dapper收集器将结果写到bigtable中,一次跟踪被记录为一行。
2.4 跟踪损耗
跟踪系统的成本由两部分组成:
- 正在被监控的系统在生成追踪和收集追踪数据的消耗导致系统性能下降
- 需要使用一部分资源来存储和分析跟踪数据。虽然你可以说一个有价值的组件植入跟踪带来一部分性能损耗是值得的,我们相信如果基本损耗能达到可以忽略的程度,那么对跟踪系统最初的推广会有极大的帮助。
接下来展现一下三个方面:Dapper组件操作的消耗,跟踪收集的消耗,以及Dapper对生产环境负载的影响。我们还介绍了Dapper可调节的采样率机制如何帮我们处理低损耗和跟踪代表性之间的平衡和取舍。
2.4.1 生成跟踪的损耗
生成跟踪的开销是Dapper性能影响中最关键的部分,因为收集和分析可以更容易在紧急情况下被关闭。Dapper运行库中最重要的跟踪生成消耗在于创建和销毁span和annotation,并记录到本地磁盘供后续的收集。根span的创建和销毁需要损耗平均204纳秒的时间,而同样的操作在其他span上需要消耗176纳秒。时间上的差别主要在于需要在跟span上给这次跟踪分配一个全局唯一的ID。
如果一个span没有被采样的话,那么这个额外的span下创建annotation的成本几乎可以忽略不计,他由在Dapper运行期对ThreadLocal查找操作构成,这平均只消耗9纳秒。如果这个span被计入采样的话,会用一个用字符串进行标注–在图4中有展现–平均需要消耗40纳秒。这些数据都是在2.2GHz的x86服务器上采集的。
在Dapper运行期写入到本地磁盘是最昂贵的操作,但是他们的可见损耗大大减少,因为写入日志文件和操作相对于被跟踪的应用系统来说都是异步的。不过,日志写入的操作如果在大流量的情况,尤其是每一个请求都被跟踪的情况下就会变得可以察觉到。
2.4.2 跟踪收集的消耗
谷歌的统计数据:
最坏情况下,Dapper收集日志的守护进程在高于实际情况的负载基准下进行测试时的cpu使用率:没有超过0.3%的单核cpu使用率。
限制了Dapper守护进程为内核scheduler最低的优先级,以防在一台高负载的服务器上发生cpu竞争。
Dapper也是一个带宽资源的轻量级的消费者,每一个span在我们的仓库中传输只占用了平均426的byte。作为网络行为中的极小部分,Dapper的数据收集在Google的生产环境中的只占用了0.01%的网络资源。
图3:Dapper守护进程在负载测试时的CPU资源使用率
2.4.3 在生产环境下对负载的影响
每个请求都会利用到大量的服务器的高吞吐量的线上服务,这是对有效跟踪最主要的需求之一;这种情况需要生成大量的跟踪数据,并且他们对性能的影响是最敏感的。在表2中我们用集群下的网络搜索服务作为例子,我们通过调整采样率,来衡量Dapper在延迟和吞吐量方面对性能的影响。
图4:网络搜索集群中,对不同采样率对网络延迟和吞吐的影响。延迟和吞吐的实验误差分别是2.5%和0.15%。
我们看到,虽然对吞吐量的影响不是很明显,但为了避免明显的延迟,跟踪的采样还是必要的。然而,延迟和吞吐量的带来的损失在把采样率调整到小于1/16之后就全部在实验误差范围内。在实践中,我们发现即便采样率调整到1/1024仍然是有足够量的跟踪数据的用来跟踪大量的服务。保持Dapper的性能损耗基线在一个非常低的水平是很重要的,因为它为那些应用提供了一个宽松的环境使用完整的Annotation API而无惧性能损失。使用较低的采样率还有额外的好处,可以让持久化到硬盘中的跟踪数据在垃圾回收机制处理之前保留更长的时间,这样为Dapper的收集组件给了更多的灵活性。
2.5 采样
分布式跟踪系统的实现要求是性能低损耗的,尤其在生产环境中分布式跟踪系统不能影响到核心业务的性能。 Google也不可能每次请求都跟踪的,所以要进行采样,每个应用和服务可以自己设置采样率。采样率应该是每个应用自己的配置里配置的,这样每个应用可以动态调整,特别是刚应用刚上线使可以适当调高采样率。
一般在系统峰值流量很大的情况下,只需要采样其中很小一部分请求,例如1/1000的采样率,即分布式跟踪系统只会在1000次请求中采样其中的某一次。
2.5.1 可变采样
任何给定进程的Dapper的消耗和每个进程单位时间的跟踪的采样率成正比。Dapper的第一个生产版本在Google内部的所有进程上使用统一的采样率,为1/1024。这个简单的方案是对我们的高吞吐量的线上服务来说是非常有用,因为那些感兴趣的事件(在大吞吐量的情况下)仍然很有可能经常出现,并且通常足以被捕捉到。
然而,在较低的采样率和较低的传输负载下可能会导致错过重要事件,而想用较高的采样率就需要能接受的性能损耗。对于这样的系统的解决方案就是覆盖默认的采样率,这需要手动干预的,这种情况是我们试图避免在dapper中出现的。
我们在部署可变采样的过程中,参数化配置采样率时,不是使用一个统一的采样方案,而是使用一个采样期望率来标识单位时间内采样的追踪。这样一来,低流量低负载自动提高采样率,而在高流量高负载的情况下会降低采样率,使损耗一直保持在控制之下。实际使用的采样率会随着跟踪本身记录下来,这有利于从Dapper的跟踪数据中准确的分析。
2.5.2 应对积极采样(Coping with aggressive sampling)
新的Dapper用户往往觉得低采样率–在高吞吐量的服务下经常低至0.01%–将会不利于他们的分析。我们在Google的经验使我们相信,对于高吞吐量服务,积极采样(aggressive sampling)并不妨碍最重要的分析。如果一个显着的操作在系统中出现一次,他就会出现上千次。低吞吐量的服务–也许是每秒请求几十次,而不是几十万–可以负担得起跟踪每一个请求,这是促使我们下决心使用自适应采样率的原因。
2.5.3 在收集过程中额外的采样
上述采样机制被设计为尽量减少与Dapper运行库协作的应用程序中明显的性能损耗。Dapper的团队还需要控制写入中央资料库的数据的总规模,因此为达到这个目的,我们结合了二级采样。
目前我们的生产集群每天产生超过1TB的采样跟踪数据。Dapper的用户希望生产环境下的进程的跟踪数据从被记录之后能保存至少两周的时间。逐渐增长的追踪数据的密度必须和Dapper中央仓库所消耗的服务器及硬盘存储进行权衡。对请求的高采样率还使得Dapper收集器接近写入吞吐量的上限。
为了维持物质资源的需求和渐增的Bigtable的吞吐之间的灵活性,我们在收集系统自身上增加了额外的采样率的支持。我们充分利用所有span都来自一个特定的跟踪并分享同一个跟踪ID这个事实,虽然这些span有可能横跨了数千个主机。对于在收集系统中的每一个span,我们用hash算法把跟踪ID转成一个标量Z,这里0<=Z<=1。如果Z比我们收集系统中的系数低的话,我们就保留这个span信息,并写入到Bigtable中。反之,我们就抛弃他。通过在采样决策中的跟踪ID,我们要么保存、要么抛弃整个跟踪,而不是单独处理跟踪内的span。我们发现,有了这个额外的配置参数使管理我们的收集管道变得简单多了,因为我们可以很容易地在配置文件中调整我们的全局写入率这个参数。
如果整个跟踪过程和收集系统只使用一个采样率参数确实会简单一些,但是这就不能应对快速调整在所有部署的节点上的运行期采样率配置的这个要求。我们选择了运行期采样率,这样就可以优雅的去掉我们无法写入到仓库中的多余数据,我们还可以通过调节收集系统中的二级采样率系数来调整这个运行期采样率。Dapper的管道维护变得更容易,因为我们就可以通过修改我们的二级采样率的配置,直接增加或减少我们的全局覆盖率和写入速度。
2.6 最重要的Dapper的不足
- 合并的影响:我们的模型隐含的前提是不同的子系统在处理的都是来自同一个被跟踪的请求。在某些情况下,缓冲一部分请求,然后一次性操作一个请求集会更加有效。(比如,磁盘上的一次合并写入操作)。在这种情况下,一个被跟踪的请求可以看似是一个大型工作单元。此外,当有多个追踪请求被收集在一起,他们当中只有一个会用来生成那个唯一的跟踪ID,用来给其他span使用,所以就无法跟踪下去了。我们正在考虑的解决方案,希望在可以识别这种情况的前提下,用尽可能少的记录来解决这个问题。
- 跟踪批处理负载:Dapper的设计,主要是针对在线服务系统,最初的目标是了解一个用户请求产生的系统行为。然而,离线的密集型负载,例如符合MapReduce模型的情况,也可以受益于性能挖潜。在这种情况下,我们需要把跟踪ID与一些其他的有意义的工作单元做关联,诸如输入数据中的键值(或键值的范围),或是一个MapReduce shard。
- 寻找根源:Dapper可以有效地确定系统中的哪一部分致使系统整个速度变慢,但并不总是能够找出问题的根源。例如,一个请求很慢有可能不是因为它自己的行为,而是由于队列中其他排在它前面的(queued ahead of)请求还没处理完。程序可以使用应用级的annotation把队列的大小或过载情况写入跟踪系统。此外,如果这种情况屡见不鲜,那么在ProfileMe中提到的成对的采样技术可以解决这个问题。它由两个时间重叠的采样率组成,并观察它们在整个系统中的相对延迟。
- 记录内核级的信息:一些内核可见的事件的详细信息有时对确定问题根源是很有用的。我们有一些工具,能够跟踪或以其他方式描述内核的执行,但是,想用通用的或是不那么突兀的方式,是很难把这些信息到捆绑到用户级别的跟踪上下文中。我们正在研究一种妥协的解决方案,我们在用户层面上把一些内核级的活动参数做快照,然后绑定他们到一个活动的span上。
2.7 MapReduce模型
MapReduce是一种编程模型,用于大规模数据集(大于1TB)的并行运算。概念"Map(映射)“和"Reduce(归约)”,是它们的主要思想,都是从函数式编程语言里借来的,还有从矢量编程语言里借来的特性。它极大地方便了编程人员在不会分布式并行编程的情况下,将自己的程序运行在分布式系统上。 当前的软件实现是指定一个Map(映射)函数,用来把一组键值对映射成一组新的键值对,指定并发的Reduce(归约)函数,用来保证所有映射的键值对中的每一个共享相同的键组。
2.8 ProfileMe提到的成对采样技术
英文论文地址:https://www.cs.tufts.edu/comp/150PAT/tools/dcpi/micro30.pdf
三、其他知名的开源APM
3.1 Pinpoint
Pinpoint是一个APM(应用程序性能管理)工具,适用于用Java / PHP编写的大型分布式系统。
受Dapper的启发,Pinpoint提供了一种解决方案,通过跟踪分布式应用程序之间的事务,帮助分析系统的整体结构以及它们中的组件如何相互连接。
对Java领域的性能分析有兴趣的都应该看看这个开源项目,这个是一个韩国团队实现并开源出来的,通过JavaAgent的机制来做字节码代码植入(另外还有ASM字节码技术),实现加入traceid和抓取性能数据的目的。
github地址:https://github.com/naver/pinpoint
NewRelic、Oneapm之类的工具在Java平台上的性能分析也是类似的机制。
NewRelic:国外收费的APM
Oneapm:国内收费的APM
Pinpoint架构示意图
Pinpoint通过在Host App启动时加入PinpointAgent来采集数据,然后把采集到的跟踪数据和性能数据实时发送到Pinpoint Collector,Collector收集之后存放到HBase数据库中,由HBase做MapReduce运算,分析出分布式系统的机器访问关系拓扑图、每个节点的线程状态、请求/响应数据、调用栈信息、应用程序的性能数据,通过Pinpoint WebUI可以进行实时的展示。
侵入性
PinPoint采用的是Java Agent向节点应用指定的函数前注入before和after逻辑,向服务器发送消息,因此基本不用修改代码,只需简单修改一下配置;
APM探针的基本原理
3.2 SkyWalking
Skywalking是由国内一位叫吴晟的工程师开源,已加入Apache孵化器,是一个APM系统,为微服务架构和云原生架构系统设计。它通过探针自动收集所需的指标,并进行分布式追踪。通过这些调用链路以及指标,Skywalking APM会感知应用间关系和服务间关系,并进行相应的指标统计。Skywalking支持链路追踪和监控应用组件基本涵盖主流框架和容器,如国产PRC Dubbo和motan等,国际化的spring boot,spring cloud。
github地址:https://github.com/apache/incubator-skywalking
Skywalking总体架构主要分为三部分:
- SkywalkingAgent:探针,原理同Pinpoint一样,使用JavaAgent做字节代码植入,用来收集和发送数据到Skywalking Collector;
- SkywalkingCollector:链路数据收集器,数据可以落地到ElasticSearch或者H2;
- SkywalkingUI:Web可视化平台,用来展示落地的数据;
3.3 Zipkin
这个是twitter开源出来的,也是参考Dapper的体系来做的。Zipkin的Java应用端是通过一个叫Brave的组件来实现对应用内部的性能分析数据采集。Brave(https://github.com/openzipkin/brave)这个组件通过实现一系列的Java拦截器,来做到对http/servlet请求、数据库访问的调用过程跟踪。然后通过在spring之类的配置文件里加入这些拦截器,完成对Java应用的性能数据采集。
github地址:https://github.com/openzipkin/zipkin
Zipkin架构图
Zipkin主要分为四个部分:
- ZipkinCollector:采集数据传输到Collector之后,Collector负责校验数据、存储数据、为数据建立索引;
- Storage:Zipkin的数据可以存储在Cassandra、ElasticSearch和MySQL中;
- Query API:提供数据的查询和检索服务;
- Web UI:可视化展示平台,用于展示跟踪数据。
3.4 CAT
CAT是由美团点评开源的项目,基于Java开发的实时应用监控平台,包括实时应用监控,业务监控,可以提供十几张报表展示。Cat的定位是实时监控平台,但与其说是监控平台,更像是个数据仓库,在数据仓库的基础上提供丰富的报表分析功能。不过CAT实现跟踪的手段,是要在代码里硬编码写一些“埋点”,也就是侵入式的。这样做有利有弊,好处是可以在自己需要的地方加埋点,比较有针对性;坏处是必须改动现有系统,很多开发团队不愿意。
github地址:https://github.com/dianping/cat
CAT架构图
CAT分客户端和服务器端,客户端使用cat接口向服务器端上报统一格式的日志信息。CAT的客户端是产生日志的地方(一般来说就是被监控的应用,上图中的"应用"节点),相应的服务器端则是接受日志、消费日志的地方(上图中的server节点),日志消费后生成会日志报表。
3.5 对比
方案 | 依赖 | 实现方式 | 存储 | JVM监控 | trace查询 | 侵入 | 部署成本 |
---|---|---|---|---|---|---|---|
Pinpoint | Java 6,7,8 maven3+ Hbase0.94+ | java探针,字节码增强 | HBase | 支持 | 需要二次开发 | 最低 | 较高 |
SkyWalking | Java 6,7,8 maven3.0+ nodejs zookeeper elasticsearch | java探针,字节码增强 | elasticsearch , H2 ,mysql,TIDN,Sharding Sphere | 支持 | 支持 | 低 | 低 |
Zipkin | Java 6,7,8 Maven3.2+ rabbitMQ | 拦截请求,发送(HTTP,mq)数据至zipkin服务 | 内存 , mysql , Cassandra , Elasticsearch | 不支持 | 支持 | 高,需要开发 | 中 |
CAT | Java 6 7 8、Maven 3+ MySQL 5.6 5.7、Linux 2.6+ hadoop可选 | 代码埋点(拦截器,注解,过滤器等) | mysql , hdfs | 不支持 | 支持 | 高,需要开发 | 中 |
基于对程序源代码和配置文件的低侵入考虑,推荐的选型顺序依次是 Pinpoint > SkyWalking > Zipkin > CAT
Pinpoint:基本不用修改源码和配置文件,只要在启动命令里指定javaagent参数即可,对于运维人员来讲最为方便;
SkyWalking:不用修改源码,需要修改配置文件;
Zipkin:需要对Spring、web.xml之类的配置文件做修改,相对麻烦一些;
CAT:因为需要修改源码设置埋点,因此基本不太可能由运维人员单独完成,而必须由开发人员的深度参与了;
相对于传统的监控软件(Zabbix之流)的区别,APM跟关注在对于系统内部执行、系统间调用的性能瓶颈分析,这样更有利于定位到问题的具体原因,而不仅仅像传统监控软件一样只提供一些零散的监控点和指标,就算告警了也不知道问题是出在哪里。
3.6 总结
主流APM工具为了更好地进行推广,主要采用了侵入程度低的方式完成对应用代码的改造。并且为了应对云计算、微服务、容器化的迅速发展与应用带来的APM监控的数据的海量增长的趋势,数据落地方式也主要以海量存储数据库为主。
未来在数据分析和性能分析方面,大数据和机器学习将在APM领域发挥重要的作用,APM的功能也将从单一的资源监控和应用监控,向异常检测、性能诊断、未来预测等自动化、智能化等方向发展。
3.7 拓展
Dapper论文:
中文版
http://bigbully.github.io/Dapper-translation/
英文版
https://github.com/bigbully/Dapper-translation/blob/master/dapper分布式跟踪系统原文.pdf
Dapper简单介绍:
https://blog.csdn.net/zhanlijun/article/details/13169771