news 2026/7/10 6:11:26

【SkyWalking从入门到精通】第30篇:SkyWalking追踪模型深度解析——三层架构的独特设计

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
【SkyWalking从入门到精通】第30篇:SkyWalking追踪模型深度解析——三层架构的独特设计

上一篇【第29篇】Google Dapper论文精读——分布式追踪的
下一篇【第31篇】SkyWalking数据传输协议解析——探针与OAP的通信密码(未完待续)


1. 引言:为什么需要三层?

上篇文章我们精读了Dapper论文,它定义了Trace和Span两层模型。Zipkin和Jaeger都沿用了这个模型。但SkyWalking偏不——它加了一个中间层:TraceSegment,搞出了一个三层模型。

为什么要"多此一举"?是SkyWalking的设计者喜欢叠床架屋,还是真有什么场景二层模型搞不定?

这篇文章带你理解这个"多出来的一层"到底解决了什么问题。

2. 三层追踪模型概览

2.1 Trace → TraceSegment → Span

+------------------------------------------------------------------+ | SkyWalking 三层追踪模型 | +------------------------------------------------------------------+ | | | Trace (一次完整请求) | | ┌──────────────────────────────────────────────────────────┐ | | │ Trace ID: 7b2a8f1d9e3c.1625.1625000000001 │ | | │ │ | | │ TraceSegment (进程内的一次执行) │ | | │ ┌─────────────────────────────────────────────────┐ │ | | │ │ Segment ID: 7b2a8f1d9e3c.1625.1625000000002 │ │ | | │ │ Service: gateway-service │ │ | | │ │ │ │ | | │ │ EntrySpan: /api/order/create (3.2s) │ │ | | │ │ ├── LocalSpan: validateOrder (5ms) │ │ | | │ │ ├── ExitSpan: POST order-service/create (200ms)│ │ | | │ │ └── ExitSpan: Kafka produce (8ms) │ │ | | │ └─────────────────────────────────────────────────┘ │ | | │ │ | | │ TraceSegment (跨进程,另一个JVM进程) │ | | │ ┌─────────────────────────────────────────────────┐ │ | | │ │ Segment ID: 7b2a8f1d9e3c.1625.1625000000003 │ │ | | │ │ Service: order-service │ │ | | │ │ │ │ | | │ │ EntrySpan: POST /create (1.5s) │ │ | | │ │ ├── ExitSpan: MySQL INSERT orders (120ms) │ │ | | │ │ ├── ExitSpan: Redis SET order:orderId (5ms) │ │ | | │ │ └── LocalSpan: sendNotification (50ms) │ │ | | │ └─────────────────────────────────────────────────┘ │ | | └──────────────────────────────────────────────────────────┘ | +------------------------------------------------------------------+

2.2 三层结构精确定义

层级英文定义生命周期
第一层Trace分布式系统中完成一个业务请求的完整调用链从用户请求到响应返回
第二层TraceSegment在一次Trace中,某个服务(JVM进程)处理的部分一个JVM进程处理一次请求的过程
第三层SpanTraceSegment中的一个具体操作(RPC/DB/Cache/…)一个具体操作的起止

2.3 为什么需要TraceSegment?

Dapper的二层模型中,所有Span直接挂在Trace下。这在一个关键场景下有缺陷:

问题场景:多个Span属于同一个服务,但它们是哪个服务实例产生的?

+------------------------------------------------------------------+ | Dapper二层模型 vs SkyWalking三层模型 | +------------------------------------------------------------------+ | | | Dapper二层 (Trace → Span 直接父子关系): | | ┌──────────────────────────────────────────────────┐ │ | │ Trace ID: abc123 │ │ | │ │ │ | │ Span-1: [gateway:/api/order] │ │ | │ ├── Span-2: [gateway:validateOrder] │ │ | │ ├── Span-3: [order-service:createOrder] │ │ | │ └── Span-4: [order-service:insertOrder] │ │ | │ │ │ | │ 问题: Span-2和Span-3之间有个跨进程边界 │ │ | │ 但二层模型没有显式标记这个边界! │ │ | │ 你很难回答: "gateway的哪个实例处理的?" │ │ | └──────────────────────────────────────────────────┘ │ | | | SkyWalking三层 (Trace → TraceSegment → Span): | | ┌──────────────────────────────────────────────────┐ │ | │ Trace ID: abc123 │ │ | │ │ │ | │ TraceSegment-1 [gateway-service @ 10.0.1.5:8080] │ │ | │ ├── Span-1: EntrySpan /api/order │ │ | │ └── Span-2: ExitSpan → order-service │ │ | │ ↑ 通过Reference连接到下一个Segment │ │ | │ │ │ | │ TraceSegment-2 [order-service @ 10.0.1.6:8080] │ │ | │ ├── Span-1: EntrySpan /create (对应上游的调用) │ │ | │ └── Span-2: ExitSpan → MySQL │ │ | │ │ │ | │ ✓ 边界清晰!每个Segment代表一个服务实例的一次处理 │ │ | └──────────────────────────────────────────────────┘ │ +------------------------------------------------------------------+

TraceSegment的核心价值

  1. 明确的服务边界:每个Segment ∈ 一个服务实例
  2. 独立的数据上报单元:一个Segment作为一个批次发送给OAP
  3. 天然的服务粒度指标:服务平均响应时间 = Segment的响应时间
  4. 进程边界标记:Segment之间的Reference标记了跨进程/跨线程关系

3. Span的三种类型及语义

3.1 EntrySpan(入口Span)

定义:标记一个外部请求进入当前服务的入口点。

// SkyWalking Agent自动捕获@RestControllerpublicclassOrderController{@PostMapping("/api/order/create")publicResultcreateOrder(@RequestBodyOrderDTOorder){// Agent在这里创建 EntrySpan// Span类型: EntrySpan// Span名称: POST:/api/order/create// 这是外部(HTTP/RPC)请求进入当前服务的"大门"returnorderService.create(order);}}

EntrySpan的特征

  • 永远是一个TraceSegment的第一个Span
  • 标记了这个服务对外提供的接口
  • 服务级别的响应时间 = EntrySpan的耗时

3.2 LocalSpan(本地Span)

定义:标记当前服务内部的方法调用,不涉及网络通信。

@ServicepublicclassOrderService{@Override@Trace(operationName="validateOrderLogic")// 可选:自定义追踪publicOrdercreate(OrderDTOdto){// Agent在这里创建 LocalSpan// Span类型: LocalSpan// 这是纯本地方法调用,不涉及任何外部依赖validateOrder(dto);// 本地调用calculateDiscount(dto);// 本地调用returnorderRepo.save(dto);// 这里会触发ExitSpan(DB)}privatevoidvalidateOrder(OrderDTOdto){// LocalSpan: validateOrderif(dto.getAmount()<=0){thrownewBusinessException("金额不能为0");}}}

LocalSpan的特征

  • 纯本地调用,不涉及网络
  • 帮助定位业务逻辑的性能瓶颈
  • 可选配置:plugin.springmvc.collect_http_params=false可以关闭LocalSpan以减少Agent开销

3.3 ExitSpan(出口Span)

定义:标记从当前服务发出的外部调用。

@RepositorypublicclassOrderRepository{@AutowiredprivateJdbcTemplatejdbc;publicOrdersave(Orderorder){// Agent在这里创建 ExitSpan// Span类型: ExitSpan// Component: MySQL/JDBI/PreparedStatement/executeUpdatejdbc.update("INSERT INTO orders(id, user_id, amount) VALUES(?,?,?)",order.getId(),order.getUserId(),order.getAmount());returnorder;}}// 另一个例子:调用外部REST服务@ServicepublicclassNotificationService{@AutowiredprivateRestTemplaterestTemplate;publicvoidsendSMS(Stringphone,Stringmessage){// Agent在这里创建 ExitSpan// Span类型: ExitSpan// Component: SpringRestTemplate// URL: http://sms-gateway/api/sendrestTemplate.postForObject("http://sms-gateway/api/send",newSMSRequest(phone,message),String.class);}}

ExitSpan的特征

  • 标记了"离开当前服务"的调用
  • 覆盖多种组件:HTTP/RPC/DB/Redis/MQ/…
  • 是定位外部依赖问题的关键

3.4 三种Span类型的完整对照

+------------------------------------------------------------------+ +------------------------------------------------------------------+ | Span类型 │ 方向 │ 典型场景 │ Component │ +------------------------------------------------------------------+ | EntrySpan │ 入 │ HTTP/RPC请求入口 │ Tomcat, │ | │ │ MQ消息消费入口 │ SpringMVC, │ | │ │ │ KafkaConsumer│ +------------------------------------------------------------------+ | LocalSpan │ 内部 │ 方法调用 │ 无 │ | │ │ 业务逻辑处理 │ │ | │ │ 本地计算 │ │ +------------------------------------------------------------------+ | ExitSpan │ 出 │ 数据库访问 │ MySQL, │ | │ │ 缓存操作 │ Redis, │ | │ │ HTTP/RPC调用下游 │ HttpClient, │ | │ │ MQ消息发送 │ KafkaProducer│ +------------------------------------------------------------------+

3.5 三种Span在代码中的创建方式

// SkyWalking Agent内部创建Span的逻辑(简化版)publicclassTracingContext{// 创建EntrySpan(HTTP请求到达时)publicAbstractSpancreateEntrySpan(StringoperationName){AbstractSpanentrySpan=newEntrySpan(spanIdGenerator.generate(),// spanIdparentSpanId,// 上游的parentSpanIdoperationName,// 如 "/api/order/create"owner// 所属的TraceSegment);entrySpan.setComponent(ComponentsDefine.TOMCAT);entrySpan.setLayer(SpanLayer.HTTP);activeSpanStack.push(entrySpan);returnentrySpan;}// 创建ExitSpan(调用外部时)publicAbstractSpancreateExitSpan(StringoperationName,StringremotePeer){AbstractSpanexitSpan=newExitSpan(spanIdGenerator.generate(),activeSpanStack.peek().getSpanId(),// 父Span = 当前活跃的operationName,// 如 "MySQL/JDBI/PreparedStatement/executeQuery"remotePeer,// 如 "192.168.1.100:3306"owner);exitSpan.setComponent(ComponentsDefine.MYSQL_JDBC_DRIVER);exitSpan.setLayer(SpanLayer.DB);activeSpanStack.push(exitSpan);returnexitSpan;}// 创建LocalSpan(方法调用时)publicAbstractSpancreateLocalSpan(StringoperationName){AbstractSpanlocalSpan=newLocalSpan(spanIdGenerator.generate(),activeSpanStack.peek().getSpanId(),operationName,// 如 "validateOrder"owner);// LocalSpan没有component和remotePeeractiveSpanStack.push(localSpan);returnlocalSpan;}}

4. 三种Span在实际Trace中的表现

4.1 一个完整的请求示例

+------------------------------------------------------------------+ | Trace: 用户下单的完整调用链 | +------------------------------------------------------------------+ | | | [gateway-service] TraceSegment-1 (3.2s) | | ┌──────────────────────────────────────────────────────│ │ | │ ★ EntrySpan: POST /api/order/create (3.2s) │ │ | │ │ ← HTTP请求到达Tomcat │ │ | │ │ │ │ | │ ├── ● LocalSpan: validateOrder (5ms) │ │ | │ │ ← 纯本地逻辑,没有网络IO │ │ | │ │ │ │ | │ ├── ▲ ExitSpan: Dubbo/orderService/create (200ms) │ │ | │ │ ← RPC调用下游订单服务 │ │ | │ │ ← 这个ExitSpan会通过Reference连接下一个Segment│ │ | │ │ │ │ | │ └── ▲ ExitSpan: Kafka/order-created (10ms) │ │ | │ ← 异步消息发送 │ │ | └──────────────────────────────────────────────────────│ │ | | | [order-service] TraceSegment-2 (1.5s) | | ┌──────────────────────────────────────────────────────│ │ | │ ★ EntrySpan: Dubbo/create (1.5s) │ │ | │ │ ← RPC请求到达(对应上面的ExitSpan) │ │ | │ │ │ │ | │ ├── ▲ ExitSpan: MySQL/INSERT orders (120ms) │ │ | │ │ ← 数据库写入 │ │ | │ │ │ │ | │ ├── ▲ ExitSpan: Redis/SET key (8ms) │ │ | │ │ ← 缓存写入 │ │ | │ │ │ │ | │ └── ● LocalSpan: sendPushNotification (50ms) │ │ | │ ← 本地推送通知(异步线程) │ │ | └──────────────────────────────────────────────────────│ │ | | | 图例: ★ EntrySpan ▲ ExitSpan ● LocalSpan | +------------------------------------------------------------------+

5. TraceSegment的设计意图详解

5.1 数据独立性

每个TraceSegment是独立收集、独立上报的。这解决了什么问题?

+------------------------------------------------------------------+ | TraceSegment的独立上报 | +------------------------------------------------------------------+ | | | 如果没有TraceSegment(二层模型): | | ┌──────────────────────────────────────────────────┐ │ | │ 所有Span混合上报,OAP需要做跨服务重组 │ │ | │ 如果一个服务的Span延迟了,整个Trace无法闭合 │ │ | └──────────────────────────────────────────────────┘ │ | | | 有了TraceSegment(三层模型): | | ┌──────────────────────────────────────────────────┐ │ | │ Segment-1 [gateway → OAP]: 3个Span,独立上报 │ │ | │ Segment-2 [order-svc → OAP]: 4个Span,独立上报 │ │ | │ │ │ | │ OAP端: │ │ | │ 1. 接收各Segment │ │ | │ 2. 根据Reference重组Trace │ │ | │ 3. Segment-2晚到没关系,Trace可部分展示 │ │ | └──────────────────────────────────────────────────┘ │ +------------------------------------------------------------------+

5.2 异常隔离

一个服务的崩溃不影响其他服务的数据完整性:

// 场景: B服务OOM,但A服务和C服务的Trace数据完好// 因为每个Segment独立上报!A-Segment[成功上报到OAP]B-Segment[OOMSegment丢失]← 只丢这一段的SpanC-Segment[成功上报到OAP]// 在SkyWalking UI中:// Trace仍然可展示,只是B服务的Span显示为灰色/虚线

5.3 天然的指标粒度

-- 有了TraceSegment,服务级别的指标计算变得简单-- 服务A的平均响应时间 = 所有属于服务A的Segment的延迟均值SELECTAVG(latency)FROMtrace_segmentWHEREservice_id='order-service'ANDtimestamp>NOW()-INTERVAL1HOUR;-- 如果只有Span,你需要规定哪个Span代表"服务响应时间"-- 有了Segment,服务边界天然存在

6. 总结

SkyWalking的三层模型(Trace → TraceSegment → Span)不是"叠床架屋",而是在Dapper二层模型基础上的工程性增强

  1. TraceSegment明确了服务边界:每个Segment=一个服务实例的一次执行
  2. 三种Span类型各司其职
    • EntrySpan:标记入口,定义服务响应时间
    • LocalSpan:标记内部方法,定位业务逻辑瓶颈
    • ExitSpan:标记出口,追踪外部依赖性能
  3. 独立上报+Reference重组:每个Segment独立上报,OAP端根据Reference重组完整Trace
  4. 异常隔离:一个服务崩溃不影响其他服务的数据完整性

这个三层模型是SkyWalking区别于Zipkin/Jaeger的核心差异化设计

下一篇文章,我们将看看这些Trace/Segment/Span在网络上传输时的数据格式——SkyWalking的数据传输协议。


上一篇【第29篇】Google Dapper论文精读——分布式追踪的
下一篇【第31篇】SkyWalking数据传输协议解析——探针与OAP的通信密码(未完待续)


版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/7/10 6:10:07

暗电流、暗信号与暗噪声:图像传感器三大“暗”概念全解析

在光学成像、图像传感器&#xff08;如 CMOS/CCD&#xff09;以及光电探测领域&#xff0c;暗电流、暗信号和暗噪声是三个经常被提及且容易混淆的概念。它们密切相关&#xff0c;但本质上处于不同的物理或数据层面。1. 暗电流 (Dark Current) 物理本质&#xff1a; 在完全黑暗、…

作者头像 李华
网站建设 2026/7/10 6:08:59

前端开发者的 C++ 实战补漏:Lambda 捕获与生命周期

1. 一次偶发崩溃引出的问题 做 N-API 扩展时&#xff0c;工作线程算完结果&#xff0c;用一个 lambda 把回调投递回主线程。写法和前端写 setTimeout 回调一样自然&#xff1a; 代码语言&#xff1a;cpp AI代码解释 controller->startDelayedTask([this]() {this->re…

作者头像 李华
网站建设 2026/7/10 6:08:23

高压安全隔离技术:ISOM8710与PIC18F87J50的工业应用

1. 高压安全隔离的核心挑战与解决方案选型在工业自动化、医疗设备和电力监控系统中&#xff0c;高压安全隔离是一个无法回避的关键需求。想象一下&#xff0c;当你的控制电路需要监测380V交流电机的工作状态时&#xff0c;如果没有可靠的隔离措施&#xff0c;一个意外的电压浪涌…

作者头像 李华
网站建设 2026/7/10 6:07:02

STM32L152ZD与ADS131M02高精度ADC系统设计与优化

1. 项目背景与核心需求在工业测量、医疗设备和便携式仪器等领域&#xff0c;高精度模数转换&#xff08;ADC&#xff09;是数据采集系统的核心。传统方案往往面临几个痛点&#xff1a;要么使用集成ADC但精度不足&#xff08;如STM32内置12位ADC&#xff09;&#xff0c;要么外接…

作者头像 李华