一、概述微服务架构的快速发展使得分布式链接跟踪系统成为观察系统中越来越重要的组成部分。经过几年的发展,字节跳动的分布式链路追踪系统已经覆盖了字节跳动的大部分在线业务,完成了数万个微服务和数百万微服务实例的在线链路追踪。在经典的指标观察分析和单请求链路跟踪的基础上,如何从浩瀚的分布式链路数据中进一步挖掘更高层次的信息,为业务架构优化、服务治理、成本等场景提供服务优化。提供更高效的数据支持,成为下一步亟待解决的问题。本次分享主要介绍了我们构建海量链路数据分析计算系统的实践经验,以及一些具体的落地场景。2.可观察性和链接追踪2.1基本概念为了方便读者更好地理解“链接分析”,我们先说说什么是“可观察性”和“链接追踪”。已经熟悉“可观察性”和“链接追踪”概念的读者可以跳过本章。随着微服务架构的快速发展,软件系统正在从单一的应用发展为由大量微服务节点组成的复杂应用。为了更好地管理和控制复杂的软件系统,“可观察性”工具变得越来越重要。可观察性工具是建立在可观察性数据的基础上的,一般包括以下几个部分:链接跟踪Trace、日志Logging、时序指标、代码级profiling、event事件和元数据相关的CMDB等。为了帮助大家拥有对可观察性工具更直观的体验,这里举个例子来说明如何基于可观察性工具解决工作中的实际问题:告警通知值班人员的服务故障率越来越高,点击关联到errorindicator对应的Trace,在Trace中定位到错误的来源,查看源头的关键异常日志和代码栈,发现源头的报错服务正在执行变更操作,所以基本上是发现这种变化很可能是造成这种故障的原因。有了高质量的可观测性数据和工具,一个对系统不是很熟悉的值班人员也可以快速完成对本次故障的排查和止损。分布式链路追踪(Trace)是可观测系统的组成部分之一。Trace从狭义上讲是对单个请求的详细跟踪,记录请求在每个环节的调用关系,耗时,以及各种详细的标签和事件。同时Trace也起到了链接各种可观察性数据的作用,即同一个RequestContext的数据载体。分布式请求上的各类信息(Metrics/Logs..)通过Trace可靠关联,进而可以构建各种可观察性数据的上下滚动钻取的跳转功能。2.2字节链接跟踪系统字节链接跟踪系统经历了数年的发展,现已覆盖公司大部分在线业务。整体开发流程如下:2019年:Trace1.0完成Trace组件能力建设。2020年:Trace2.0实现Metrics/Trace/Log的融合,升级数据协议和技术架构,开始打造一站式观测平台Argos。2021年:字节跳动已实现与大部分主流框架组件的默认集成,微服务在公司所有业务线的覆盖率>80%。2022:构建和探索场景化、智能化的场景,比如本次分享聊天中的“链接分析”。字节链接跟踪系统的当前状态数据(2022年10月):覆盖范围:50,000+微服务/FAAS,300万+容器实例。使用:每日UV6,000+,每日PV40,000+。吞吐量:跨度数2000万+/s(默认0.1%采样)。资源配比:100+CPU核心支持100万Span/s。SDK性能:单线程40wSpan/s。查询性能:TraceID枚举P50<100ms,P99<500ms。数据生成到可检索延迟:AVG<1分钟,P99<2分钟。存储资源:10PB(3个副本TTL15天)。字节链追踪系统从数据接入端、消费存储端、到查询平台端的整体架构如下图所示。更多详情请阅读之前的分享。上次分享详细介绍了如何从零到一搭建分布式链路跟踪系统。本文主要讲链路跟踪系统中的链路聚合计算和分析部分。3.链路分析技术实践3.1需求场景经常使用Trace的同学可能对Trace的印象是通过TraceID或一些Tags检索到一个或一些Traces,然后从Trace数据中仔细查看详细的调用traces和各种Tags。分析一些具体的问题,比如请求为什么慢,或者为什么请求出错。但是我们还面临着一些更高层次的问题,比如面对一个复杂的不断变化的微服务系统:稳定性:在紧急情况下,哪些服务可以降级,哪些服务必须保证?高危易受攻击点在哪里?流量/容量:如果某个页面的PV要提升10倍,需要扩容哪些服务?你需要扩展多少?性价比:明显的性能反模式在哪里?架构不合理造成的性能浪费在哪里?这些问题的答案,通过人工逐条查看Trace,很难得到可靠的结果。但是,它可以从大量的Trace数据中自动计算出来,为最终的决策提供可靠的数据支持。我们称这种大量踪迹链接分析的聚合计算。3.2基本原理链路分析的基本原理是对大量迹线进行聚合计算。一般遵循MapReduce计算模型。在这个过程中,可能会结合一些订阅规则和其他数据得到计算结果,然后应用到具体的场景应用中。.3.3技术架构适用于大量链路数据聚合计算的可选模式主要有三种,即基于在线数据流的流式计算、从在线存储查询有限踪迹后即兴(抽样)计算、基于离线数据的离线计算仓库。这三种计算方式的特点分析如下表所示。计算方案优缺点流式计算-接近实时的分析结果-更高的数据完整性和准确性-多机房部署和升级更方便(自研不依赖flink)-无法计算任何下的数据随时情况-受数据影响受流量波动影响,维护成本相对较高即兴(抽样)计算-可随时对任何情况下的数据发起分析并快速得出结果-最低的附加机器成本和运营和维护成本只能针对有限的采样轨迹进行计算数据完整性低离线计算-数据完整性和准确性高-稳定性高,机器成本和运维成本低-每小时或天级延迟分析结果-严重依赖大数据套件,多区域部署分析了三种聚合计算模式的特点后,再分析下行分析场景面临的技术需求。需求类型此类高需求场景描述适用计算方案示例实时需求是否需要分析最新数据,是否可以接受一定的延迟?故障归因即兴计算/流计算数据完整性要求抽样部分数据计算能否满足需求?还是必须对所有的Traces都进行计算才能得到结果?流量估算离线计算/流式计算需求即兴创作任务是否即兴发起,必须快速获得结果?故障归因即兴计算基于以上分析,我们认为没有一种计算模型可以解决所有问题,因此最终选择的技术方案是一种流式、即兴、离线一体化的技术方案:基于统一的基础数据模型和逻辑算子支持三种不同的计算引擎,满足不同的场景需求。在实现这个方案的过程中,我们总结了一些有用的实践经验:逻辑运算符和计算引擎分离,Trace本身的数据结构比较复杂,其分析计算逻辑往往具有一定的复杂度。算子和引擎分离方便将同一个逻辑算子应用到不同的计算引擎上,可以更好地提高研发效率和代码可维护性。例如,性能瓶颈分析相关的算法既可以用于即兴计算,也可以用于离线计算。数据异常自动检测与阻断Trace数据往往存在一些数据不规则性,比如超高维的接口名称,导致聚合后的数据维度远超预期,影响计算任务的稳定性。自动化的异常数据发现和封禁或降级机制可以起到更好的保护作用。保留原始样本,提高可解释性尽量保留聚合分析结果对应的具有代表性的原始(极)迹样本,可以提高分析结果的可解释性和用户信任度。配备订阅和降级机制,大量数据计算成本高,非基础功能可以使用按需订阅模式提高ROI;构建任务灵活降级能力,资源紧张时优先保证高频基础功能的高可用。4.链接分析实现场景介绍完链接分析的底层技术架构,我们再介绍一些具体的实现场景。4.1精确的链路拓扑计算链路分析最常用的场景是链路拓扑计算。这里所说的“链路拓扑”是指进入任意一个服务节点,获取所有流经该节点的trace的聚合路径,从而清楚地知道该服务节点的上下游依赖拓扑。由于字节微服务数量众多,中端和基础服务种类繁多,调用关系比较复杂,所以我们拓扑计算的目标是:准确:只检索与检索节点有直接流量的拓扑节点。灵活性:可以根据任意节点(不仅仅是入口),不同深度,不同粒度(服务/接口/集群/机房)进行拓扑搜索实时:最好有一个例子来说明精度要求是什么:如下图,“抖音.X”和“Volcano.Y”都调用了“Zhongtai.Z”,但是对于“抖音.X”流量,“Zhongtai.Z”会使用“Redis.””,而对于“Volcano.Y”的流量“中台.Z”会使用“Redis.Volcano”,所以实际上,“抖音.X”与“Redis.Volcano”没有直接依赖关系。那么当我们检索"抖音.X"时,想要的拓扑结构是["抖音.X","Zhongtai.Z","Redis.抖音"]而不是"Redis.Volcano"。举例说明什么是灵活性需求:如下图,不仅可以根据入口检索拓扑,还可以根据中间节点“Zhongtai.Z”或存储组件检索拓扑节点“Redis.抖音”;拓扑不仅可以按照服务+接口的粒度进行检索,还可以按照服务粒度、服务+集群粒度、服务+机房粒度等其他粒度进行检索。面对这样的技术需求,我们研究了一些目前业界已有的拓扑计算方案:方案一:也是开源的主流方案,聚合单跳调用关系,在检索拓扑时拼装单跳调用关系。该方案简单、成本低,但精度低。对于中小型微服务系统还可以,但是对于字节类场景,比如上面的例子,当你想用“♂.X”来检索拓扑时,你不想看到“Redis.Volcano”无法满足需求。方案二:复旦&Ebay共享,将Trace按照Path进行分组聚合,每个服务节点对应一组Paths。查找时,先检索服务对应的PathID列表,然后根据PathID检索出Path路径聚合拓扑,这种方案的准确率最高,可以清晰的梳理出每个服务节点对应的所有调用路径,但是在bytes的实际场景中,我们发现Path展开非常严重,一个服务节点往往有上千条Path,检索成本高。速度慢,难以满足字节场景的实际需求。结合字节场景的实际需求,兼顾准确率、成本和检索速度,我们终于设计了一个新的解决方案。方案三:为每个节点计算一个拓扑图,选择一个图数据库作为存储载体。图数据库的一个节点对应一个业务节点,图数据库的一条边对应[“所属拓扑”,“源节点”,“目标节点”]三元组。在上面的例子中,有这样的边:拓扑源节点和目的节点对应的颜色是抖音.X抖音.X中台.ZBlue抖音.X中台.ZRedis.抖音Blue火山。Y火山。Y中泰。Z黄色火山。Y中泰。ZRedis。火山黄中台。Z抖音。X中泰。Z青中台。Z火山。Y中泰。Zhongtai.ZZhongtai.ZRedis.抖音GreenZhongtai.ZZhongtai.ZRedis.VolcanoGreen当需要检索“抖音.X”对应的拓扑时,首先在图数据库中定位“抖音.X”,然后以此为起点发起图遍历搜索,找到所有满足“从属拓扑=抖音.X”的边(即图中所有蓝色边),得到“抖音。X》对应的拓扑为["抖音.X","Zhongtai.Z","Redis.♂"]。该方案精度可以满足需求,成本相对有所折衷。同时同时,很好地利用了图数据库的特性,可以实现非常高效的拓扑查询,同时由于链路拓扑对数据完整性要求高,对实时性有一定的需求,所以我们使用流式计算引擎来支持链路拓扑的计算,大致流程如下图所示,精准链路拓扑的应用场景有:比较宽。下面是一些具体的例子:全链路实时观察:实时观察拓扑中每个节点的流量/延迟/错误率/资源使用/告警/变化,快速从全链路视角获取总体状态信息。混沌演练:为故障演练提供链路依赖底图,生成演练方案,将故障注入整条链路的各个环节,收集并验证系统响应,得到演练报告。压测准备:提前梳理好压测流量将流经的节点,让相关节点做好压测准备。服务架构治理:为服务架构治理场景梳理上下游依赖关系提供依据。故障归因:为故障归因提供上下游依赖遍历的底图数据。4.2全链路流量估计全链路流量估计回答的主要问题是:流量流向哪些下游?如果当前节点的流量增加N,那么这些下游节点的流量会增加多少?流量来自哪些上游?如果当前节点的流量增加N,这个流量是谁带来的?比例是多少?全链路流量估计是在精确拓扑计算的基础上实现的,因此也采用流计算,根据每条路径的踪迹数和踪迹对应的采样率数据进行估计。计算结果的格式如下图所示。每个拓扑中的每个边都对应于一个估计的流量和流量比。基于这样的数据,我们可以针对任何微服务接口给出上述两个问题的答案。全链路流量预估的主要应用场景如下:业务活动扩展预估:业务推广前,往往通过产品预估DAU增量,然后将DAU增量换算成一组的QPS增量。入口服务接口。然后根据整条链路的流量比例,估算整条链路各链路的QPS增量和扩展需求。可靠的全链路流量比数据可以为该场景提供很好的辅助支持。成本能力管理:精准的全链路流量预估数据,帮助业务建立更精细的成本能力规划,促进降本增效。流量变化根因分析:当流量出现意外波动时,全链路流量预估数据可以帮助快速分析波动的根因和来源。4.3强弱依赖分析强弱依赖信息是服务稳定性治理场景的重要数据支撑,也可以通过在线Trace数据自动计算。强依赖:当异常发生时,影响核心业务流程和系统可用性的依赖称为强依赖。弱依赖:当异常发生时,不影响核心业务流程,不影响系统可用性的依赖称为弱依赖。如下图所示,当A调用B失败时,如果A仍能成功响应其Client,则B为A的弱依赖;当A调用B失败时,如果A不能成功响应它的Client,那么B就是A依赖的强依赖。计算强弱依赖的技术目标包括:准确度:尽可能高,特别是减少“弱强误判”,为判断提供依据样本覆盖率:尽可能高,尽可能多的调用线上存在的关系强度取决于数据粒度:<调用者服务接口,被调用者服务接口>,<场景,调用者服务接口,被调用者服务接口>实时性:最好有,尽可能满足数据的完整性和及时性需求,我们选择流式计算方式,从数据流中选择TracewithError,计算依赖关系的强弱。需要注意的是,短期的实时数据样本往往是不够的,需要结合历史积累的数据进行联合判断才能下结论。强弱依赖分析的主要挑战:准确率:不同业务线的业务稳态判定规则难以统一,需要推动业务完善数据标注或规则录入。覆盖:部分路由归一化错误率极低,很难收集到足够的错误样本,需要辅以混沌演练。强弱依赖分析的主要应用场景包括:限流降级方案配置指导:强弱依赖数据可以解答哪些服务可以降级,哪些服务在紧急情况下必须保证的问题。弱依赖可以被限制和降级,而强依赖应该尽可能保证高可用。超时漏斗配置指导:强弱依赖数据可以指导业务更合理地配置超时漏斗。如下图所示,弱依赖配置不合理,超时时间长,可能导致不必要的慢请求;设置超时时间短的强依赖配置也不合理,可能造成不必要的请求失败,影响用户体验。辅助自动化故障归因:准确的强弱依赖信息,可以对自动故障归因起到很好的辅助作用。服务架构治理:准确的强弱依赖信息,辅助业务优化架构,管理不符合预期的强依赖,提前做好容灾预案,提升整体稳定性和高可用。4.4全链路性能反模式分析在实践过程中,我们观察到有一些非常典型的性能反模式问题,可以从Trace数据中自动计算发现。常见的性能反模式包括:调用放大:在单个请求中,大量调用同一个服务接口。如果线上链路出现调用放大率非常高的情况,往往不仅暴露性能问题,还可能带来稳定性风险,需要尽快解决。重复调用:在一个请求中,多个地方使用同一个Request调用同一个服务接口。这种情况常见于基础信息的重复获取,比如在多个地方重复请求用户信息或者设备信息,可以优化。读写放大:在单次请求中,从底层服务获取的数据量远大于最终返回给客户端的数据量。当滥用重接口获取轻信息时,这种情况很常见。比如调用heavy接口,请求所有用户信息,但只取用户名字段。串行环路:串行环路具有明显的特点,其形状大致如下图所示。通常,它可以针对并行或批处理调用进行优化。性能反模式问题的发现有以下两个要求:优先发现极值问题并提供最坏情况样本优先发现核心链路上的高流量+反模式问题。因此,性能反模式的分析任务需要自动发现最严重的反模式问题,给出极值样本,关联这些问题所关联路径的流量和ingress优先级,从而帮助企业优化服务延迟和成本,及早解决相关潜在的稳定性风险。4.5全链路性能瓶颈分析单个请求的分布式trace视图清晰直观,但局限性在于观察者无法判断单个请求呈现的trace模式是普遍现象还是特殊现象。因此,从大量Trace数据中分析链路性能瓶颈,找出整体性能模式和worstcase样本,也是链路分析的需求场景。链路性能模式是从批量跟踪中聚合和计算的,满足即兴模式和离线模式的要求。Adhoc模式可以满足筛选任意时间段、灵活条件(各种标签、耗时区间)的批次痕迹,快速获得分析结果。离线订阅模式可以满足更完整地分析全量Trace数据的整体性能模式,观察长期趋势的需求。因此,我们将在即兴和离线计算模式下重用链路性能分析聚合算子。分析结果示例:4.6ErrorPropagationChainAnalysis一条ErrorTrace可以观察到一条错误传播路径,但观察者无法确认一条错误传播路径是否一定代表一个普遍问题,也无法回答错误传播的影响。因此聚合大量的ErrorTrace来分析整体的错误来源、传播路径、影响面也是链路分析的需求场景。与链路性能分析类似,错误传播路径是从批量跟踪中聚合和计算的,这在临时模式和离线模式下都是必需的。我们还将错误传播链分析算子应用于临时和离线计算模式。Adhoc模式可以满足任意时间段、灵活条件(各种标签)批量过滤ErrorTrace的需求,快速得到聚合分析结果。离线订阅模式可以满足更完整的全量ErrorTrace数据聚合分析需求,观察长期趋势,协助业务长期稳定优化。分析结果示例:五、总结与展望本文主要介绍在从零到一构建链路追踪基础能力后,如何对海量链路数据进行聚合分析,以回答更高层次的场景化问题。我们分享了我们具体的技术选型过程和实施技术架构,以及一些成功的实施案例。最后分享一些后续展望:数据质量的持续建设:数据质量对链接数据分析结果的质量起着极其重要的作用。不断完善业务语义规范,推进各级接入覆盖,将是我们的后续工作。场景化:持续构建开放能力,积累业务知识和专家经验,打磨业务最佳实践。智能化:基于数据+知识+算法,在故障归因、稳定性优化、性能成本优化等场景持续提升能效。拥抱云原生:完善OpenTelemetry接入,打磨基础组件,更好适配各种云端场景。6.加入我们我们是字节跳动-基础设施-应用观察(服务器)团队,专注于PB级海量数据的可观察性基础设施(Metrics、Tracing、Logging、Event、Profiling)和上层可观察性应用(如告警生命周期)管理、异常检测、根因分析)建设,为字节跳动整体业务稳定、性能优化、服务治理保驾护航。
