设计目标
链路追踪
1、调用统计
2、调用链追踪
3、调用采样性能分析
Dapper
参考Google Dapper论文实现,为每个请求都生成一个全局唯一的traceid,端到端透传到上下游所有节点,每一层生成一个spanid,通过traceid将不同系统孤立的调用日志和异常信息串联一起,通过spanid和level表达节点的父子关系。
核心概念:
- Tree
- Span
-
调用链
在跟踪树结构中,树节点是整个架构的基本单元,而每一个节点又是对span的引用。虽然span在日志文件中只是简单的代表span的开始和结束时间,他们在整个树形结构中却是相对独立的。
核心概念: TraceID
- SpanID
- ParentID
-
追踪信息
追踪信息包含时间戳、事件、方法名(Family+Title)、注释(TAG、Comment)
- 客户端和服务器上的时间戳来自不同的主机,我们必须考虑到时间偏差。RPC客户端发送一个请求之后 ,服务器端才能接收到,对于响应也是一样的(服务器先响应,然后客户端才能接收到这个响应)。这样一来,服务器端的RPC就有一个时间戳的一个上限和下限。
跟踪消耗
处理跟踪消耗
- 正在被监控的系统在生成追踪和收集追踪数据的消耗导致系统性能下降
- 需要使用一部分资源来存储和分析跟踪数据,是Dapper性能影响中最关键的部分
- 因为收集和分析可以更容易在紧急情况下关闭,ID生成耗时,创建Span等
- 修改agent nice值,以防在一台高负载的服务器上发生cpu竞争
- 采样:如果一个显著的操作系统中出现一次,他就会出现上千次,基于这个事情我们不全量收集数据,通过模型来预估真实情况
Reference:Uncertainty i Aggregate Estimates from Sampled Distributed Traces
跟踪采样
固定采样:1/1024
这个简单的方案是对我们的高吞吐量的线上服务来说是非常有用,因为那些感兴趣的事件(在大吞吐量的情况下)仍然很有可能经常出现,并且通常足以被捕捉到。然而,在较低的采样率和较低的传输负载下可能会导致错过重要事件,而想用较高的采样率就需要能接受的性能损耗。对于这样的系统的解决方案就是覆盖默认的采样率,这需要手动干预的,这种情况是我们试图避免在dapper中出现的
应对积极采样
我们理解为单位时间期望采集样本的条目,在高QPS下,采样率自然下降,在低QPS下,采样率自然增加;比如1S内某个接口采集1条API
搜索
按照Family(服务名),Title(接口),时间,调用者等维度进行搜索
详情
根据单个traceid,查看整体链路信息,包含span,level统计,span详情,依赖的服务,组件信息等
全局依赖图
由于服务之间的依赖是动态改变的,所以不可能仅从配置信息上推断出所有这些服务之间的依赖关系,能够推算出任务各自之间的依赖,以及任务和其他软件组件之间的依赖。
依赖搜索
搜索单个服务的依赖情况,方便我们做“异地多活”时候来全局考虑资源的部署情况,以及区分服务是否属于多活范畴,也可以方便我们经常性的梳理依赖服务和层级来优化我们的整体架构可用性
推断环依赖
一个复杂的业务架构,很难避免全部是层级关系的调用,但是我们要尽可能保证一点:调用栈永远向下,即不产生环依赖
经验&优化
性能优化
不必要的串行调用
- 缓存读放大
- 数据库写放大
- 服务接口聚合调用
异常日志系统集成
如果这些异常发生在dapper跟踪采样的上下文中,那么相应的traceid和spanid也会作为元数据记录在异常日志中。异常监测服务的前端会提供一个链接,从特定的异常信息的执行直接导向到他们各自的分布式跟踪用户日志集成
在请求的头中返回Traceid,当用户遇到故障或者上报客服我们可以根据Traceid作为整个请求链路的关键字,再根据接口级的服务依赖接口所涉及的服务并行搜索ES Index,聚合排序数据,就比较直观的诊断问题了容量预估
根据入口 网关服务,推断整体下游服务的调用扇出来精确预估流量在各个系统的占比网络热点&易故障点
我们内部的rpc框架还不够统一,以及基础库的组件部分还没解决拿到应用层协议大小、如果我们收集起来,可以很简单的实现流量热点,机房热点,异常流量等情况。同理容易失败的span,很容易统计出来,方便我们辨识服务的易故障点opentraing
标准化推广,上面几个特性,都依赖span tag来进行计算,因此我们会逐步完成标准化协议,也更方便我们开源,而不是一个内部“特殊系统”
