链路追踪系统在微盟的实践

综述

微盟作为一家有复杂业务场景的 SaaS 公司,拥有着多套复杂的微服务系统,从业务拆分上,既有自己的微服务集群,也有通过微盟云面向租户的微服务集群。从架构上既有多集群,又有多区域。微盟之前使用的是 CAT+单链路体系+ELK 体系,三者系统只有简单的跳转,为了解决过去的痛点和面向未来的扩展,我们近期基于 APM 的概念建设了新一代的一站式 APM 平台,帮助用户监控,排障,性能分析等。本文将会侧重介绍微盟新版链路系统的模型,架构和在设计实践过程中的对链路的思考。因为链路系统在微服务系统中承担着穿针引线作用,是微服务调用状态最重要的观测工具。

APM简介

APM (Application Performance Management) 即应用性能管理,面向大规模微服务时,为了探查感知应用的运行状态,业界通常使用从三个维度对应用进行监控,Logs(日志) 、Traces(链路追踪) 和 Metrics(指标),本文重点讲述Traces(链路追踪)系统。三者系统共同构成了微盟一体化的 APM 系统,用户可以通过 Metrics 查询 Traces和 Logs 的监控数据和设置对应的告警,Traces 和 Logs 可以互相跳转查看相应的数据,Traces 和 Logs 也会通过 Metrics 来暴露指标。

链路追踪系统在微盟的实践

Traces

首先我们来了解下什么是分布式链路追踪,分布式链路追踪是在海量微服务集群背景下应运而生的,它解决了服务和服务调用之间的观测问题。

通常来说,它通过定义一些预制的埋点,当请求经过这些埋点时,记录下相关调用信息,只要埋点设计的足够合理,就能够绘制出一个请求的调用树和服务调用情况,例如经过了哪些服务,在每个服务中执行了多长时间,以及是否报错等。

在分布式链路追踪中,上文所述的埋点定义为 Span,一个请求流经 Span 形成的有向无环图就是 Trace,一个 Trace 唯一标识就是 TraceID。如下图所示,下图是一个典型的链路树。

链路追踪系统在微盟的实践

现代分布式链路追踪系统的发展历史,不能不提到 Google 在2010年发表的《Dapper, a Large-Scale Distributed Systems Tracing Infrastructure》1,它系统性的介绍了链路系统。在此之后,链路系统蓬勃发展,业界形成了许多的链路标准,而其中最主流的是 Opentracing2 标准,Opentracing 规定了链路的协议,提供了一套标准的和实现无关的 API,Zipkin、Jaeger、和 Skywalking 等开源组件都兼容Opentracing 标准,有兴趣的同学可以到 Opentracing 官网去看下。

Traces联动Metric

首先,所有的链路数据都会通过主动上报或者实时计算的方式汇聚耗时/次数/失败率等指标。这些指标都会在 Tag 里带上 Span 的元数据。通过这种方式,能够实现Metric 和 Traces 的联动。举个例子,下面是一个调用次数的指标结构示例,其他的指标可以参考下面的进行构建。

apm_operation_durations_gauge_count {traceScopeInstance,service,spanLayer,spanType,component,operationName}

Traces联动Log

 在日志的 SDK 中,默认把链路信息的 traceID 和 spanID 追加到日志中。

链路追踪系统在微盟的实践

(日志追加链路信息)

微盟链路系统的目标

上面已经大致上介绍了 APM 整个体系,结合微盟的现状,我们对新版调用链提出了下面几个功能目标和技术目标。

一 功能

从功能上来说,我们把链路系统拆分成后台+前台,后台链路服务可以视作一个小型的链路中台服务,配套上一个足够灵活前台链路服务。能够更加灵活地适应业务场景。这也算是中台模型的一个小应用。

1.1 后台链路服务

我们把底层的能力都沉淀到这里,后台链路服务要能够适配多种不同的调用链客户端,比如 Skywalking、Jaeger、Zipkin,不仅能够满足微盟自身的链路诉求,还能通过 iPaaS 对外赋能。统一数据模型,与 Logs 和 Metrics 互通,一站式提供 APM能力。

1.2 前台链路服务

 一方面基于深度定制的微盟链路前台服务,满足业务方的对于链路的各种诉求,另外一方面也可以平滑支持第三方开源链路客户端,然后通过后端链路模型提供服务。

二 技术

2.1 弹性稳定的链路服务

链路系统设计,都遵循无状态,弹性扩缩容,功能可降级,高可用等原则。

2.2 尽可能降低的业务使用负担

只有尽可能减少对业务方的接入成本和链路对于使用方的业务影响,链路的推广才会顺利。在 Java 侧,我们借助 Agent 技术来减少用户的接入成本。Agent 里面,通过异步化处理 Span,优化序列化逻辑,异步上报 Span 数据等方式来减少对业务方的性能损耗。

2.3 高性价比的存储方案

链路服务中,成本最大的往往是存储,如何低成本地对外提供链路服务,而又不影响业务方的体验,是我们努力的方向。我们的存储通过 ES+COS 组合有效降低了存储成本。下面的会详细说明如何拆分。

微盟链路系统的架构设计

整体架构

基于以上的目标,我们重新设计了微盟的链路架构,下图是微盟链路相关的整体架构图,然后我们来详细介绍。

链路追踪系统在微盟的实践

一 前台链路服务

前台链路服务分成微盟前台链路服务和其他链路组件客户端两大部分。

1.1 微盟前台链路服务

微盟前台链路服务的架构如下图所示,结合微盟的实际情况,做了许多的扩展性功能。

链路追踪系统在微盟的实践

(微盟前台链路服务架构图)

SDK配置: 得益于 Java 的 Agent 能力和微盟配套的 Agent 管理能力,我们实现了无侵入接入,业务方可以无感接入链路 SDK 和升级。

业务关键字: 基于微盟的常用的关键字段,比如商户 ID,用户 ID,订单 ID 等,在 span 入参动态识别业务关键字并提供基于 bizData 检索链路的能力。

业务异常: 基于微盟的 Web 和 Rpc 响应码标准,提供基于响应码的业务异常查询告警能力。

中心化配置: 支持采样,索引,出入参策略,脱敏等动态化管控配置能力。

业务方自定义配置能力: 支持业务方在中心化配置的基础上,设置自己的自定义化配置。

跨线程链路识别: 借助了 TransmittableThreadLocal 的 Agent 能力实现。

性能优化: 为了减少链路 Agent 对于业务方的影响,我们做了一系列的优化操作。Agent 里面全程链路收集异步化;采用 Protocol Buffers 协议上报数据;超过缓冲区大小,则丢弃链路数据;序列化出入参时,实时检查对象深度和当前数据大小,而不是在序列化完成之后再检查等。

采样策略: 鉴于微盟的高流量,和众多业务方的流量各异,不同业务方有不同的业务诉求。比如有的 ToB 业务,对于链路完整性要求比较高,有的业务 ToC 业务,对于链路性能损耗要求比较高,偏向于采样链路。为此微盟采用一整套灵活的采样策略来满足业务方的诉求。

固定比例采样

微盟链路全局以链路 ID 取 hash,按照一定的比例进行采样,该比例随时可以调整。

链路追踪系统在微盟的实践

染色采样

赋予某个链路 ID 一个特征,并把这个特征传递下去。所有的链路 SDK 看到这个特征则全量上报数据。

链路追踪系统在微盟的实践

异常场景收集

假如某个 Span 内发生了一些我们认为异常的场景,则触发染色。以此来避免异常链路丢失。

时间兜底采样

在固定比例的基础上,假如某个时间点,某个接口没有一条链路的话,则触发时间兜底染色。保证任何接口的任何时间段内都有链路信息。

基于时间特征采样

某个时间段内的链路全部设置链路染色保证全采样,可以满足业务方在上线后,几分钟内观察全链路等场景。

基于特定特征采样

用户传入 debug 参数后,进行链路染色,全链路采集,以此来满足业务方的测试诉求。

1.2 其他链路组件

我们公司的主编程语言是 Java,同时有一部分业务方也使用了比如 Go,Python 等其他编程语言,出于开发成本的考虑,我们不太可能对所有的编程语言提供全功能的链路客户端,但是这部分的业务方链路诉求也必须满足,为此我们支持了第三方的开源链路组件的接入,只要链路客户端兼容 Opentracing 规范,我们稍作适配,就能正常接入我们的链路服务。目前我们已经完美支持 Skywalking 链路采集客户端。

二 后台链路服务

后台链路服务负责传输,存储,加工,应用等一站式链路服务。

2.1 链路数据模型

数据在客户端采集好,接下来需要上报到我们的 apmAdapter(适配层),然后我们把前台的链路数据格式统一转换成内部标准的链路数据模型,链路的数据模型承上启下,需要考虑扩展性和普适性,需要能够兼容不同的链路模型。

得益于市面上丰富的开源生态,在参考了 Skywalking,Opentracing 等开源组件并结合微盟的实际场景和将来规划,构建了微盟的链路数据模型。一个 Trace 由多个 SegmentObject 构成,一个 SegmentObject 是由多个 Span 构成。

链路追踪系统在微盟的实践

 (链路数据模型)

SegmentObject参数:

SegmentObject 是链路数据的基本上报数据结构,他是代表了一个请求,从进入某个线程开始,到结束线程的追踪信息。考虑到微盟有多租户的场景,新加了全局参数 traceScopeInstance,用作链路实例隔离。

链路追踪系统在微盟的实践

Span参数:

Span 是链路中数据收集的最小单位,我们在这里扩展了异常的维度,把异常分成了系统异常和业务异常。系统异常我们很容易理解,就是指 Span 抛出了Exception ,而业务异常是什么?举个例子,一个 Http 请求,响应了状态 200,并在 ResponseBody 里设置了一个 Errcode,这个时候市面上的链路系统就无能为力了。而微盟的新版链路系统,能够基于业务异常这个字段,在链路系统中,精准标识上述的业务异常场景。

链路追踪系统在微盟的实践

2.1 链路数据中转

规范好的数据我们会统一分发到到消息组件,后面的再通过消费消息方式进行存储和进行链路指标收集等处理,我们采用了多套高可用 Kafka 集群作为我们的消息组件。

2.2 链路数据处理

数据进入消息组件之后,接下来我们可以把这些数据从消息组件拿出来然后进行存储,转指标等处理。

2.2.1 链路存储

存储设计

业界主流的方案是直接存储到 ElasticSearch,然后做时间切片,该方案从性能上勉强能满足我们的要求,但是成本过于昂贵,为此,我们重新设计了我们的存储方案。既能够兼顾ES所拥有的扩展性,也能减少我们的存储成本。

对象拆分

为了实现高性价比存储和高业务扩展性的目标,我们设计了下图所示的存储架构,链路数据存储的时候,首先会根据时间维度进行切分,时间切分之后,会把链路数据分成 indexData (需要索引的数据)和 rawData (原始数据,不需要充当检索条件,只需要被动获取即可),indexData 存储在 ES 的 Index 中,rawData 存储在对象存储中。通过这种方式,不仅能够满足业务需求,而且能够大幅度减少链路的存储成本,新版链路存储成本比老版本降低了50%。

链路追踪系统在微盟的实践
indexData

Span 的索引数据按照数据来源可以再细分,即:traceFields,rawDataFields,userFulltext,userIndexFields。

traceFields: 链路的共有字段,比如spanID,traceID,traceScopeInstance等。

rawDataFields: 链路存储的原始数据存储在对象存储中的bucket和bucketKey和Range。

userFulltext: 我们设计了一个bizData字段,用户可以指定tags和logs进入到bizData,用作全文检索。既能满足业务方的检索诉求,也能避免所有的数据都进入ES。

userIndexFields: 我们新增了indexTags字段,类型为object,用户可以指定tags进入到indexTags,用作检索字段。

rawData

除了索引数据之外其他的数据,如 tags,logs 等,会以二进制的形式存储到对象存储中。当用户需要看到某个链路详情的时候,可以从这里取回详细数据。这里采取了批量合并存储的理念,详情看下图。

链路追踪系统在微盟的实践

2.2.2 链路指标存储

指标这块之前一直是老版本链路大短板,通过 Cat 的形式透出部分指标,严重限制了用户的使用,用户不能够灵活的查看自己想要的数据。指标数据特别适合存储在时序存储库,我们通过实时计算平台把链路数据按照 30s 聚合成一批指标,比如耗时指标,异常指标等,最后写入到时序数据库中。我们的技术选型最终是VictoriaMetrics(后面我们称作 VM),VictoriaMetrics 是一个对标 Prometheus 的开源指标组件,你可以把它基本当成高性能的 Prometheus 使用,它的架构合理,支持水平扩展,性能优异,基本无大短板,已经经过微盟的线上海量链路数据考验了,目前VM集群规模 datapoint 已经达200亿左右,这里就不多赘述了。我们写入指标存储的架构比较简单,但是在 Flink 任务这块,我们踩过很多坑,我们用 Flink 的设计很简单,目的聚合数据然后计算指标,然后存储到 VM,但是在微盟这个量级而言,还是是需要很多的心思的。我们在这里分享几个使用心得。

链路追踪系统在微盟的实践

(链路指标存储数据流)

链路追踪系统在微盟的实践

(Flink任务)

第一个是 VictoriaMetrics的Histogram(直方图),相关指标都是 Counter类型的,而我们是后置收集数据的,所以没有办法变成 Counter,最后我们通过 Gauge类型上报所有的指标数据,使用 histogram_quantile 的函数的时候,取值方式按照 Gauge 去解决了这个问题。

第二个注意合理设置指标的 label 的 key 和 value,尤其是链路指标这种拥有庞大的 key value 的数据,比如指标数据可以不到实例级别,到应用和 Span 级别即可,VictoriaMetrics 虽然对于单个指标存储上没有类似宽表的问题,但是查询涉及的数据越多,查询耗时就越长。

第三个,假如使用 Flink 来计算指标的话,由于链路的数据量是非常庞大的,state backend 又使用了 RocksDB 存储,所以需要应该尽量避免哪些使用到了 state backend 的逻辑,比如 Window 等,可以基于 process 方法来自己实现逻辑。在链路系统中,特别容易出现热点应用问题,为了避免 Flink 数据倾斜问题,可以加上两阶段聚合解决 KeyBy 热点问题。

2.3 APM平台透出功能

有了链路详情数据和指标数据数据,业务方可以在我们的平台进行一站式链路服务。

2.3.1 告警

通过在告警板块,业务方可以配置异常告警,耗时告警,调用次数告警。

链路追踪系统在微盟的实践

(配置告警)

2.3.2 大盘

业务方收到告警或者发现异常的时候,可以借助大盘工具来分析业务 通过调用概览,业务方可以快速感知到业务异常等异常状态。可以更进一步地跳转到检索页面进行深度的分析。

链路追踪系统在微盟的实践

调用概览

2.3.3 检索

业务方可以通过业务关键字,接口名称等属性,快速定位到相关链路,也可以直接输入链路 ID 进行检索,不用很麻烦的到处找链路 ID 了。

链路追踪系统在微盟的实践

链路检索

2.3.4 更多定制化分析能力

得益于我们新的 APM 体系初步建设,链路数据不再是简单的存储,查询,而是一个体系数据,数据互联互通。借助这套体系,联动分析能力初步实现,业务方可以借助 Grafana+VM 实现更加定制化的分析需求。

阶段实施效果

吞吐量:最大支撑能力 瞬时Span上报1200万/秒

存储能力:ES Index 数据200T COS(对象存储)rawData 300T

链路从产生到页面完整查询的延迟时间:平均30s以内,99线40s

链路从产生到指标汇聚完整指标:平均40s,99线60s

小结

随着新版链路的推广和 APM 平台的初步建设完毕,这套架构不仅在功能上已经能够极大地满足业务方的各种诉求,而且在性能上表现良好,高峰期支持数百万级链路数据写入,查询和告警数据延迟控制在秒级别。目前的基础已经打好,接下来我们会进一步深入建设链路的分析能力,告警能力,巡检能力,来帮助用户快速定位故障原因,完善链路拓扑图分析能力等。

参考文献

  • [1]https://static.googleusercontent.com/media/research.google.com/en//archive/papers/dapper-2010-1.pdf
  • [2] https://opentracing.io/

本文转载自微盟技术中心,已获取转载授权,【点击跳转查看原文】

(0)
上一篇 2023年1月30日 下午2:44
下一篇 2023年4月14日 下午4:17

相关推荐

发表评论

登录后才能评论