原创声明:本文系作者原创,谢绝个人、媒体、公众号或网站未经授权转载,违者追究其法律责任。
Google Dapper
首先简单回顾一下 Google 的 Dapper,2010 年 Google 发布 Dapper 的论文系统阐述了在复杂的、大规模分布式集群环境下如何进行系统的跟踪以及问题的分析与定位。通过产生一个全局唯一的 traceId 以及用 span、annotation 核心数据模型把相关调用组成一个完整的调用树。关于 Dapper 如何做低消耗、应用级透明等细节,可以参考 Dapper 的论文。这里主要是简单回顾一下其核心思想,因为后续出现的分布式跟踪产品包括本文要分析的 Facebook 的 Canopy 都有受其启发。此文是基于2017年 Facebook 发布的 Canopy 论文,分析其如何进行分布式跟踪以及问题分析定位。
Span在 Dapper 跟踪树种短暂的关联关系
图片出自 Google Dapper[1]
背景介绍
TMM
在 Canpoy 产品前,Facebook 有一个产品叫 TMM(The Mystery Machine),在其 2014 年的论文中有具体描述,简单看一下其架构如下:
将 trace 数据存储到 Hive、Scribe 中,然后通过 Hadoop 去跑 job 去分析计算其 trace 模型。从 1.3M 条 trace 数据中分析计算出相关模型需要 2 个小时,分析还是依赖离线计算能力上,实时性没有那么强。
Canopy
在互联网分布式环境下,每一个系统在其特定场景下都有其特殊性,各自的模型都有其不同点,无法继承如 RPC 场景、Event Loop、浏览器页面加载、Mobile App 等,如果为某些场景定制化,会面临这些场景的迭代开发时间长。
另一方面一些性能分析工具不能跨系统进行分析,需要掌握每一种场景下应该使用什么样的分析工具以及相关数据之间的映射关系。每一种工具都有其开发周期,部署时间较长。基于此 Facebook 设计开发 Canopy 的出发点很明确主要是进行单个或跨系统间性能问题的定位,对一些问题分析的推测进行快速验证。
Canopy 于 2015 年开始在生产环境投入使用,每天产生和处理 13 亿数据量,整个产品是作为替换 TMM 在进行演进。
示 例
首先通过一个分析诊断问题的示例来看一下 Canopy 是如何去定位问题的。在此之前,先简单说下 Facebook 页面显示的一些技术点。Facebook 显示的一个完整的页面是有很多由不同团队开发的 page pieces 组成。Facebook 的核心框架会将它们进行合并在 web servers 和用户的浏览器端运行。为了优化用户体验,会用到一种 Early Flush 技术。Early-Flush 是服务端的一种优化技术手段,实现预测客户端需要的资源,批量发送给客户端。
问题描述,Facebook.com 在显示某个页面的初始化平均响应时间增加了300ms,如 a) 图显示:
图片出自 Facebook Canopy[2]
通过页面加载延时看到其延时增加 300ms,但没法定位到具体的某部分导致其增加,在此基础上进行进一步的拆解分析,如 b) 图显示:
图片出自 Facebook Canopy[2]
从图分析服务后端的 server 以及网络相关延时都没有发生变化,但浏览器执行时间以及资源 (css,js 等) 的获取时间增加了。很自然接下来去分析资源加载这块的问题如 c) 图所示:
图片出自Facebook Canopy[2]
页面初始化显示需要的资源发现 css 的资源下降了 5%,同时发现 Early-Flush 图有一次 miss,导致 css 额外加载了 10KB 的数据,如 d) 图所示:
图片出自 Facebook Canopy[2]
通过这些 metrics 已经知道问题所在(Early-Flush 没有命中),但还不知道具体是哪一部分导致,所以进行进一步的拆解,按 page pieces 进行分组,找到 root cause 是 UserInput 部分导致资源的加载如 e) 图所示:
图片出自 Facebook Canopy[2]
分析 UserInput 因增加的新的功能有变动,需要一些新的资源,但 Early-Flush 组件并不知道其发生了变更,从而没有达到的优化的效果,通过调整 Early-Flush 相关配置,性能问题从而解决。这是一个很常见的因相关配置没有进行修改而导致性能问题的 case,但往往这种问题很难发现定位到。
图片出自 Facebook Canopy[2]
挑 战
从上面的示例中可以看出,后面需要很丰富、详细的 trace 以及 metrics 数据进行分析、拆解。挑战主要体现在如下几个方面:
- Trace数据的模型
直接操作海量的基本的性能数据是非常笨重的,扩展上也不太可能。工程师和使用的工具都必须理解底层的事件数据与各组件高层次(High level)的数据之间的映射关系。但如果建立一个高层次的固化的模型又会带来问题:已经存在的组件,已经未来新的组件,以及三方的组件的如何支持进来。如果用多种执行模型,性能数据粒度以及质量存在大幅度变化,性能数据多样化,无法统一。因此 trace 模型的如何管理非常关键。在 Dapper 中 Span 来表示树形层次关系,在模型方面 Dapper 用 Span 来表示树形层次
- 数据的分析
- 在数据量大的情况下提供从多特征角度进行切分、分组、过滤、组合及汇总 trace 数据
- 满足通用以及个性化需求
对于一些常用场景的一般的用法支持的情况下,具备支持个性化需求的能力来满足不同场景的需求。
设 计
这部分主要看 Canopy 是如何设计去满足以及迎接这些挑战。
Pipeline
通过管道机制,从系统生成的跟踪数据中提取性能数据。在管道的每一个阶段,给用户的提供定制能力,并且各个组件之间的关注点是隔离的,这样可以进行彼此的演进而不互相影响。
Canopy 的 trace 数据的收集的思路和 Dapper 类似,也是基于一些通用的组件进行 instrument,产生一个 traceId,然后在各个组件中进行传递,产生相关的事件,Canopy Event 会在后续介绍。
图片出自 Facebook Canopy[2]
产生的事件会已上报进行收集处理,以下是其主要处理流程
图片出自 Facebook Canopy[2]
④ 事件中带有执行过程中的相关性能数据以及依赖(因果)关系数据
⑤ 收到数据在内存中进行聚合
⑥ 落地存储
⑦ 如果一个请求的所有事件收齐,然后就排队处理,在 Canopy 中所有和跟踪相关数据都已事件方式上报,需要确保一次完整的链路数据已经收齐
⑧ 把 trace event 事件映射成 trace model,因为 trace 事件中包含了相关数据能以此进行模型的重构
⑨ 执行 feature 表达式,从已经模型化的 trace中抽取计算需要的特征数据,从这块可以支持一些定制化的需求
⑩ 表达式中通过dataset配置绑定自己感兴趣的过滤掉自己不需要的 trace,以及数据的输出配置
⑪ 自己查询 dataset,或查看视图和 Dashboard
⑫ 除了可以自定义 dataset,也提供了共享的(通用)dataset,一些通用的high-level特性进行查询视图以及 Dashboard,以及一些工具用下钻分析。
以上介绍了 Canopy 的主体流程,通过 Canopy 的 events 把所有 trace 数据进行上报存储,在 Instrumentation API 层都是基于 events 携带需要的 trace 信息,而没有特定的数据模型进行封装,而在数据收集后可以进行很灵活的模型重建以及数据的抽取。
下面来看起各个相关点的设计。
Instrumentation APIs
主要职责
- 产生 traceId,并负责传递
- 记录请求相关的结构层次(e.g. 何时何地,线程和组件因果关系,网络通信)
- 收集有用的性能数据
解耦设计
- 特定的 Instrumentation 任务应该访问有限的 API,这样降低接入门槛减少出错,产生一些不正常的 trace 数据
- 访问这些有限的 API (封装好相关逻辑),产生的数据相比自己去构建数据更健壮
- 从多个角度捕获性能相关的数据,而不会破坏已经存在的逻辑。这样可以很好的支持自定义需求的设计以及与第三方包的集成
Trace Event
Trace Event
Trace Event
Canopy中的 event 结构信息:
图片出自 Facebook Canopy[2]
Event type 可扩展,会根据 type 来做不同的处理,sequenceNumber 和 timestamp 主要用于将同一个进程或线程中的 event 排序,EventId(RandomId) 用于关联其他有关系的 event。会在发送方记录 eventid,traceId 和 eventid 都会透传给接收方,在接收方会记录发送者的 eventid。
Modeled Traces
根据 event 来构建 Modeled Trace,性能数据 High-Level 表现形式,隐藏了底层日志事件的不一致性。
类型
- Execution units:执行线程
- Blocks:execution unit中计算段(Segments of computation within an execution unit)
- Points:一个Block中即时发生的events
- Edge:points间的因果关系
可以表示进程间或机器间的因果关系也可用于表示同一进程中的execution units或blocks。非常适合以下模式
长时间运行的执行单元间的流,双向通讯,应用级别block依赖
Trace Datasets
- Trace 数据衍生出来的数据集,囊括了很多Traces数据的信息
性能分析的主要入口点(面向工程师)
- Feature(trace中一个元素的label或聚合值或一些结构关系) 通过抽取函数(抽取函数是无状态的且一条trace记录执行一次)产生
Trace Dataset 表现形式
基于这些丰富或自定义的 trace 数据集来支持一般以及个性化需求。
小 结
本文介绍了 Facebook Canopy 在面对海量 trace 数据以及各种环境下,如何去设计端到端的性能分析产品。纵观下来,其设计特点是解耦的设计,trace 事件数据没有绑定到固定的数据模型中,数据处理环节支持灵活的 feature 抽取,形成不同的 trace dataset 数据集来达到不通场景的需求。
参考
[1] Dapper, a Large-Scale Distributed Systems Tracing Infrastructure
[2] Canopy: An End-to-End Performance Tracing and Analysis System