目录
[SkyWalking 源码复盘 ⑫:从一次请求到监控结果的完整源码地图](#SkyWalking 源码复盘 ⑫:从一次请求到监控结果的完整源码地图)
[一、SkyWalking 整体分层](#一、SkyWalking 整体分层)
[二、第一阶段:JVM 启动 Agent](#二、第一阶段:JVM 启动 Agent)
[四、第三阶段:HTTP 请求创建 EntrySpan](#四、第三阶段:HTTP 请求创建 EntrySpan)
[五、第四阶段:ContextManager 管理当前 Trace](#五、第四阶段:ContextManager 管理当前 Trace)
[Span 栈](#Span 栈)
[六、第五阶段:@Trace 创建业务 LocalSpan](#六、第五阶段:@Trace 创建业务 LocalSpan)
[七、第六阶段:HikariCP 与 JDBC 拆分数据库耗时](#七、第六阶段:HikariCP 与 JDBC 拆分数据库耗时)
[1. 获取连接](#1. 获取连接)
[2. 执行 SQL](#2. 执行 SQL)
[3. 归还连接](#3. 归还连接)
[SSH 隧道断开](#SSH 隧道断开)
[八、第七阶段:Feign 创建 ExitSpan 并传播上下文](#八、第七阶段:Feign 创建 ExitSpan 并传播上下文)
[九、第八阶段:TraceSegment 异步上报](#九、第八阶段:TraceSegment 异步上报)
[十、OAP 接收并解析 Segment](#十、OAP 接收并解析 Segment)
[十一、为什么一条 Trace 能产生仪表盘指标](#十一、为什么一条 Trace 能产生仪表盘指标)
[十三、原始 Trace 与 Metrics 是两条不同链路](#十三、原始 Trace 与 Metrics 是两条不同链路)
[1. %tid 输出 Trace ID](#1. %tid 输出 Trace ID)
[2. GRPCLogClientAppender 上传日志](#2. GRPCLogClientAppender 上传日志)
[控制台有 TID,但 UI 没日志](#控制台有 TID,但 UI 没日志)
[为什么 WebHook 是 JSON 数组](#为什么 WebHook 是 JSON 数组)
[十七、Trace Profiling 链路](#十七、Trace Profiling 链路)
[Dump Count](#Dump Count)
[Self Duration](#Self Duration)
[链路二:Metrics 与拓扑](#链路二:Metrics 与拓扑)
[1. UI 看不到服务](#1. UI 看不到服务)
[2. 服务有指标,但没有 Trace](#2. 服务有指标,但没有 Trace)
[3. Trace 断链](#3. Trace 断链)
[4. Trace 显示数据库慢](#4. Trace 显示数据库慢)
[5. 控制台有 TID,但 UI 没日志](#5. 控制台有 TID,但 UI 没日志)
[6. 告警不触发](#6. 告警不触发)
[7. Profiling 没数据](#7. Profiling 没数据)
[二十二、你目前对 SkyWalking 的掌握层次](#二十二、你目前对 SkyWalking 的掌握层次)
SkyWalking 源码复盘 ⑫:从一次请求到监控结果的完整源码地图
这一节把前面所有内容收束成一条完整主线。
以你的请求为例:
GET /order/query/1
最终可能产生:
服务监控指标
端点指标
分布式 Trace
服务拓扑
数据库调用记录
带 TID 的业务日志
日志与 Trace 关联
告警 WebHook
Trace Profiling 线程栈
这些并不是一个模块一次性完成的,而是由 Agent 采集、OAP 分析、存储、UI 查询 四个阶段共同完成。
一、SkyWalking 整体分层
┌─────────────────────────────────────────┐
│ 业务应用 │
│ order / account / storage / alarm │
└─────────────────────────────────────────┘
│
│ -javaagent
▼
┌─────────────────────────────────────────┐
│ Java Agent │
│ │
│ 插件匹配 │
│ 字节码增强 │
│ 创建 Span │
│ 传播 Trace Context │
│ 采集日志、JVM、Profiling 数据 │
│ 异步上报 │
└─────────────────────────────────────────┘
│
│ gRPC 11800
▼
┌─────────────────────────────────────────┐
│ OAP │
│ │
│ 接收 Segment / Log / Profile Snapshot │
│ 分析 Span │
│ 生成 Source │
│ OAL 聚合 Metrics │
│ 执行告警规则 │
│ 写入存储 │
└─────────────────────────────────────────┘
│
│ HTTP 12800
▼
┌─────────────────────────────────────────┐
│ SkyWalking UI 8080 │
│ │
│ 服务仪表盘 │
│ 拓扑图 │
│ Trace │
│ 日志 │
│ 告警 │
│ Profiling │
└─────────────────────────────────────────┘
端口必须记住:
| 端口 | 用途 |
|---|---|
11800 |
Agent 向 OAP 上报数据 |
12800 |
UI 查询 OAP |
8080 |
SkyWalking UI |
8084 |
你的告警接收服务 |
二、第一阶段:JVM 启动 Agent
你的启动参数类似:
-javaagent:H:\tools\...\skywalking-agent.jar
-Dskywalking.agent.service_name=order-service
-Dskywalking.collector.backend_service=127.0.0.1:11800
JVM 启动顺序:
JVM 启动
→ 加载 skywalking-agent.jar
→ 调用 SkyWalkingAgent.premain()
→ 读取 Agent 配置
→ 扫描插件
→ 构造 Byte Buddy Transformer
→ 注册 Instrumentation
→ 启动 Agent 内部服务
→ 执行业务应用 main()
核心逻辑可概括为:
public static void premain(
String agentArgs,
Instrumentation instrumentation
) {
initializeConfig(agentArgs);
loadPlugins();
installClassTransformer(instrumentation);
ServiceManager.INSTANCE.boot();
}
这里要区分:
Maven 依赖
→ 让项目能编译和调用 Toolkit API
-javaagent
→ 真正修改目标类字节码并采集数据
所以:
只添加 Maven 依赖
≠
已经启用 SkyWalking Agent
三、第二阶段:插件匹配与字节码增强
Agent 不会修改所有类。
它会根据插件规则匹配:
Tomcat
Feign
JDBC
HikariCP
Logback
@Trace
线程池
......
例如:
Tomcat 插件
→ 增强 StandardHostValve.invoke()
Feign 插件
→ 增强 HTTP Client execute()
HikariCP 插件
→ 增强 getConnection()、close()
JDBC 插件
→ 包装 PreparedStatement.execute()
Toolkit 插件
→ 增强带 @Trace 的方法
运行时可以近似理解为:
beforeMethod();
try {
return originalMethod();
} catch (Throwable e) {
handleException(e);
throw e;
} finally {
afterMethod();
}
SkyWalking 没有修改你的业务源代码,但 JVM 实际执行的方法已经插入了追踪逻辑。
四、第三阶段:HTTP 请求创建 EntrySpan
浏览器访问:
GET http://127.0.0.1:8083/order/query/1
进入:
Tomcat
→ StandardHostValve.invoke()
→ TomcatInvokeInterceptor
请求前:
ContextCarrier carrier = readHeaders(request);
AbstractSpan span =
ContextManager.createEntrySpan(
"GET:/order/query/1",
carrier
);
随后记录:
URL
HTTP Method
Tomcat Component
HTTP Layer
形成:
EntrySpan
└─ GET:/order/query/1
如果这是浏览器发起的首个请求:
请求头没有有效上游上下文
→ 创建一条新的 Trace
如果是上游微服务调用:
请求头中存在传播信息
→ 恢复上游 Trace
五、第四阶段:ContextManager 管理当前 Trace
第一次创建 Span 时:
CONTEXT.get() == null
→ 创建 TracingContext
→ 创建 TraceSegment
→ 写入 ThreadLocal
关键结构:
ContextManager
└─ ThreadLocal<TracingContext>
├─ TraceSegment
├─ activeSpanStack
└─ spanIdGenerator
不同 Tomcat 工作线程:
线程 A → TracingContext A
线程 B → TracingContext B
线程 C → TracingContext C
因此并发请求不会互相串链。
Span 栈
你的请求执行时:
GET:/order/query/1
→ queryOrder
→ HikariCP getConnection
→ JDBC execute
对应入栈:
Tomcat
Tomcat → queryOrder
Tomcat → queryOrder → HikariCP
Tomcat → queryOrder → JDBC
创建子 Span 时:
AbstractSpan parent = activeSpanStack.getLast();
newSpan.parentSpanId =
parent.getSpanId();
结束时必须严格后进先出:
先结束 JDBC
再结束 queryOrder
最后结束 Tomcat
Span 栈为空后:
TraceSegment 完成
→ ThreadLocal.remove()
六、第五阶段:@Trace 创建业务 LocalSpan
业务方法:
@Trace(operationName = "queryOrder")
@Tags({
@Tag(key = "orderId", value = "arg[0]"),
@Tag(key = "result", value = "returnedObj")
})
public OrderInfo queryOrder(Integer orderId) {
return orderMapper.selectById(orderId);
}
执行过程:
进入方法
→ createLocalSpan("queryOrder")
→ 读取 arg[0]
→ 执行业务逻辑
→ 读取 returnedObj
→ stopSpan()
结果:
GET:/order/query/1 EntrySpan
└─ queryOrder LocalSpan
├─ orderId = 1
└─ result = OrderInfo(...)
这里不是 Spring AOP,而是 Agent 直接增强方法字节码。
七、第六阶段:HikariCP 与 JDBC 拆分数据库耗时
数据库调用过程:
MyBatis
→ HikariDataSource.getConnection()
→ PreparedStatement.execute()
→ Connection.close()
形成:
queryOrder
├─ HikariCP/Connection/getConnection LocalSpan
├─ Mysql/JDBC/PreparedStatement/execute ExitSpan
└─ HikariCP/Connection/close LocalSpan
1. 获取连接
HikariCP/Connection/getConnection
回答:
应用拿到数据库连接花了多久?
慢时重点排查:
连接池耗尽
等待其他线程归还连接
数据库不可达
SSH 隧道断开
建立新连接过慢
连接泄漏
2. 执行 SQL
Mysql/JDBC/PreparedStatement/execute
回答:
从调用 JDBC 到数据库返回,整体花了多久?
慢时重点排查:
SQL 缺少索引
锁等待或死锁
扫描行数过多
数据库 CPU、磁盘 IO 高
网络延迟
SSH 隧道拥塞
结果集过大
3. 归还连接
HikariCP/Connection/close
一般不是关闭物理连接,而是:
清理连接状态
→ 归还连接池
→ 供其他请求复用
你的两个实验
SSH 隧道断开
HikariCP/getConnection ≈ 30 秒
结论:
SQL 尚未真正执行
主要慢在获取或创建连接
/order/slowSql?time=2
getConnection 很短
JDBC execute ≈ 2 秒
close 很短
结论:
连接正常
主要慢在 SQL 或数据库网络调用
八、第七阶段:Feign 创建 ExitSpan 并传播上下文
order-service 调用:
accountApi.deduct(...);
Feign 请求发送前:
ContextCarrier carrier =
new ContextCarrier();
AbstractSpan span =
ContextManager.createExitSpan(
"/account/deduct",
carrier,
"127.0.0.1:8082"
);
随后将 Carrier 中的数据写入 HTTP Header。
调用链:
order-service
├─ EntrySpan:POST:/order/create
└─ ExitSpan:/account/deduct
│
│ Trace Header
▼
account-service
└─ EntrySpan:PUT:/account/deduct
两个服务拥有:
相同 Trace ID
不同 Segment ID
不同 Span ID
不是共享同一个 Span 对象。
九、第八阶段:TraceSegment 异步上报
最后一个 Span 结束:
activeSpanStack 为空
→ TraceSegment.finish()
→ 通知 TraceSegmentServiceClient
业务线程只执行:
carrier.produce(traceSegment);
真正的处理由后台线程完成:
DataCarrier
→ Agent 后台消费者
→ TraceSegment.transform()
→ SegmentObject
→ gRPC
→ OAP 11800
这种设计避免:
OAP 慢
→ Tomcat 请求线程也被阻塞
缓冲区满了怎么办
默认策略偏向:
缓冲区有空间
→ 正常放入
缓冲区满
→ 丢弃部分 Segment
→ 不长期阻塞业务线程
SkyWalking 的取舍是:
业务稳定性
>
遥测数据绝对完整性
所以:
接口成功
≠
这条 Trace 一定成功上报
十、OAP 接收并解析 Segment
OAP 接收路径:
TraceSegmentReportServiceHandler
→ SegmentParserService
→ TraceAnalyzer
TraceAnalyzer 遍历:
EntrySpan
ExitSpan
LocalSpan
Segment
并通知不同监听器:
RPCAnalysisListener
SegmentAnalysisListener
VirtualServiceAnalysisListener
......
监听器再生成统一的 Source:
Service
ServiceInstance
Endpoint
ServiceRelation
EndpointRelation
DatabaseAccess
Segment
随后:
SourceReceiver
→ Dispatcher
→ OAL
→ Metrics
→ Storage
十一、为什么一条 Trace 能产生仪表盘指标
入口 Span:
GET:/order/slowSql
Duration ≈ 2100 ms
可以生成:
Service:order-service
ServiceInstance:order 实例
Endpoint:GET:/order/slowSql
OAL 再计算:
service_resp_time
service_sla
service_cpm
service_percentile
service_instance_resp_time
endpoint_resp_time
endpoint_sla
endpoint_cpm
endpoint_percentile
所以:
Trace Duration
是单次请求时间,而:
endpoint_resp_time
是一定时间窗口内的聚合指标。
十二、拓扑图是如何形成的
拓扑并不是直接打开一条 Trace 绘制。
真实过程:
大量 EntrySpan 和 ExitSpan
→ 生成 ServiceRelation
→ 按时间窗口聚合
→ UI 查询关系指标
→ 展示拓扑
例如:
order-service
→ account-service
→ MySQL
分别来自:
Feign ExitSpan / 下游 EntrySpan
JDBC Database ExitSpan
MySQL 没有安装 Java Agent,但 JDBC Span 中存在:
peer
database type
latency
SQL statement
所以 OAP 可以创建:
Virtual Database Service
并显示:
order-service
↓
127.0.0.1:13306
十三、原始 Trace 与 Metrics 是两条不同链路
同一个 Segment 会被不同监听器处理:
RPCAnalysisListener
→ 生成服务、端点、关系和指标
SegmentAnalysisListener
→ 根据采样策略决定是否保存原始 Trace
因此可能出现:
服务仪表盘有数据
拓扑图有关系
但 Trace 列表中找不到某次请求
原因可能是:
Metrics 已参与计算
但原始 Segment 因采样没有保存
这不是矛盾。
十四、日志关联链路
业务代码:
log.info("查询订单,orderId={}", orderId);
分成两部分。
1. %tid 输出 Trace ID
Logback Pattern
→ %tid
→ Agent 增强 PatternConverter
→ ContextManager.getGlobalTraceId()
→ 输出到控制台
例如:
[TID:abc123] 查询订单,orderId=1
它主要方便人肉排查。
2. GRPCLogClientAppender 上传日志
Appender 将日志转换为:
LogData
├─ service
├─ serviceInstance
├─ endpoint
├─ timestamp
├─ body
├─ level
├─ logger
├─ thread
└─ traceContext
├─ traceId
├─ segmentId
└─ spanId
然后:
DataCarrier
→ 后台线程
→ gRPC 11800
→ OAP Log Receiver
→ Log Analyzer
→ Storage
系统关联日志与 Trace,依靠的是结构化:
traceId + segmentId + spanId
不是简单搜索日志字符串中的 TID。
控制台有 TID,但 UI 没日志
常见原因:
只配置了 %tid,没有配置 gRPC Appender
Root Logger 未引用 SkyWalking Appender
服务没有挂 Agent
11800 不通
Agent 日志队列已满
OAP 日志 Receiver 异常
UI 时间或服务筛选错误
十五、告警链路
OAP 生成分钟级 Metrics 后:
Metrics
→ RunningRule.in()
→ 每个 AlarmEntity 维护一个 Window
→ AlarmCore 定时检查
→ MQE 表达式判断
→ AlarmMessage
→ WebhookCallback
例如:
expression: sum(endpoint_resp_time > 1000) >= 2
period: 10
silence-period: 10
含义:
最近 10 个分钟桶中
至少有 2 个分钟桶
端点平均响应时间超过 1000 ms
所以不是:
一条请求超过 1000 ms
→ 立即告警
而是:
分钟级指标进入滑动窗口
→ 时间窗口满足规则
→ 告警
为什么 WebHook 是 JSON 数组
一次定时检查可能同时产生:
端点告警
数据库告警
服务实例告警
OAP 统一保存到:
List<AlarmMessage>
然后:
gson.toJson(messages)
所以请求体是:
[
{
"scope": "ENDPOINT",
"name": "GET:/order/slowSql"
},
{
"scope": "DATABASE",
"name": "127.0.0.1:13306"
}
]
你的 Controller 应使用:
@RequestBody List<AlarmMessage> messages
十六、你的告警完整链路
PowerShell 循环请求
→ GET /order/slowSql?time=2
→ JDBC execute 约 2 秒
→ Agent 上报 Segment
→ OAP 生成分钟级 Metrics
→ RunningRule 窗口满足条件
→ 创建 List<AlarmMessage>
→ POST alarm-service:8084
→ AlarmController
→ 邮件服务
→ QQ SMTP
→ 邮件到达
WebHook 或邮件发送失败:
不会直接影响 order-service 请求
因为告警发生在 OAP 的独立链路中。
但 WebHook 本身仍应快速返回,邮件发送最好异步化。
十七、Trace Profiling 链路
任务创建:
UI
→ OAP 保存 ProfileTask
Agent 定时查询:
ProfileTaskChannelService
→ getProfileTaskCommands()
收到任务后:
校验任务
→ 等待 Start Time
→ 启动 ProfileTaskExecutionContext
新的请求到达:
创建 TracingContext
→ firstSpanOPName 与任务 Endpoint 匹配
→ 创建 ThreadProfiler
→ 状态 PENDING
请求运行超过:
Min Duration Threshold
后:
PENDING
→ PROFILING
Profiler 周期执行:
targetThread.getStackTrace();
生成:
taskId
segmentId
sequence
dumpTime
stack
异步上传 OAP。
OAP 再将大量采样栈合并为树。
三个核心指标
Dump Count
该方法出现在多少次线程栈采样中
不是方法调用次数。
Duration
该节点在连续采样区间中出现的近似总时间
不是精确方法计时。
Self Duration
节点 Duration
-
直接子节点 Duration 之和
表示尽量排除子调用后,当前方法自身消耗的近似时间。
判断方法
Duration 高,Self Duration 低
→ 主要慢在子调用
Duration 高,Self Duration 高
→ 当前方法自身更可能是热点
Socket read 高
→ 主要等待网络
JDBC Driver 高
→ 主要等待数据库
Thread.sleep 高
→ 线程主要处于休眠状态
十八、四条核心数据链
学完 SkyWalking 后,应该把它拆成四条链理解。
链路一:Trace
插件拦截
→ Entry / Local / Exit Span
→ TracingContext
→ TraceSegment
→ DataCarrier
→ gRPC
→ OAP
→ Trace UI
链路二:Metrics 与拓扑
Span
→ TraceAnalyzer
→ Source
→ OAL
→ Metrics
→ Storage
→ Dashboard / Topology
链路三:日志
ILoggingEvent
→ %tid
→ GRPCLogClientAppender
→ LogData
→ gRPC
→ OAP Log Analyzer
→ Log UI
链路四:告警
Metrics
→ RunningRule
→ Window
→ MQE Expression
→ AlarmMessage
→ WebHook
→ 邮件
Profiling 则是第五条专项诊断链:
ProfileTask
→ Endpoint 匹配
→ Thread Stack Sampling
→ Snapshot
→ OAP 合并分析
十九、故障排查的标准顺序
1. UI 看不到服务
先检查:
服务是否挂载 -javaagent
service_name 是否正确
Agent 是否启动成功
Agent 到 OAP 11800 是否连通
是否真正访问过接口
UI 时间范围是否正确
2. 服务有指标,但没有 Trace
检查:
是否被采样丢弃
Agent Segment 缓冲区是否满
OAP Trace 存储是否正常
Trace 时间范围和服务筛选
请求是否真正经过支持的插件
3. Trace 断链
检查:
调用方是否创建 ExitSpan
是否注入跨进程 Header
下游是否挂载 Agent
下游是否读取 Header
是否经过不支持的客户端或网关
异步线程是否传播上下文
4. Trace 显示数据库慢
先区分:
HikariCP/getConnection 慢
还是
JDBC/execute 慢
这是你当前已经掌握的高价值诊断能力。
5. 控制台有 TID,但 UI 没日志
检查:
GRPCLogClientAppender
Root Logger
Agent Activation
11800
OAP 日志接收器
时间与服务筛选
6. 告警不触发
检查:
告警 Metric 名称是否正确
表达式是否正确
period 是否满足
请求是否跨多个分钟桶
规则是否限定了 include-names
alarm-settings.yml 是否被 OAP 加载
Webhook URL 是否正确
alarm-service 是否监听 8084
7. Profiling 没数据
检查:
Agent Profiling 是否启用
OAP receiver-profile 是否启用
任务 Endpoint 是否与真实 Operation Name 一致
任务是否在有效时间内
请求是否超过 Min Duration Threshold
Max Sampling Count 是否已达到
请求是否在任务创建后重新发起
二十、整套源码中的核心类
| 领域 | 核心类 |
|---|---|
| Agent 启动 | SkyWalkingAgent |
| 插件发现 | PluginFinder |
| 字节码增强 | Transformer |
| 上下文入口 | ContextManager |
| 本地追踪上下文 | TracingContext |
| 一段本地追踪 | TraceSegment |
| HTTP 入口 | Tomcat Interceptor |
| 跨服务出口 | Feign/HTTP Client Interceptor |
| JDBC 追踪 | JDBC Statement Wrapper |
| HikariCP | Pooling Interceptor |
| Toolkit | Trace Annotation Interceptor |
| Trace 上报 | TraceSegmentServiceClient |
| OAP Trace 接收 | TraceSegmentReportServiceHandler |
| OAP 解析 | TraceAnalyzer |
| 服务关系分析 | RPCAnalysisListener |
| 日志上传 | LogReportServiceClient |
| 告警核心 | AlarmCore、RunningRule |
| Profiling 执行 | ProfileTaskExecutionService |
| 线程采样 | ThreadProfiler |
| Profiling 分析 | ProfileAnalyzer |
二十一、一段完整的面试回答
面试官问:
请整体介绍 SkyWalking 的工作原理。
可以回答:
SkyWalking 主要由 Java Agent、OAP、存储和 UI 组成。Java 应用通过
-javaagent在启动阶段执行 Agent 的premain,Agent 加载插件并使用 Byte Buddy 与 Instrumentation 对 Tomcat、Feign、JDBC、连接池等框架类进行字节码增强。请求进入 Tomcat 时创建 EntrySpan,业务内部通过 Toolkit 可以创建 LocalSpan,对外调用 Feign 或数据库时创建 ExitSpan。ContextManager使用 ThreadLocal 为每个请求线程维护独立的TracingContext,并通过活动 Span 栈确定父子关系。跨服务时,调用方将 Trace 上下文注入 HTTP Header,下游从 Header 中恢复上下文,从而生成属于同一 Trace 的不同 Segment。
当所有 Span 结束后,Agent 将 TraceSegment 放入内存缓冲区,由后台线程转换成 Protobuf 对象并通过 gRPC 上报 OAP,避免网络发送阻塞业务线程。OAP 对 Segment 中的 Entry、Exit 和 Local Span 进行分析,生成 Service、Endpoint、ServiceRelation、DatabaseAccess 等 Source,再通过 OAL 聚合成响应时间、成功率、CPM、百分位等 Metrics,用于仪表盘、拓扑和告警。日志可通过 Toolkit 获取 Trace ID,并以结构化 LogData 携带 Trace ID、Segment ID 和 Span ID 上传,实现日志与链路关联。告警模块对分钟级 Metrics 维护滑动窗口,规则命中后通过 WebHook 通知外部系统。对于 Span 内部难以定位的慢方法,还可以通过 Trace Profiling 周期采样目标请求线程栈,由 OAP 合并计算 Duration、Self Duration 和 Dump Count。
二十二、你目前对 SkyWalking 的掌握层次
现在已经不只是会:
启动 OAP
打开 UI
查看 Trace
而是已经贯通了:
-javaagent 启动
插件匹配
字节码增强
Entry / Local / Exit Span
ThreadLocal 上下文
Span 栈
跨服务传播
数据库分层诊断
Segment 异步上报
OAP Source 与 Metrics
服务拓扑
日志关联
告警时间窗口
Trace Profiling
按后端面试标准,可以归纳为三个层次:
| 层次 | 当前状态 |
|---|---|
| 会部署和使用 | 已掌握 |
| 会根据 Trace 定位问题 | 已掌握 |
| 能解释 Agent 与 OAP 核心原理 | 已形成完整主线 |
| 能独立修改 Agent/OAP 源码 | 尚需专项源码工程训练 |
二十三、最终记忆图
-javaagent
↓
premain
↓
配置加载 + 插件扫描
↓
Byte Buddy + Instrumentation
↓
框架类被增强
↓
HTTP 请求进入
↓
EntrySpan
↓
LocalSpan / ExitSpan
↓
ContextManager + ThreadLocal + Span Stack
↓
跨服务 ContextCarrier
↓
TraceSegment
↓
Agent 内存队列
↓
gRPC 11800
↓
OAP TraceAnalyzer
↓
Source
↓
OAL Metrics
├─ Dashboard
├─ Topology
├─ Alarm
└─ Storage
同时:
业务日志
→ LogData
→ TraceContext
→ OAP 日志关联
Profiling Task
→ Thread.getStackTrace()
→ Snapshot
→ OAP 合并分析
下一阶段建议不再继续平铺源码,而是进入 SkyWalking 面试拷打与故障实战复盘:从 Agent 不上报、链路断裂、连接池耗尽、慢 SQL、日志丢失、告警不触发等场景,要求你先判断问题层级,再给出排查顺序。