1 . 介绍
1.1 产生背景
以一次网络搜索为例,用户在Google首页输入关键字后只要敲个回车键就能返回搜索结果。但是这样一个简单的请求可能会经过多个子系统的处理,而且这些处理是发生在不同机器甚至是不同集群上的。
同时在网络搜索过程中用户对请求延迟敏感度非常高,所以快速定位到是哪个子系统、哪个环节出问题是非常重要的。
1.2 Dapper设计目标
有两个重要的需求需要予以满足:
-
全面(ubiquitous deployment):追踪系统的监控对象应该是没有死角的
-
持续(continuous monitoring):异常的发生是无法预料的,而且可能是很难复现的
基于此Dapper有三个最重要的设计目标:
-
低开销(Low overhead):dapper本质是用来发现性能消耗问题,不能因为引入dapper而带来过度开销
-
低侵入(Application-level transparency):应用级透明,使用者可以无需显式嵌入dapper相关代码,否则会对代码维护和职责划分带来麻烦
-
高扩展(Scalability):至少未来几年的服务和集群的扩展,跟踪系统应该具有相关的扩展性能够适应。
2. 实现简介
2.1 如何跟踪
利用应用程序或中间件给每条记录一个全局标志符,借此将一串请求关联起来。
2.1.1 数据格式
Dapper用span来表示一个服务调用开始和结束的时间,对于Dapper来说,一个trace(跟踪过程)实际上是一颗树,树中的节点被称为一个span,根节点被称为root span。
2.1.2 数据存储
Dapper的整个数据收集过程如下图所示:首先将span数据写入本地日志文件,然后将数据收集并写入Bigtable。
每个trace记录将会被作为表中的一行,Bigtable的稀疏表结构非常适合存储trace记录,因为每条记录可能有任意个的span。
2.2 低开销
2.2.1 成本损耗
跟踪系统的成本由两部分组成
-
生成追踪和收集追踪数据
-
存储和分析跟踪数据
生成:
生成跟踪的开销是Dapper性能影响中最关键的部分,Dapper运行库中最重要的跟踪生成消耗在于创建和销毁span和annotation,并记录到本地磁盘供后续的收集。根span的创建和销毁需要损耗平均204纳秒的时间,而同样的操作在其他span上需要消耗176纳秒。时间上的差别主要在于需要在跟span上给这次跟踪分配一个全局唯一的ID。
存储:
在Dapper运行期写入到本地磁盘是最昂贵的操作,但是他们的可见损耗大大减少,因为写入日志文件和操作相对于被跟踪的应用系统来说都是异步的。不过,日志写入的操作如果在大流量的情况,尤其是每一个请求都被跟踪的情况下就会变得可以察觉到。
收集:
在生产环境下,跟踪数据处理中,这个守护进程从来没有超过0.3%的单核cpu使用率,而且只有很少量的内存使用(以及堆碎片的噪音)。我们还限制了Dapper守护进程为内核scheduler最低的优先级,以防在一台高负载的服务器上发生cpu竞争。
2.2.2 可变采样
可以不记录全部请求,而通过采样的方式来尽可能降低开销(定位耗时问题点时适用,全面跟踪系统追溯问题时不适用)
Dapper的第一个版本设置了一个统一的采样率1/1024,也就是1024个请求才追踪一次。后来发现对一些高吞吐的服务来说是可以的,比如每秒几十万的请求,但是对一些低吞吐量的服务,比如每秒几十个请求的服务,如果采样率设置为1/1024,很多性能问题可能不会被追踪到。因此在第二版本dapper提供了自适应的采样率,在低吞吐量时候提高采样率,在高吞吐量时降低采样率。
2.3 低侵入
在Google,应用使用的是相同的多线程,控制流,RPC等基础库代码,所以仅通过修改它们就可以实现跟踪功能。通过底层中间件支持来实现对业务代码低侵入。
2.3.1 异步处理
当线程进行一个被跟踪的处理时,Dapper会将一个trace context关联到线程本地化存储中。trace context中包含了span相关属性,比如trace和span id。
对于需要进行异步处理的情况,Google开发者通常都会采用一个通用的控制流库来实现回调,并将它们调度到一个线程池或是执行器中调用。Dapper保证所有回调都会保存它们创建者的trace context,同时在该回调被调用时该trace context也会被关联到对应线程。这样,Dapper就可以实现这种异步处理过程的跟踪。
2.4 企业级应用
工具 |
介绍 |
---|---|
Zipkin —— twitter |
|
hydra —— 京东 |
https://github.com/odenny/hydra 6年前更新 |
鹰眼 —— 阿里 |
|
|
|
3. 一点思考
3.1.1 耗时定位粒度
理论可以定位到方法级别
但是耗时长原因很多,不一定是方法耗时长,比如说请求在线程池中等待,根据这种统计耗时方式,可能不能快速定位到问题(mtthrift在1.8.5.10版本中,服务端在cat上打点耗时统计是没有包含在线程池中等待的时间的,也就是说从服务端的cat统计来看mtthrift请求耗时,可以看到一个请求在服务端处理完成的真实需要花费的时间)
4.博客
https://blog.csdn.net/zhanlijun/article/details/13169771
https://www.cnblogs.com/rongfengliang/p/6209301.html