目前字节跳动有巨大的流量,众多的活跃微服务、容器实例数,以及庞大的研发团队。一个复杂业务链路动辄涉及数百个微服务,有一线业务,有中台,也有基础设施,不同微服务由不同的研发团队开发,同时还有各类横向团队负责整体架构的质量、稳定性、安全和效率等相关工作。不同团队对链路追踪系统都会有不一样的诉求。
同时我们也有着难得的机遇:
-
微服务框架高度统一
-
微服务高度容器化,环境统一
-
存储/中间件等基础设施高度统一
得益于长期的统一基建工作,字节全公司范围内的所有微服务使用的底层技术方案统一度较高。绝大部分微服务都部署在公司统一的容器平台上,采用统一的公司微服务框架和网格方案,使用公司统一提供的存储组件及相应 SDK。高度的一致性对于基础架构团队建设公司级别的统一链路追踪系统提供了有利的基础。
字节链路追踪系统的目标
面对这样的现状,字节链路追踪系统围绕着一些目标展开建设。我们的功能性目标主要包括这几个方面:
-
统一数据模型与语义:统一数据模型和语义规范,对所有主流框架/组件进行默认埋点中间件的替换升级,建立 Metrics / Trace / Log 可靠关联关系。
-
开放自定义:统一模型的基础上,充分开放自定义能力,满足不同业务场景的监控追踪需求。
-
中心化配置管控:中心化动态管理采样、染色、熔断限流、索引、脱敏保密等各类策略。
-
一站式观测平台:提供从 SDK 到采集、计算、存储、查询和产品化交互的完整解决方案,基于高质量基础数据,构建一站式观测平台,提升监控排障、SLO 调优、架构梳理、容量管理等场景的能效。
在功能性目标的背后,我们追求的技术目标主要围绕这几个方面:
-
业务集成开销最小化:集成开销包括业务接入的改造成本和接入后带来的 Overhead 开销。大范围的链路追踪能够成功覆盖推广,必须保证将集成开销降到最低。
-
平衡存储效率与检索需求:需要以有限的机器预算完成较大数据量的处理和存储,保证数据从产生到可被检索的延迟在分钟级以内,检索响应速度在秒级以内。
-
多机房容灾完备性:需要优先考虑当发生断网或拥塞、机房宕机等灾难场景,业务急需观测线上状况时,保持可用。
-
最小化架构与依赖复杂度:字节在海内外有众多机房,需尽可能最小化整体架构的复杂度和第三方依赖的复杂度,否则多机房的部署运维包括容灾完备性保障会非常困难。
字节链路追踪系统的实现
数据采集
数据模型
统一的数据模型是 Trace 的基础,字节链路追踪系统的数据模型设计借鉴了 opentracing 和 CAT 等优秀的开源解决方案,结合字节内部实际生态和使用习惯,使用如下数据模型:
-
Span: 一个有时间跨度的事件,例如一次 RPC 调用,一个函数执行。
-
Event: 一个没有时间跨度的事件,例如一条 log,一次 panic。
-
Metric: 一个带多维 tag 的数值,例如一个消息体的大小,一个订单的价格。
-
Trace: 一个请求上下文在多个分布式微服务节点的完整执行链路。
-
Transaction: 一条 Trace 在单个服务节点上的所有 Span / Event / Metric 对象构成的树形结构消息体。Transaction 是 Trace 数据的处理和存储的最小单位,紧凑的数据结构有利于节约成本和提高检索性能。
下图展示了使用字节链路追踪系统 SDK 埋 Trace 的代码示例。注意其中 Context 贯穿整个请求生命周期,在进程内和跨进程间持续传递,将数据串联起来。
继续这个示例,我们结合下图阐述一下如何基于这套模型将 Metric / Trace / Log 进行可靠关联的。
Metric 关联 Trace:
-
每个 Span 会有内置的频次/耗时/失败率 Metric 统计时序,Rpc/Mq 场景的 Span 还会有 SendSize/RecvSize/MqLag 等内置统计时序。Span Tag 和 Metric Tag 一一对应,以此为依据可以将 Span 时序指标与 Trace 中的 Span 可靠关联。
-
每个 Event 不仅会挂载在 Span 上,也会有内置的频次 Metric 统计时序。Event Tag 与 Metric Tag 一一对应,以此为依据可以将 Event 时序指标与 Trace 中的 Event 可靠关联。
-
每个 Metric 不仅会挂载在 Span 上,也会按 Metric 类型输出 rate/timer/store 等各类统计时序,两边 Tag 一一对应,以此为依据可以将 Metric 时序指标与 Trace 中的 Metric 对象可靠关联。
Trace 关联 Log:
- Log SDK 会将 Context 中的 TraceID 和 SpanID 写入日志头中,通过 TraceID 和 SpanID 与 Trace 建立可靠关联。
语义规范
仅有统一的抽象数据模型还不够。如果每个服务都五花八门的随意打 tag 没有统一标准,那么即使有统一抽象模型也很难建设高质量的观测平台。必须对 HTTP Server, RPC Server, RPC Client, MySQL Client, Redis Client, MQ Consumer, MQ Producer 等各类主流场景都进行统一的语义规范,确保不同语言不同框架在相同场景下上报的数据遵循统一语义规范,才能够真正获取高质量的可观测性数据。
语义规范没有唯一标准,下面给出字节内部目前使用的部分语义规范作为参考示例。
通用基础字段
| 字段名称 | 描述 |
| — | — |
| ServiceName | 服务名称 |
| Framework | 服务所使用的框架组件 |
| DC | 服务所在机房 |
| Cluster | TCE 上服务的逻辑集群 |
| DeployStage | 部署阶段(小流量,单机房,全流量) |
| IPV4 | IPV4 地址 |
| IPV6 | IPV6 地址 |
| PodName | 容器唯一名称 |
| Env | 服务部署的环境泳道 |
场景化语义规范示例:RPC Client 场景
| 字段名称 | 含义 |
| — | — |
| Method | 此 RPC Client 调用发起时所在的服务接口 |
| ToService | 被调用的远端 Server 服务名 |
| ToMethod | 被调用的远端 Server 接口名称 |
| ToServiceType | 被调用的远端 Server 服务类型,例如 thrift/grpc |
| ToCluster | 被调用的远端 Server 集群名 |
| ToDc | 被调用的远端 Server 机房 |
| ToAddr | 被调用的远端 Server IP:Port 地址 |
| StatusCode | 系统状态码 |
| BusinessStatusCode | 业务状态码 |
| IsError | 此字段标识请求是否失败,0: 成功 1: 失败,框架默认会按照是否发生系统层面错误设置此字段的值,也允许业务自行调整这个字段的值,此字段会用于默认的错误率监控告警等场景 |
| SendSize | 发送数据大小(单位 Byte) |
| RecvSize | 收到数据大小(单位 Byte) |
| Latency | RPC 调用耗时(单位微秒) |
采样策略
由于字节整体线上流量非常大,微服务数目众多,不同微服务的性能敏感度、成本敏感度和数据需求各有不同,例如有些服务涉及敏感数据,必须有非常完整的追踪数据;有些服务性能高度敏感,需要优先控制采样数最小化 Overhead;测试泳道、小流量灰度或者线上问题追查等场景会需要不同的采样策略;常规流量和发生异常的流量也需要不同的采样策略。因此灵活的采样策略以及调控手段非常必要。字节链路追踪系统主要提供了如下几种采样模式:
-
固定概率采样+低流量接口兜底采样:默认以 Logid 作为采样种子,按固定概率进行采样。对于流量较低的接口,按固定概率采样难以命中的,SDK 会自动按一定的时间间隔进行兜底采样,确保低流量接口也有一定数目的请求被采集。
-
自适应概率采样:按单位时间对每个接口采集一定数目的 Transaction 为目标,例如 100 条/min,SDK 自动根据当前 QPS 动态计算采样率进行采样。流量较低或不稳定的服务建议采取这种模式。
-
染色采样:对特定的请求添加染色标记,SDK 检测到染色标对该请求进行强制采样。
-
PostTrace 后置采样: 当一个 Trace 一开始未命中采样,但在执行过程中发生了一些令人感兴趣的事(例如出错或时延毛刺)时,可以在 Trace 中间状态发起采样。相较于先全采再后置采样,此方案开销极低。PostTrace 是前置概率采样的一个重要补充,可以针对性地采集到异常链路,相比于先全采后 tail-based sampling 方案其开销是极小的。但 PostTrace 的缺点只能采集到 PostTrace 时刻尚未结束的 Span,因此数据完整性相较前置采样有一定损失。
我们结合一个示例来更好的理解什么是 PostTrace。左图是一个请求,按照阿拉伯数字标识的顺序在微服务间发生了调用,本来这条 trace 没有采样,但是在阶段 5 时发生了异常,触发了 posttrace,这个 posttrace 信息可以从 5 回传到 4,并传播给后续发生的 6 和 7,最后再回传到 1,最终可以采集到 1,4,5,6,7 这几个环节的数据,但是之前已经结束了的 2、3 环节则采集不到。右图是我们线上的一个实际的 posttrace 展示效果,错误层层向上传播最终采集到的链路的样子。PostTrace 对于错误链传播分析、强弱依赖分析等场景有很好的应用。
这些采样策略可以同时组合使用。需注意,采样不影响 Metrics 和 Log。Metrics 是全量数据的聚合计算结果,不受采样影响。业务日志也是全量采集,不受采样影响。
中心化配置管控
为了提高效率,方便不同团队高效工作,字节链路追踪系统提供了丰富的中心化配置管控能力,核心能力包括以下几个方面:
-
采样策略:支持业务按照不同的集群、接口、机房、部署阶段设置不同的概率采样策略;也可以动态设置染色、PostTrace 触发条件。
-
自定义索引:不同的框架和场景会有不同的默认索引字段,也支持业务按需在默认索引的基础上为自定义字段创建索引。
-
熔断保护:SDK 默认配备多种熔断保护机制确保 Trace 采集不会占用过多资源影响主线功能,同时允许业务根据实际情况对相关的熔断参数进行动态调整。
-
脱敏保密:业务可以按需对 Trace 数据进行脱敏和保密。
整体架构
整体架构
字节链路追踪系统从数据接入侧、消费存储到查询整体模块架构如上图所示。这里说一些技术细节:
-
私有协议数据流,性能更极致:从 SDK 到最终写入存储,整体数据流采用私有协议,数据流中各环节仅需解码部分 header 即可完成处理,无需解码所有内容,可以节约大量的中间环节资源,降低时延。
-
底层本高吞吐的字节自研日志存储:以较低的存储成本实现较高的写入速度和查询性能,用于支持各类 Trace 在线检索场景。
-
单元化架构保障多机房容灾完备性:整体采用单元化架构,节约机房间网络带宽,在部分机房间网络故障或单机房宕机时保持高可用。
-
精细灵活的中心化调控能力:统一的配置中心向整个数据流各阶段下发各类动态配置发并实时生效。
-
兼顾在线实时查询与计算分析:如架构图所示,数据流主要分两条,一条负责数据的在线存储和实时查询,要求链路尽可能短,追求性能极致,压低时延,保证数据从产生到可检索要尽可能快+高可用;另一条是计算分析流,对延迟的要求相对较低,但是需要满足各类场景化的计算分析需求,与公司数仓平台有较好的集成。
-
元数据采集与安全过期:从 Trace 数据流中可以采集到准确度和时效性很高的元数据,例如每个微服务有哪些活跃接口,使用了哪些框架组件等信息,为多个第三方系统例如监控告警和服务治理等平台提供支持。
多机房容灾完备性
前面讲目标时提到,链路追踪系统作为一个可观测性基础设施,需要优先考虑当发生断网或拥塞、机房宕机等灾难场景,业务急需观测线上状况时,保持可用。
字节链路追踪系统采用单元化部署机制,写入数据流上各机房间无通信,顶层查询模块部署在汇聚机房(同时在主机房部署备用查询节点),查询时向各机房发起检索并合并结果。
-
写入流主机房间无数据通信,主机房间发生断网时,功能不受损。
-
当发生单机房宕机或孤岛时,可执行预案在查询侧屏蔽掉故障机房,保证其他机房数据可用。
-
当顶层查询模块所在的机房宕机或断网时,可执行预案将查询切到备用机房查询节点继续提供服务。
分析计算
除了基础的实时检索能力以外,场景化的数据聚合分析计算也是链路追踪系统的一个重要需求,目前字节链路追踪系统支持的分析计算场景主要包括:
-
拓扑计算:为每个微服务(精确到接口/集群/机房粒度)计算上下游依赖链路拓扑骨架,满足业务架构梳理,全链路监控等场景需求。
-
流量估算:聚合计算所有 Trace 并结合每条 Trace 的采样率估算出链路的原始流量及调用比例,满足活动扩容评估,流量来源分析,成本分摊计算等场景需求。
-
错误链分析:聚合计算含有错误的 Trace 的错误传播路径,满足故障根因定位,错误影响面分析,易故障点优化等场景需求。
-
链路性能分析:聚合计算满足特定条件的 Trace 并分析各环节的耗时及调用比例等数据,满足全链路性能优化,耗时增加根因定位等场景需求。
不同的需求场景可以选择不同的计算模式,目前字节链路追踪系统采用的计算模式主要有三种:
-
近实时流式计算:从消息队列中消费数据按照时间窗口进行流式聚合计算,近实时地不断更新计算结果。例如拓扑计算主要采取此模式,以获取近实时的拓扑骨架。
-
即兴抽样计算:即兴从在线存储中按照特定条件抽样检索出有限数目(例如数百条)的 Trace 进行聚合计算,快速获得计算结果。
-
离线批计算:定时对离线数仓中的 Trace 进行 MapReduce 批计算,输出分析结果。
大部分场景的的 Trace 分析计算实质都是批量 Trace 的 MapReduce 计算,基础逻辑算子在不同的计算模式中可以复用。例如错误传播链分析计算,既可以在故障时刻进行即兴抽样计算快速得到分析结果,也可以通过离线批计算的方式进行长期订阅用于 SLO 持续优化。
现阶段实施效果
最后
**要想成为高级安卓工程师,必须掌握许多基础的知识。**在工作中,这些原理可以极大的帮助我们理解技术,在面试中,更是可以帮助我们应对大厂面试官的刁难。
《Android学习笔记总结+移动架构视频+大厂面试真题+项目实战源码》,点击传送门,即可获取!
于 SLO 持续优化。
现阶段实施效果
最后
**要想成为高级安卓工程师,必须掌握许多基础的知识。**在工作中,这些原理可以极大的帮助我们理解技术,在面试中,更是可以帮助我们应对大厂面试官的刁难。
[外链图片转存中…(img-aZQeXzrZ-1715014882902)]
[外链图片转存中…(img-Wh4rqOYP-1715014882903)]
《Android学习笔记总结+移动架构视频+大厂面试真题+项目实战源码》,点击传送门,即可获取!