要解决的问题
- 如何记录请求经过多个分布式服务的信息,以便分析问题所在?
- 如何保证这些信息得到完整的追踪?
- 如何尽可能不影响服务性能?
追踪
当用户请求到达前端A,将会发送rpc请求给中间层B、C;B可以立刻作出反应,但是C需要后端服务D、E的配合才能应答
一个简单有用的请求追踪应该包含发送、接收消息的消息标识符、时间戳。为了关联给定发起者的这些记录,在此有黑盒和基于标注的监控模式
-
黑盒:假定并没有除了以上记录之外的额外信息,使用统计回归进行推断关联性。
更加轻量化,但是需要更多数据以保证准确性
-
基于标注:基于应用或者中间件去显式采用全局标识符标注这些记录,从而关联。
需要代码侵入,但可以将之植入到通用组件中
追踪信息
- span: 链路追踪的基本单元,以追踪树结构串联起来。
- trace id: 对于每一个请求链路上的span都有唯一的trace id
度量点
- 当一个线程处理追踪控制路径时,dapper附加追踪上下文到本地线程储存中
- 当异步处理时,dapper保证异步回调将会储存追踪上下文到创建者,并且当回调调用时将会关联到适当的线程
- 植入到rpc通信中
采样收集
- 写入span数据到本地log文件
- dapper拉取各主机数据
- 最终写入到dapper bigtable
bigtable中每个trace置于表行,每个span置于表列
收集模式
Dapper采用out-of-band进行追踪收集,主要因为
- in-band收集模式追踪数据在rpc响应头中,会动态影响应用网络
- in-band收集模式假定rpc调用完美嵌套的,
in-band: 将追踪数据随着调用链进行传送
out-of-band: 通过其他链路进行追踪
收集方式
-
基于日志:也即将trace、span等信息直接输出到应用日志中,而后汇集所有节点日志,最后推断出完整调用关系
对网络消息没有侵入,对应用也只有少量侵入,但日志本身并不追求绝对连续与一致性,这意味着追踪结果可能并不准确
-
基于服务:通过某些手段给目标应用注入追踪探针。探针在结构上可视为一个寄生在目标服务身上的小型微服务系统,它一般会有自己专用的服务注册、心跳检测等功能,有专门的数据收集协议,把从目标系统中监控得到的服务调用信息,通过另一次独立的 HTTP 或者 RPC 请求发送给追踪系统。
因此,基于服务的追踪会比基于日志的追踪消耗更多的资源,也有更强的侵入性,换来的收益是追踪的精确性与稳定性都有所保证,不必再依靠日志归集来传输追踪数据。
-
基于边车代理:用于服务网格,应用透明、语言无关、独立通道
性能损耗如何降低?
追踪系统的成本可分为追踪数据生成,追踪数据收集以及追踪数据分析组成。而收集和分析在紧急的时候是可以关闭的,因此追踪生成就成了最关键的损耗
而追踪生成的损耗最关键的则是运行时库创建和销毁span以及annotation,和写入log到硬盘中
写入硬盘合并了多个日志文件写入操作,并异步进行,有效地减少了由于写入log到硬盘造成的性能损耗
此外,dapper还发现
从图中可以看到随着采样频率的降低,延时和吞吐量都有性能上的提升。尤其是1/1024的频率下,对吞吐量的影响只有万分之六,而对延时的影响也只有千分之二。
这意味着是不是可以降低采样频率来降低对性能的影响呢?事实上是可以的,即使是降低到1/1024对于大型系统而言仍然有足够的数据进行追踪。
除了对于延时、吞吐量的影响,对于数据储存规模的影响,dapper也做了相应控制。
其使用二阶采样控制数据量大小。通过将traceID hash为0到1之间的度量z,若z小于采用参数,则将采样该数据,写入到bigtable中
总结
-
如何记录请求经过多个分布式服务的信息,以便分析问题所在?
从上文可知通过引入span和trace分别从被追踪者和请求链路两个维度,推断追踪树,从而用于分析问题
-
如何保证这些信息得到完整的追踪?
只要采样的绝对数量够大,那么就比较好追踪。对于分布式的情况,通过span组织的逻辑链路来达成;对于异步,关联到相关的线程;
-
如何尽可能不影响服务性能?
分析收集可以通过动态的开关来保证紧急情况下的性能稳定,而追踪主要是通过尽量减少采样保证的
Ref
- https://storage.googleapis.com/pub-tools-public-publication-data/pdf/36356.pdf
- http://icyfenix.cn/distribution/observability/tracing.html