原創(chuàng)文章,歡迎轉載。轉載請注明:轉載自IT人故事會,謝謝!
原文鏈接地址:『互聯(lián)網架構』dubbo 調用埋點(114)
上邊幾次都是說的單體的攔截埋點,應用的內部進行的,很多的情況系統(tǒng)都是分布式的,怎么去監(jiān)聽RPC(遠程過程調用),dubbo,RMI,springcloud,http。只要遠程調用,跨進程調用都屬于RPC,也不可能所有的能都涉及到,很多公司都有自己的封裝,例如阿里的HFS,這次只針對dubbo這種RPC進行調用。
源碼:https://github.com/limingios/netFuture/tree/master/源碼/『互聯(lián)網架構』調?鏈系統(tǒng)工程結構(111)

(一)Dubbo執(zhí)行過程
對于dubbo的埋點,首先要了解dubbo的執(zhí)行過程

| 節(jié)點 | 角色說明 |
|---|---|
| Provider | 暴露服務的服務提供方 |
| Consumer | 調用遠程服務的服務消費方 |
| Registry | 服務注冊與發(fā)現的注冊中心 |
| Monitor | 統(tǒng)計服務的調用次數和調用時間的監(jiān)控中心 |
| Container | 服務運行容器 |
-
Dubbo調用過程
-
消費者調用過程
(二)調用端埋點實現
埋點目的
1.捕捉消費者調用信息(遠程接口、URL、參數、用時、返回結果、異常)
2.傳遞TraceRequest調用信息模型表結構
| 名稱 | 類型 | 描述 |
|---|---|---|
| servicePath | string | 服務路徑 |
| serviceName | string | 服務 |
| inParam | json | 返回結果 |
| outParam | json | 返回結果 |
| ErrorMessage | string | 異常信息 |
| ErrorStack | text | 異常堆棧 |
| ResultState | string | 執(zhí)行狀態(tài) |
| beginTime | date | 開始時間 |
| endTime | date | 結束時間 |
| addressIp | string | 遠程IP |
| fromIp | string | 調用者IP |
- 埋點位置
如何才能完整的捕捉到以上信息呢?那么就需要了解Dubbo內部的調用
1.分解調用過程為多個步驟。
2.這些步驟分別是在哪些協(xié)作線程上完成的?
3.經過了哪些方法?
4.經過了哪些過濾器?
-
調用過程分解&線程協(xié)作
- 選擇斷點位置Debug調試調用過程
-
消費者調用線程源碼分析:
- 經過對源碼的分析,埋點的位置如下:
DubboInvoker.doInvoke()
FutureFilter.invoke()
DubboInvoker.doInvoke() 方法最靠近調用方,異常捕捉范圍較大,但是該位置無法通過Attachment 向下傳遞TraceRequest 參數,所以需要FutureFilter.invoke() 進行補充,其具體分工如下:
·1.DubboInvoker.doInvoke捕獲如下信息: 1、開始時間 2、服務路徑 3、服務方法 4、輸入參數 5、異常信息 6、本地地址
2.FutureFilter.invoke 基于Attachment 向下傳遞參數 2、異常信息與堆棧 3、返回結果
DubboInvoker.doInvoke攔截源碼參見 :com.cbt.agent.collects.dubbo.DubboConsumerRpcExceptionMonitorHandle#invokerBefore
FutureFilter.invoke攔截源碼參見 :
com.cbt.agent.collects.dubbo.DubboConsumerMonitorHandle#invokerBefore
(三)調用端埋點實現
- 埋點目的
接收TraceRequest信息 ,并創(chuàng)建會話

- 埋點位置:
相對調用廣方接收方埋點目的較簡單,但同樣需分析源碼找準埋點位置
-
提供者處理線程分析
經分析埋點位置選在離實際調用方法較遠的EchoFilter過濾器理由是捕捉的信息更全面。
具體會話開啟過程:
- 基于Attachment獲取TraceId、ParentId、TraceProperties。
- 封裝TraceRequest ,并此為參數開啟會話。
- 在調用結束時關閉會話。
具體源碼參見:com.cbt.agent.collects.dubbo.DubboProviderMonitorHandle#invokerBefore

(二)Servlet處理埋點
Servlet埋點目的
1.生成TraceId
2.開啟關閉監(jiān)控會話
3.捕捉Http請求(url、客戶端IP、參數、響應時長、響應狀態(tài)碼)埋點埋在哪?
1.每一個Control方法
2.DispatcherServlet.doDispatch方法
3.HttpServlet.service 方法方案對比
| 方案 | 優(yōu)點 | 缺點 |
|---|---|---|
| 應用層Control類 | 簡單,風險因素低 | 判別成本高,有局限性,只能根據 HttpServlet 子類或@RequestMapping進行識別。 |
| DispatcherServlet.doDispatch | 簡單,適應性強 | 1、只能針對spring mvc 項目 2、spring boot 項目不支持 |
| HttpServlet.service | 適應性強,與應用層和框架無關 | 1、不同的容器ClassPath不一樣,存在兼容性問題。 2、存在風險,幾乎所有請求都會經過此方法 3、業(yè)務異常無法捕獲 |
總合比較還是選擇 HttpServlet.service 會更好些。
HttpServlet.service 埋點需要做的工作:
1.字節(jié)碼插樁
2.請求攔截并獲取請求信息字節(jié)碼插樁流程
字節(jié)碼插是指在數據裝載前在HttpServlet.service 插入監(jiān)控指令,以攔截Http請求,其插樁的過程。

請求攔截是指具體Http請求過來時進行攔截過濾,這么做主要是為了完成兩個目的
1.開啟監(jiān)控會話
2.開啟對Servlet響應過程的監(jiān)控


(三)Redis 調用埋點

- 埋點可選方案
| 方案 | 優(yōu)點 | 缺點 |
|---|---|---|
| 埋點jedis 類Get、Set等API方法 | 簡單直接 | 工作量大,方法較多、需要了解每個方法特性 |
| 埋點 Connection sendCommand方法 | 全面、所有命令都會經過此方法 | 存在未知風險、不方便計算執(zhí)行時間、和返回結果 |
| 埋點 Protocol | 全面、所有命令都會經過此方法 | 存在未知風險、不方便計算執(zhí)行時間、和返回結果 |

PS:源碼中可以查看DevelopBootMain類,我看這代碼也看了2天,原來老寫業(yè)務代碼,看看這確實很容易懵X,確實這才是有技術含量的代碼。其實有沒有技術含量不太重要,重要是的有沒有商業(yè)價值。




