人人都需要的 Application Performance Monitoring
了解一下
需要收集什么数据
首先我们要做 APM
那我们,我们先明确我们的 APM
需要收集的数据有哪些?因为是针对 Application
而言,CPU
DISK
就不算是在这个范围之内。一般认为有两类数据需要收集
- 运行状态数据 [Metrics]: uptime,health,snaphost,avg_response_time
- 链路数据 [Tracing]:单次调用的上下文和链路信息
第一类数据的作用是分析现在的系统状态,比如依赖的中间件是否有问题,系统的 GC 状态是否正常,对外的 Web 服务是否正常。
第二类数据的作用是分析在单次处理的过程中是否出现一些问题,以及出现问题时的上下文是如何的。
采集侧
¶探针体系
探针顾名思义,插在运行程序上面的一层。因此探针体系的 APM
都有着最好的一个卖点 无侵入
,大多数都是利用了特定语言提供的无侵入能力做这件事。这里面的玩家有很多 Skywalking
pinpoint
等。
¶例子: Java Agent
因为 Java
是一门虚拟机语言,支持一种名为 动态字节码
的功能,这个在功能提供了 2
种模式,C++
版本的 agentlib
和 Java
版本的 javaagent
只需要在启动的时候
1 | java -javaagent:agent1.jar -javaagent:agent2.jar -jar MyProgram.jar |
这里的 agent.jar
就是我们的探针代码,比如有以下代码
1 | public static void premain(String agentArgs, Instrumentation inst) { |
¶小结
探针体系依赖于语言提供的能力,优点也是不辩自明的,无需客户端做任何修改就可以兼容大部分的框架。主要说说这个体系的缺点
- 适用性低:受限于语言本身的能力
- 适配繁琐:这可以参考 Skywalking 的适配,对于开源的还好,如果是内部框架呢?
主要选手:


¶SDK 体系
SDK
体系最好理解,提供了一套 SDK API
供使用者调用,这样就可以让使用者无感知监控的内部实现。对于大部分的 编译型
语言来说,这是唯一可选的路径。
¶例子: go-agent
比如 Newrelic
Tingyun
都是采用这种模式,支持大部分的常见的 Middware
,然后也支持手动的创建一些监控数据。
1 | func main() { |
然后再我们想要的地方进行埋点
1 | // 原逻辑 |
原理不用猜也知道了,对于大部分的框架来说,我们在处理上下文的时候都会传递一个 Context
对象,我们希望能够捕获一些状态数据的话,将其放置于 Context
中,然后将其在最终 End
的地方通过异步的队列上报即可。
¶例子:Cat
单纯的埋点,无需支持任何框架,本身就是一个全局对象。用起来也非常的粗暴简单
1 | Transaction t = Cat.newTransaction("URL", "pageName"); |
¶自报体系
还有一类以 Web API
提供远端的消费者来提供服务的,这样的需要自行在客户端侧进行逻辑适配,然后按照 API
的要求将数据上报来,因此在我们上面所说的两种监控数据类型上,社区有相对标准的数据格式。
指标类型:prometheus 的 metric types,不过这块还没有什么标准的文件出现,不过有一个 micrometer spring 社区在推进。
链路类型:opentracing 一统天下
¶小结
SDK
体系就实现起来很简单,并且在使用的过程中如果有什么不兼容的在使用侧进行修改都可以,但是缺点就是一旦引入了 SDK
,就是涉及到代码的更新,这就是传统 SDK
同样导致的问题。
主要选手:
处理侧
对于收集上来的数据总是需要进行处理的,因此这块设计也不尽相同。
¶聚合处理型
Skywalking
和 Pinpoint
都是这个模式,以 Skywalking
为例子,有一个单独的部署的模块为 Observability Analysis Platform
下称 OAP
OAP
承担三件事情
- 数据计算:对于
Tracing
数据需要将多个Span
聚合在一起 - 数据的收集对接储存:
OAP
支持H2
Mysql
Elasticsearch
- 数据的查询: 因为不同的数据库,对应不同的储存模型,但是对外要提供统一的接口
这个模式看起来还不错,不过从开源的版本看起来,都会有几个问题。
- OAP 的性能堪忧,随着接入节点的变多,OAP 既是接入端,又是处理端,还是查询端。
- OAP 的逻辑复杂,且难以扩展。
¶简单透传型
jaeger
就属于这一类型,只有最终的一份工作,将 Tracing
的数据落到不同的数据库中,而且几乎不进行什么处理,因此在数据内存放的都是散落的 Span
信息,在后面继续分析的时候,聚合数据的工作需要留到查询侧再进行,这样的设计也不是不无道理,因为对于大部分的时间来说,我们不会去看 Tracing
的信息,等我们真的点击的时候再进行数据分析即可。
¶理想型
- 数据接入:所有的数据应该可以直接连接
OAP
或者Kafka
,因为对于POC
或者小客户场景,加入Kafka
是不适合的,增加了运维复杂度,直接在OAP
进行处理即可。 - 数据处理:1. 对于大规模的场景,数据的消费者应该是
Spark/Flink
,基于流处理的能力减少开发复杂度, 2. 对于简单场景,OAP
进行逻辑处理即可
储存侧
储存方案笔者也不是很精通,随便看看
对于数据的存储一般都是监控体系最头疼的地方,不过还算是比较好的现在已经有很多可以参考的案例。现在常见的储存有 Hbase
Elasticsearch
TSDB
等。
对于大规模的长时间储存有需要检索分析的话,Hbase
也许符合需求,对于每日场景分析的数据我认为使用 Elasticsearch
更加适合,对于 Metris
指标的话,采用时序数据库又更更适合。因此我认为这几个应该搭配起来使用,大部分时候链路数据的热点也就是在一天左右,因为这些数据可以一开始储存在 Elasticsearch
方便分析,之后可以将数据归档到 Hbase
进行冷处理。