控源碼深度解析)
目錄SkyWalking 源碼復(fù)盤(pán) ?從一次請(qǐng)求到監(jiān)控結(jié)果的完整源碼地圖一、SkyWalking 整體分層二、第一階段JVM 啟動(dòng) Agent三、第二階段插件匹配與字節(jié)碼增強(qiáng)四、第三階段HTTP 請(qǐng)求創(chuàng)建 EntrySpan五、第四階段ContextManager 管理當(dāng)前 TraceSpan 棧六、第五階段Trace 創(chuàng)建業(yè)務(wù) LocalSpan七、第六階段HikariCP 與 JDBC 拆分?jǐn)?shù)據(jù)庫(kù)耗時(shí)1. 獲取連接2. 執(zhí)行 SQL3. 歸還連接你的兩個(gè)實(shí)驗(yàn)SSH 隧道斷開(kāi)/order/slowSql?time2八、第七階段Feign 創(chuàng)建 ExitSpan 并傳播上下文九、第八階段TraceSegment 異步上報(bào)緩沖區(qū)滿了怎么辦十、OAP 接收并解析 Segment十一、為什么一條 Trace 能產(chǎn)生儀表盤(pán)指標(biāo)十二、拓?fù)鋱D是如何形成的十三、原始 Trace 與 Metrics 是兩條不同鏈路十四、日志關(guān)聯(lián)鏈路1. %tid 輸出 Trace ID2. GRPCLogClientAppender 上傳日志控制臺(tái)有 TID但 UI 沒(méi)日志十五、告警鏈路為什么 WebHook 是 JSON 數(shù)組十六、你的告警完整鏈路十七、Trace Profiling 鏈路三個(gè)核心指標(biāo)Dump CountDurationSelf Duration判斷方法十八、四條核心數(shù)據(jù)鏈鏈路一Trace鏈路二Metrics 與拓?fù)滏溌啡罩炬溌匪母婢?、故障排查的?biāo)準(zhǔn)順序1. UI 看不到服務(wù)2. 服務(wù)有指標(biāo)但沒(méi)有 Trace3. Trace 斷鏈4. Trace 顯示數(shù)據(jù)庫(kù)慢5. 控制臺(tái)有 TID但 UI 沒(méi)日志6. 告警不觸發(fā)7. Profiling 沒(méi)數(shù)據(jù)二十、整套源碼中的核心類二十一、一段完整的面試回答二十二、你目前對(duì) SkyWalking 的掌握層次二十三、最終記憶圖SkyWalking 源碼復(fù)盤(pán) ?從一次請(qǐng)求到監(jiān)控結(jié)果的完整源碼地圖這一節(jié)把前面所有內(nèi)容收束成一條完整主線。以你的請(qǐng)求為例GET /order/query/1最終可能產(chǎn)生服務(wù)監(jiān)控指標(biāo) 端點(diǎn)指標(biāo) 分布式 Trace 服務(wù)拓?fù)?數(shù)據(jù)庫(kù)調(diào)用記錄 帶 TID 的業(yè)務(wù)日志 日志與 Trace 關(guān)聯(lián) 告警 WebHook Trace Profiling 線程棧這些并不是一個(gè)模塊一次性完成的而是由Agent 采集、OAP 分析、存儲(chǔ)、UI 查詢四個(gè)階段共同完成。一、SkyWalking 整體分層┌─────────────────────────────────────────┐ │ 業(yè)務(wù)應(yīng)用 │ │ order / account / storage / alarm │ └─────────────────────────────────────────┘ │ │ -javaagent ▼ ┌─────────────────────────────────────────┐ │ Java Agent │ │ │ │ 插件匹配 │ │ 字節(jié)碼增強(qiáng) │ │ 創(chuàng)建 Span │ │ 傳播 Trace Context │ │ 采集日志、JVM、Profiling 數(shù)據(jù) │ │ 異步上報(bào) │ └─────────────────────────────────────────┘ │ │ gRPC 11800 ▼ ┌─────────────────────────────────────────┐ │ OAP │ │ │ │ 接收 Segment / Log / Profile Snapshot │ │ 分析 Span │ │ 生成 Source │ │ OAL 聚合 Metrics │ │ 執(zhí)行告警規(guī)則 │ │ 寫(xiě)入存儲(chǔ) │ └─────────────────────────────────────────┘ │ │ HTTP 12800 ▼ ┌─────────────────────────────────────────┐ │ SkyWalking UI 8080 │ │ │ │ 服務(wù)儀表盤(pán) │ │ 拓?fù)鋱D │ │ Trace │ │ 日志 │ │ 告警 │ │ Profiling │ └─────────────────────────────────────────┘端口必須記住端口用途11800Agent 向 OAP 上報(bào)數(shù)據(jù)12800UI 查詢 OAP8080SkyWalking UI8084你的告警接收服務(wù)二、第一階段JVM 啟動(dòng) Agent你的啟動(dòng)參數(shù)類似-javaagent:H:\tools\...\skywalking-agent.jar -Dskywalking.agent.service_nameorder-service -Dskywalking.collector.backend_service127.0.0.1:11800JVM 啟動(dòng)順序JVM 啟動(dòng) → 加載 skywalking-agent.jar → 調(diào)用 SkyWalkingAgent.premain() → 讀取 Agent 配置 → 掃描插件 → 構(gòu)造 Byte Buddy Transformer → 注冊(cè) Instrumentation → 啟動(dòng) Agent 內(nèi)部服務(wù) → 執(zhí)行業(yè)務(wù)應(yīng)用 main()核心邏輯可概括為public static void premain( String agentArgs, Instrumentation instrumentation ) { initializeConfig(agentArgs); loadPlugins(); installClassTransformer(instrumentation); ServiceManager.INSTANCE.boot(); }這里要區(qū)分Maven 依賴 → 讓項(xiàng)目能編譯和調(diào)用 Toolkit API -javaagent → 真正修改目標(biāo)類字節(jié)碼并采集數(shù)據(jù)所以只添加 Maven 依賴 ≠ 已經(jīng)啟用 SkyWalking Agent三、第二階段插件匹配與字節(jié)碼增強(qiáng)Agent 不會(huì)修改所有類。它會(huì)根據(jù)插件規(guī)則匹配Tomcat Feign JDBC HikariCP Logback Trace 線程池 ……例如Tomcat 插件 → 增強(qiáng) StandardHostValve.invoke() Feign 插件 → 增強(qiáng) HTTP Client execute() HikariCP 插件 → 增強(qiáng) getConnection()、close() JDBC 插件 → 包裝 PreparedStatement.execute() Toolkit 插件 → 增強(qiáng)帶 Trace 的方法運(yùn)行時(shí)可以近似理解為beforeMethod(); try { return originalMethod(); } catch (Throwable e) { handleException(e); throw e; } finally { afterMethod(); }SkyWalking 沒(méi)有修改你的業(yè)務(wù)源代碼但 JVM 實(shí)際執(zhí)行的方法已經(jīng)插入了追蹤邏輯。四、第三階段HTTP 請(qǐng)求創(chuàng)建 EntrySpan瀏覽器訪問(wèn)GET http://127.0.0.1:8083/order/query/1進(jìn)入Tomcat → StandardHostValve.invoke() → TomcatInvokeInterceptor請(qǐng)求前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如果這是瀏覽器發(fā)起的首個(gè)請(qǐng)求請(qǐng)求頭沒(méi)有有效上游上下文 → 創(chuàng)建一條新的 Trace如果是上游微服務(wù)調(diào)用請(qǐng)求頭中存在傳播信息 → 恢復(fù)上游 Trace五、第四階段ContextManager管理當(dāng)前 Trace第一次創(chuàng)建 Span 時(shí)CONTEXT.get() null → 創(chuàng)建 TracingContext → 創(chuàng)建 TraceSegment → 寫(xiě)入 ThreadLocal關(guān)鍵結(jié)構(gòu)ContextManager └─ ThreadLocalTracingContext ├─ TraceSegment ├─ activeSpanStack └─ spanIdGenerator不同 Tomcat 工作線程線程 A → TracingContext A 線程 B → TracingContext B 線程 C → TracingContext C因此并發(fā)請(qǐng)求不會(huì)互相串鏈。Span 棧你的請(qǐng)求執(zhí)行時(shí)GET:/order/query/1 → queryOrder → HikariCP getConnection → JDBC execute對(duì)應(yīng)入棧Tomcat Tomcat → queryOrder Tomcat → queryOrder → HikariCP Tomcat → queryOrder → JDBC創(chuàng)建子 Span 時(shí)AbstractSpan parent activeSpanStack.getLast(); newSpan.parentSpanId parent.getSpanId();結(jié)束時(shí)必須嚴(yán)格后進(jìn)先出先結(jié)束 JDBC 再結(jié)束 queryOrder 最后結(jié)束 TomcatSpan 棧為空后TraceSegment 完成 → ThreadLocal.remove()六、第五階段Trace創(chuàng)建業(yè)務(wù) LocalSpan業(yè)務(wù)方法Trace(operationName queryOrder) Tags({ Tag(key orderId, value arg[0]), Tag(key result, value returnedObj) }) public OrderInfo queryOrder(Integer orderId) { return orderMapper.selectById(orderId); }執(zhí)行過(guò)程進(jìn)入方法 → createLocalSpan(queryOrder) → 讀取 arg[0] → 執(zhí)行業(yè)務(wù)邏輯 → 讀取 returnedObj → stopSpan()結(jié)果GET:/order/query/1 EntrySpan └─ queryOrder LocalSpan ├─ orderId 1 └─ result OrderInfo(...)這里不是 Spring AOP而是 Agent 直接增強(qiáng)方法字節(jié)碼。七、第六階段HikariCP 與 JDBC 拆分?jǐn)?shù)據(jù)庫(kù)耗時(shí)數(shù)據(jù)庫(kù)調(diào)用過(guò)程MyBatis → HikariDataSource.getConnection() → PreparedStatement.execute() → Connection.close()形成queryOrder ├─ HikariCP/Connection/getConnection LocalSpan ├─ Mysql/JDBC/PreparedStatement/execute ExitSpan └─ HikariCP/Connection/close LocalSpan1. 獲取連接HikariCP/Connection/getConnection回答應(yīng)用拿到數(shù)據(jù)庫(kù)連接花了多久慢時(shí)重點(diǎn)排查連接池耗盡 等待其他線程歸還連接 數(shù)據(jù)庫(kù)不可達(dá) SSH 隧道斷開(kāi) 建立新連接過(guò)慢 連接泄漏2. 執(zhí)行 SQLMysql/JDBC/PreparedStatement/execute回答從調(diào)用 JDBC 到數(shù)據(jù)庫(kù)返回整體花了多久慢時(shí)重點(diǎn)排查SQL 缺少索引 鎖等待或死鎖 掃描行數(shù)過(guò)多 數(shù)據(jù)庫(kù) CPU、磁盤(pán) IO 高 網(wǎng)絡(luò)延遲 SSH 隧道擁塞 結(jié)果集過(guò)大3. 歸還連接HikariCP/Connection/close一般不是關(guān)閉物理連接而是清理連接狀態(tài) → 歸還連接池 → 供其他請(qǐng)求復(fù)用你的兩個(gè)實(shí)驗(yàn)SSH 隧道斷開(kāi)HikariCP/getConnection ≈ 30 秒結(jié)論SQL 尚未真正執(zhí)行 主要慢在獲取或創(chuàng)建連接/order/slowSql?time2getConnection 很短 JDBC execute ≈ 2 秒 close 很短結(jié)論連接正常 主要慢在 SQL 或數(shù)據(jù)庫(kù)網(wǎng)絡(luò)調(diào)用八、第七階段Feign 創(chuàng)建 ExitSpan 并傳播上下文order-service調(diào)用accountApi.deduct(...);Feign 請(qǐng)求發(fā)送前ContextCarrier carrier new ContextCarrier(); AbstractSpan span ContextManager.createExitSpan( /account/deduct, carrier, 127.0.0.1:8082 );隨后將 Carrier 中的數(shù)據(jù)寫(xiě)入 HTTP Header。調(diào)用鏈order-service ├─ EntrySpanPOST:/order/create └─ ExitSpan/account/deduct │ │ Trace Header ▼ account-service └─ EntrySpanPUT:/account/deduct兩個(gè)服務(wù)擁有相同 Trace ID 不同 Segment ID 不同 Span ID不是共享同一個(gè) Span 對(duì)象。九、第八階段TraceSegment 異步上報(bào)最后一個(gè) Span 結(jié)束activeSpanStack 為空 → TraceSegment.finish() → 通知 TraceSegmentServiceClient業(yè)務(wù)線程只執(zhí)行carrier.produce(traceSegment);真正的處理由后臺(tái)線程完成DataCarrier → Agent 后臺(tái)消費(fèi)者 → TraceSegment.transform() → SegmentObject → gRPC → OAP 11800這種設(shè)計(jì)避免OAP 慢 → Tomcat 請(qǐng)求線程也被阻塞緩沖區(qū)滿了怎么辦默認(rèn)策略偏向緩沖區(qū)有空間 → 正常放入 緩沖區(qū)滿 → 丟棄部分 Segment → 不長(zhǎng)期阻塞業(yè)務(wù)線程SkyWalking 的取舍是業(yè)務(wù)穩(wěn)定性 遙測(cè)數(shù)據(jù)絕對(duì)完整性所以接口成功 ≠ 這條 Trace 一定成功上報(bào)十、OAP 接收并解析 SegmentOAP 接收路徑TraceSegmentReportServiceHandler → SegmentParserService → TraceAnalyzerTraceAnalyzer遍歷EntrySpan ExitSpan LocalSpan Segment并通知不同監(jiān)聽(tīng)器RPCAnalysisListener SegmentAnalysisListener VirtualServiceAnalysisListener ……監(jiān)聽(tīng)器再生成統(tǒng)一的 SourceService ServiceInstance Endpoint ServiceRelation EndpointRelation DatabaseAccess Segment隨后SourceReceiver → Dispatcher → OAL → Metrics → Storage十一、為什么一條 Trace 能產(chǎn)生儀表盤(pán)指標(biāo)入口 SpanGET:/order/slowSql Duration ≈ 2100 ms可以生成Serviceorder-service ServiceInstanceorder 實(shí)例 EndpointGET:/order/slowSqlOAL 再計(jì)算service_resp_time service_sla service_cpm service_percentile service_instance_resp_time endpoint_resp_time endpoint_sla endpoint_cpm endpoint_percentile所以Trace Duration是單次請(qǐng)求時(shí)間而endpoint_resp_time是一定時(shí)間窗口內(nèi)的聚合指標(biāo)。十二、拓?fù)鋱D是如何形成的拓?fù)洳⒉皇侵苯哟蜷_(kāi)一條 Trace 繪制。真實(shí)過(guò)程大量 EntrySpan 和 ExitSpan → 生成 ServiceRelation → 按時(shí)間窗口聚合 → UI 查詢關(guān)系指標(biāo) → 展示拓?fù)淅鏾rder-service → account-service → MySQL分別來(lái)自Feign ExitSpan / 下游 EntrySpan JDBC Database ExitSpanMySQL 沒(méi)有安裝 Java Agent但 JDBC Span 中存在peer database type latency SQL statement所以 OAP 可以創(chuàng)建Virtual Database Service并顯示order-service ↓ 127.0.0.1:13306十三、原始 Trace 與 Metrics 是兩條不同鏈路同一個(gè) Segment 會(huì)被不同監(jiān)聽(tīng)器處理RPCAnalysisListener → 生成服務(wù)、端點(diǎn)、關(guān)系和指標(biāo) SegmentAnalysisListener → 根據(jù)采樣策略決定是否保存原始 Trace因此可能出現(xiàn)服務(wù)儀表盤(pán)有數(shù)據(jù) 拓?fù)鋱D有關(guān)系 但 Trace 列表中找不到某次請(qǐng)求原因可能是Metrics 已參與計(jì)算 但原始 Segment 因采樣沒(méi)有保存這不是矛盾。十四、日志關(guān)聯(lián)鏈路業(yè)務(wù)代碼log.info(查詢訂單orderId{}, orderId);分成兩部分。1.%tid輸出 Trace IDLogback Pattern → %tid → Agent 增強(qiáng) PatternConverter → ContextManager.getGlobalTraceId() → 輸出到控制臺(tái)例如[TID:abc123] 查詢訂單orderId1它主要方便人肉排查。2.GRPCLogClientAppender上傳日志Appender 將日志轉(zhuǎn)換為L(zhǎng)ogData ├─ service ├─ serviceInstance ├─ endpoint ├─ timestamp ├─ body ├─ level ├─ logger ├─ thread └─ traceContext ├─ traceId ├─ segmentId └─ spanId然后DataCarrier → 后臺(tái)線程 → gRPC 11800 → OAP Log Receiver → Log Analyzer → Storage系統(tǒng)關(guān)聯(lián)日志與 Trace依靠的是結(jié)構(gòu)化traceId segmentId spanId不是簡(jiǎn)單搜索日志字符串中的 TID。控制臺(tái)有 TID但 UI 沒(méi)日志常見(jiàn)原因只配置了 %tid沒(méi)有配置 gRPC Appender Root Logger 未引用 SkyWalking Appender 服務(wù)沒(méi)有掛 Agent 11800 不通 Agent 日志隊(duì)列已滿 OAP 日志 Receiver 異常 UI 時(shí)間或服務(wù)篩選錯(cuò)誤十五、告警鏈路OAP 生成分鐘級(jí) Metrics 后Metrics → RunningRule.in() → 每個(gè) AlarmEntity 維護(hù)一個(gè) Window → AlarmCore 定時(shí)檢查 → MQE 表達(dá)式判斷 → AlarmMessage → WebhookCallback例如expression: sum(endpoint_resp_time 1000) 2 period: 10 silence-period: 10含義最近 10 個(gè)分鐘桶中 至少有 2 個(gè)分鐘桶 端點(diǎn)平均響應(yīng)時(shí)間超過(guò) 1000 ms所以不是一條請(qǐng)求超過(guò) 1000 ms → 立即告警而是分鐘級(jí)指標(biāo)進(jìn)入滑動(dòng)窗口 → 時(shí)間窗口滿足規(guī)則 → 告警為什么 WebHook 是 JSON 數(shù)組一次定時(shí)檢查可能同時(shí)產(chǎn)生端點(diǎn)告警 數(shù)據(jù)庫(kù)告警 服務(wù)實(shí)例告警OAP 統(tǒng)一保存到ListAlarmMessage然后gson.toJson(messages)所以請(qǐng)求體是[ { scope: ENDPOINT, name: GET:/order/slowSql }, { scope: DATABASE, name: 127.0.0.1:13306 } ]你的 Controller 應(yīng)使用RequestBody ListAlarmMessage messages十六、你的告警完整鏈路PowerShell 循環(huán)請(qǐng)求 → GET /order/slowSql?time2 → JDBC execute 約 2 秒 → Agent 上報(bào) Segment → OAP 生成分鐘級(jí) Metrics → RunningRule 窗口滿足條件 → 創(chuàng)建 ListAlarmMessage → POST alarm-service:8084 → AlarmController → 郵件服務(wù) → QQ SMTP → 郵件到達(dá)WebHook 或郵件發(fā)送失敗不會(huì)直接影響 order-service 請(qǐng)求因?yàn)楦婢l(fā)生在 OAP 的獨(dú)立鏈路中。但 WebHook 本身仍應(yīng)快速返回郵件發(fā)送最好異步化。十七、Trace Profiling 鏈路任務(wù)創(chuàng)建UI → OAP 保存 ProfileTaskAgent 定時(shí)查詢ProfileTaskChannelService → getProfileTaskCommands()收到任務(wù)后校驗(yàn)任務(wù) → 等待 Start Time → 啟動(dòng) ProfileTaskExecutionContext新的請(qǐng)求到達(dá)創(chuàng)建 TracingContext → firstSpanOPName 與任務(wù) Endpoint 匹配 → 創(chuàng)建 ThreadProfiler → 狀態(tài) PENDING請(qǐng)求運(yùn)行超過(guò)Min Duration Threshold后PENDING → PROFILINGProfiler 周期執(zhí)行targetThread.getStackTrace();生成taskId segmentId sequence dumpTime stack異步上傳 OAP。OAP 再將大量采樣棧合并為樹(shù)。三個(gè)核心指標(biāo)Dump Count該方法出現(xiàn)在多少次線程棧采樣中不是方法調(diào)用次數(shù)。Duration該節(jié)點(diǎn)在連續(xù)采樣區(qū)間中出現(xiàn)的近似總時(shí)間不是精確方法計(jì)時(shí)。Self Duration節(jié)點(diǎn) Duration - 直接子節(jié)點(diǎn) Duration 之和表示盡量排除子調(diào)用后當(dāng)前方法自身消耗的近似時(shí)間。判斷方法Duration 高Self Duration 低 → 主要慢在子調(diào)用 Duration 高Self Duration 高 → 當(dāng)前方法自身更可能是熱點(diǎn) Socket read 高 → 主要等待網(wǎng)絡(luò) JDBC Driver 高 → 主要等待數(shù)據(jù)庫(kù) Thread.sleep 高 → 線程主要處于休眠狀態(tài)十八、四條核心數(shù)據(jù)鏈學(xué)完 SkyWalking 后應(yīng)該把它拆成四條鏈理解。鏈路一Trace插件攔截 → Entry / Local / Exit Span → TracingContext → TraceSegment → DataCarrier → gRPC → OAP → Trace UI鏈路二Metrics 與拓?fù)銼pan → 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 則是第五條專項(xiàng)診斷鏈ProfileTask → Endpoint 匹配 → Thread Stack Sampling → Snapshot → OAP 合并分析十九、故障排查的標(biāo)準(zhǔn)順序1. UI 看不到服務(wù)先檢查服務(wù)是否掛載 -javaagent service_name 是否正確 Agent 是否啟動(dòng)成功 Agent 到 OAP 11800 是否連通 是否真正訪問(wèn)過(guò)接口 UI 時(shí)間范圍是否正確2. 服務(wù)有指標(biāo)但沒(méi)有 Trace檢查是否被采樣丟棄 Agent Segment 緩沖區(qū)是否滿 OAP Trace 存儲(chǔ)是否正常 Trace 時(shí)間范圍和服務(wù)篩選 請(qǐng)求是否真正經(jīng)過(guò)支持的插件3. Trace 斷鏈檢查調(diào)用方是否創(chuàng)建 ExitSpan 是否注入跨進(jìn)程 Header 下游是否掛載 Agent 下游是否讀取 Header 是否經(jīng)過(guò)不支持的客戶端或網(wǎng)關(guān) 異步線程是否傳播上下文4. Trace 顯示數(shù)據(jù)庫(kù)慢先區(qū)分HikariCP/getConnection 慢 還是 JDBC/execute 慢這是你當(dāng)前已經(jīng)掌握的高價(jià)值診斷能力。5. 控制臺(tái)有 TID但 UI 沒(méi)日志檢查GRPCLogClientAppender Root Logger Agent Activation 11800 OAP 日志接收器 時(shí)間與服務(wù)篩選6. 告警不觸發(fā)檢查告警 Metric 名稱是否正確 表達(dá)式是否正確 period 是否滿足 請(qǐng)求是否跨多個(gè)分鐘桶 規(guī)則是否限定了 include-names alarm-settings.yml 是否被 OAP 加載 Webhook URL 是否正確 alarm-service 是否監(jiān)聽(tīng) 80847. Profiling 沒(méi)數(shù)據(jù)檢查Agent Profiling 是否啟用 OAP receiver-profile 是否啟用 任務(wù) Endpoint 是否與真實(shí) Operation Name 一致 任務(wù)是否在有效時(shí)間內(nèi) 請(qǐng)求是否超過(guò) Min Duration Threshold Max Sampling Count 是否已達(dá)到 請(qǐng)求是否在任務(wù)創(chuàng)建后重新發(fā)起二十、整套源碼中的核心類領(lǐng)域核心類Agent 啟動(dòng)SkyWalkingAgent插件發(fā)現(xiàn)PluginFinder字節(jié)碼增強(qiáng)Transformer上下文入口ContextManager本地追蹤上下文TracingContext一段本地追蹤TraceSegmentHTTP 入口Tomcat Interceptor跨服務(wù)出口Feign/HTTP Client InterceptorJDBC 追蹤JDBC Statement WrapperHikariCPPooling InterceptorToolkitTrace Annotation InterceptorTrace 上報(bào)TraceSegmentServiceClientOAP Trace 接收TraceSegmentReportServiceHandlerOAP 解析TraceAnalyzer服務(wù)關(guān)系分析RPCAnalysisListener日志上傳LogReportServiceClient告警核心AlarmCore、RunningRuleProfiling 執(zhí)行ProfileTaskExecutionService線程采樣ThreadProfilerProfiling 分析ProfileAnalyzer二十一、一段完整的面試回答面試官問(wèn)請(qǐng)整體介紹 SkyWalking 的工作原理??梢曰卮餝kyWalking 主要由 Java Agent、OAP、存儲(chǔ)和 UI 組成。Java 應(yīng)用通過(guò)-javaagent在啟動(dòng)階段執(zhí)行 Agent 的premainAgent 加載插件并使用 Byte Buddy 與 Instrumentation 對(duì) Tomcat、Feign、JDBC、連接池等框架類進(jìn)行字節(jié)碼增強(qiáng)。請(qǐng)求進(jìn)入 Tomcat 時(shí)創(chuàng)建 EntrySpan業(yè)務(wù)內(nèi)部通過(guò) Toolkit 可以創(chuàng)建 LocalSpan對(duì)外調(diào)用 Feign 或數(shù)據(jù)庫(kù)時(shí)創(chuàng)建 ExitSpan。ContextManager使用 ThreadLocal 為每個(gè)請(qǐng)求線程維護(hù)獨(dú)立的TracingContext并通過(guò)活動(dòng) Span 棧確定父子關(guān)系。跨服務(wù)時(shí)調(diào)用方將 Trace 上下文注入 HTTP Header下游從 Header 中恢復(fù)上下文從而生成屬于同一 Trace 的不同 Segment。當(dāng)所有 Span 結(jié)束后Agent 將 TraceSegment 放入內(nèi)存緩沖區(qū)由后臺(tái)線程轉(zhuǎn)換成 Protobuf 對(duì)象并通過(guò) gRPC 上報(bào) OAP避免網(wǎng)絡(luò)發(fā)送阻塞業(yè)務(wù)線程。OAP 對(duì) Segment 中的 Entry、Exit 和 Local Span 進(jìn)行分析生成 Service、Endpoint、ServiceRelation、DatabaseAccess 等 Source再通過(guò) OAL 聚合成響應(yīng)時(shí)間、成功率、CPM、百分位等 Metrics用于儀表盤(pán)、拓?fù)浜透婢H罩究赏ㄟ^(guò) Toolkit 獲取 Trace ID并以結(jié)構(gòu)化 LogData 攜帶 Trace ID、Segment ID 和 Span ID 上傳實(shí)現(xiàn)日志與鏈路關(guān)聯(lián)。告警模塊對(duì)分鐘級(jí) Metrics 維護(hù)滑動(dòng)窗口規(guī)則命中后通過(guò) WebHook 通知外部系統(tǒng)。對(duì)于 Span 內(nèi)部難以定位的慢方法還可以通過(guò) Trace Profiling 周期采樣目標(biāo)請(qǐng)求線程棧由 OAP 合并計(jì)算 Duration、Self Duration 和 Dump Count。二十二、你目前對(duì) SkyWalking 的掌握層次現(xiàn)在已經(jīng)不只是會(huì)啟動(dòng) OAP 打開(kāi) UI 查看 Trace而是已經(jīng)貫通了-javaagent 啟動(dòng) 插件匹配 字節(jié)碼增強(qiáng) Entry / Local / Exit Span ThreadLocal 上下文 Span 棧 跨服務(wù)傳播 數(shù)據(jù)庫(kù)分層診斷 Segment 異步上報(bào) OAP Source 與 Metrics 服務(wù)拓?fù)?日志關(guān)聯(lián) 告警時(shí)間窗口 Trace Profiling按后端面試標(biāo)準(zhǔn)可以歸納為三個(gè)層次層次當(dāng)前狀態(tài)會(huì)部署和使用已掌握會(huì)根據(jù) Trace 定位問(wèn)題已掌握能解釋 Agent 與 OAP 核心原理已形成完整主線能獨(dú)立修改 Agent/OAP 源碼尚需專項(xiàng)源碼工程訓(xùn)練二十三、最終記憶圖-javaagent ↓ premain ↓ 配置加載 插件掃描 ↓ Byte Buddy Instrumentation ↓ 框架類被增強(qiáng) ↓ HTTP 請(qǐng)求進(jìn)入 ↓ EntrySpan ↓ LocalSpan / ExitSpan ↓ ContextManager ThreadLocal Span Stack ↓ 跨服務(wù) ContextCarrier ↓ TraceSegment ↓ Agent 內(nèi)存隊(duì)列 ↓ gRPC 11800 ↓ OAP TraceAnalyzer ↓ Source ↓ OAL Metrics ├─ Dashboard ├─ Topology ├─ Alarm └─ Storage 同時(shí) 業(yè)務(wù)日志 → LogData → TraceContext → OAP 日志關(guān)聯(lián) Profiling Task → Thread.getStackTrace() → Snapshot → OAP 合并分析下一階段建議不再繼續(xù)平鋪源碼而是進(jìn)入SkyWalking 面試拷打與故障實(shí)戰(zhàn)復(fù)盤(pán)從 Agent 不上報(bào)、鏈路斷裂、連接池耗盡、慢 SQL、日志丟失、告警不觸發(fā)等場(chǎng)景要求你先判斷問(wèn)題層級(jí)再給出排查順序。