在分佈式領域,有一個十分使人頭疼的問題,那就是分佈式的追蹤,日誌與監控。由於服務部署在不一樣的主機上,而實際的業務開發中,服務之間相互調用,尤爲是伴隨着服務微小化,單位化,微服務架構的趨勢下服務數量激增 即使是有着良好的前期規劃,可是中大型項目在實際開發中,依然會面臨着服務關係錯綜複雜,問題追蹤調試困難的等等問題javascript
那這個使人頭疼的問題,是否已經有良好的解決方案了呢?答案是確定確又是否認的... 目前商用規模的分佈式追蹤方案主要有如下幾類:html
第1類是採用非標準化的追蹤系統,通常爲針對單一業務系統設計實現,不具有普遍通用性和持續維護性java
第2類是從架構層考慮使用服務網格治理的方案來進行分佈式追蹤,優勢是功能強大,控制緯度廣,追蹤精度細,追蹤覆蓋面大。可是缺點也很明顯,目前不管是第一代以envoy爲表明的解決方案的ServiceMesh,仍是第二代以istio爲表明的ServiceMesh,都極爲重量,須要專門的架構和運維團隊進行細緻的前期安排和規劃後方能實施,部署成本高昂node
第3類是採用OpenTracing標準的分佈式追蹤系統,OpenTracing是CNCF(大名鼎鼎的Cloud Native Computing Foundation)爲了規範業界的分佈式跟蹤系統產品的統一範式,設計的trace標準,基於該標準,能夠解決分佈式追蹤系統跨平臺和兼容通用的問題。目前twitter、uber、apple等知名企業徹底遵循該標準設計trace系統。ios
分佈式跟蹤系統各種產品,根據設計目標和標準造成綜合對比*(資料來源於互聯網,非權威)*:git
產品名稱 | 廠商 | 開源 | OpenTracing標準 | 侵入性 | 應用策略 | 時效性 | 決策支持 | 可視化 | 低消耗 | 延展性 |
---|---|---|---|---|---|---|---|---|---|---|
jaeger | uber | 開源 | 徹底支持 | 部分侵入 | 策略靈活 | 時效性高, UDP協議傳輸數據(在Uber任意給定的一個Jaeger安裝能夠很容易地天天處理幾十億spans) | 決策支持較好,而且底層支持metrics指標 | 報表不豐富,UI比較簡單 | 消耗低 | jaeger比較複雜,使用框架較多,好比:rpc框架採用thrift協議,不支持pb協議之類。後端存儲比較複雜。但通過uber大規模使用,延展性好 |
zipkin | 開源 | 部分支持 | 侵入性強 | 策略靈活 | 時效性好 | 決策通常(功能單一,監控維度和監控信息不夠豐富。沒有告警功能) | 豐富的數據報表 | 系統開銷小 | 延展性好 | |
CAT | 大衆點評 吳其敏 | 開源 | - | 侵入性強 | 策略靈活 | 時效性較好,rpc框架採用tcp傳輸數據 | 決策好 | 報表豐富,知足各類需求 | 消耗較低 , 國內不少大廠都在使用 | - |
Appdash | sourcegraph | 開源 | 徹底支持 | 侵入性較弱 | 採樣率支持(粒度:不能根據流量採樣,只能依賴於請求數量);沒有trace開關 | 時效性高 | 決策支持低 | 可視化太弱,無報表分析 | 消耗方面。不支持大規模部署, 由於appdash主要依賴於memory,雖然能夠持久化到磁盤,以及內存存儲支持hash存儲、帶有效期的map存儲、以及不加限制的內存存儲,前者存儲量太小、後者單機內存存儲沒法知足 | 延展性差 |
MTrace | 美團 | 不開源 | - | - | - | - | - | - | - | - |
CallGraph | 京東 | 不開源 | - | - | - | - | - | - | - | - |
Watchman | sina微博 | 不開源 | - | - | - | - | - | - | - | - |
EagleEye | 淘寶 | 不開源 | - | - | - | - | - | - | - | - |
skywalking | 華爲 吳晟 | 開源 | 徹底支持 | 侵入性很低 | 策略靈活 | 時效性較好 | 因爲調用鏈路的更細化, 可是做者在性能和追蹤細粒度之間保持了比較好的平衡。決策好 | 豐富的數據報表 | 消耗較低 | 延展性很是好,水平理論上無限擴展 |
綜合上分佈式追蹤系統對比github
1. jaeger對於go開發者來講,可能比較合適一些,可是入手比較困難。它的rpc框架採用thrift協議,如今主流grpc並不支持。後端豐富存儲,社區正在積極適配
2. appdash對於go開發者想搭建一個小型的trace比較合適,不適合大規模使用
3. zipkin項目,github很活躍,star數量不少,屬於java系。不少大廠使用
4. CAT項目也屬於java系, github不活躍,已不太更新了。不過不少大廠使用,平安、大衆點評、攜程...
5. skywalking項目, 也屬於java系。目前已成爲Apache下的項目了,github活躍,做者也很是活躍,噹噹、華爲正在使用。
6. appdash項目,go語言開發,由於可視化過於簡單、且徹底內存存儲,不太適合大規模項目使用。
複製代碼
以上除了閉源的分佈式追蹤系統,jaeger和zipkin還有skywalking是開源中不錯的選擇,可是,可能讀者也發現了,以上的分佈式追蹤系統,對於Java平臺的支持都很不錯,可是對於其餘平臺的支持,就都比較有限了,尤爲是低侵入代碼的自動探針部分。並且,以上全部的分佈式追蹤系統,無一例外,生產部署(非簡單體驗)都十分複雜,有些甚至依賴於kubernetes。這讓不少想要使用分佈式追蹤系統來解決實際問題的中小企業望而卻步web
我很清楚的記得在去年,我瘋狂的在尋找一個開箱可生產的分佈式追蹤系統,因而就有了本文以上部分,最終是以失敗了結,很遺憾我沒有尋找到任何一款開箱可生產的開源產品。可是,路是人走出來的嘛,既然已經有了OpenTracing的標準,那理論上任何人和任何組織均可以實現一套標準化的分佈式追蹤系統,由於產生了這個可怕的念頭,因而就有了本文的下半部分——我決定造一個輪子,自行實現一套OpenTracing標準的分佈式追蹤系統,目標是開箱可生產docker
我但願,能夠在上面的表格中插入一行:shell
產品名稱 | 廠商 | 開源 | OpenTracing標準 | 侵入性 | 應用策略 | 時效性 | 決策支持 | 可視化 | 低消耗 | 延展性 |
---|---|---|---|---|---|---|---|---|---|---|
NodeTracing | cheneyxu | 開源 | 徹底支持 | 自動探針,幾乎無侵入 | 策略靈活 | 時效性秒級 | 決策較好,且持續升級 | 包括服務拓撲圖,跨度甘特圖等在內的多種圖表,且持續升級 | 消耗極低 | 無狀態和關聯架構,徹底容器化的追蹤節點,可輕鬆簡單集羣化,採用內存與LevelDB配合的持久化存儲,可應對十億以上量級的span數據 |
最終,包括構思,架構,開發,測試等等兩個多月的時間,終於完成了NodeTracing的初個開箱可生產版本!這是至今爲止我我的最滿意的做品,同時也指望NodeTracing在之後能夠持續進化成爲至少是NodeJS領域最好的分佈式追蹤系統,如下是架構圖和演示demo
#NodeTracing使用 ##下載
git clone https://github.com/cheneyweb/nodetracing
cd nodetracing && npm i
複製代碼
##快速開始&單例啓動
cd server && npm run standalone
複製代碼
##生產部署&集羣啓動
docker stack deploy --prune -c docker-compose.yml nodetracing
複製代碼
**NodeTracing的部署就是這麼簡單!**根據須要選擇單例啓動/集羣啓動以後,打開瀏覽器訪問: http://localhost:3636/nodetracing/web/index.html 即可以看到系統管理頁,默認賬號密碼:admin/123456
##安裝自動探針
npm i nodetracing
複製代碼
const nodetracing = require('nodetracing')
const tracer = new nodetracing.Tracer({
serviceName: 'S1', // 必須,服務名稱
rpcAddress: 'localhost',// 必須,後臺追蹤收集服務地址
rpcPort: '36361', // 可選,後臺追蹤收集服務端口,默認:36361
auto: true, // 可選,是否啓用自動追蹤,默認:false
stackLog: false, // 可選,是否記錄詳細堆棧信息(包括代碼行號位置等,啓用內存消耗較大),默認:false
maxDuration: 30000 // 可選,最大函數執行時間(垃圾回收時間間隔),默認:30000
})
複製代碼
由此便完成了nodetracing的加載工做,接下來您能夠根據您的服務類型選擇如下自動探針/手動探針...
async function func1(){
...
}
async function func2(){
...
}
func1 = nodetracing.aop(func1)
func2 = nodetracing.aop(func2)
...
複製代碼
axios.interceptors.request.use(nodetracing.axiosMiddleware())
複製代碼
//koa
app.use(nodetracing.koaMiddleware())
//express
app.use(nodetracing.expressMiddleware())
複製代碼
const grpc = require('grpc')
const Service = grpc.loadPackageDefinition(...)[packageName][serviceName]
new Service("ip:port", grpc.credentials.createInsecure(), { interceptors: [nodetracing.grpcClientMiddleware()] })
複製代碼
const grpc = require('grpc')
const interceptors = require('@echo-health/grpc-interceptors')
let server = new grpc.Server()
server = interceptors.serverProxy(this.server)
server.use(nodetracing.grpcClientMiddleware())
複製代碼
const RPCClient = require('x-grpc').RPCClient
const rpcClient = new RPCClient({
port: 3333,
protosDir: "/grpc/protos/",
implsDir: "/grpc/impls/",
serverAddress: "localhost"
})
rpcClient.use(nodetracing.grpcClientMiddleware())
rpcClient.connect()
let result = await rpcClient.invoke('demo.User.login', { username: 'cheney', password: '123456' } , optionMeta?)
複製代碼
const RPCServer = require('x-grpc').RPCServer
const rpcServer = new RPCServer({
port: 3333,
protosDir: "/grpc/protos/",
implsDir: "/grpc/impls/",
serverAddress: "localhost"
})
rpcServer.use(nodetracing.grpcServerMiddleware())
rpcServer.listen()
複製代碼
簡單的使用說明以後,下面重點講解一下整個系統的實現方案 一個分佈式追蹤系統,由三大部分組成,分別是探針,追蹤服務,可視化服務
這三大部分,其實每個部分都是難啃的骨頭,根據實踐下來的經驗,每一部分的難點以下:
首先針對探針的實現,OpenTracing的標準其實已經給出了接口,目前第一版優先考慮目前還沒有有成熟分佈式追蹤系統的nodejs平臺。因此第一步須要基於opentracing-javascript實現OpenTracing的API接口 在這一步中,很遺憾OpenTracing給出的官方文檔實在有限,想要完整的實現全套API,必須耐下心來閱讀OpenTracing的JS源碼,沒有別的辦法
在實現OpenTracing的API以後,也僅僅是完成了手動探針的實現,由於OpenTracing其實只是指定了接口標準與追蹤數據標準,並無提供自動探針的實現思路 因此,其實自動探針的實現實際上是依賴於各語言平臺自由的特性。不過萬變不離其中,任何想要實現自動探針的語言平臺,就必定要實現AOP和上下文追蹤,不然幾乎不可能 由於首選支持nodejs平臺,因此在nodejs上實現自動探針主要會依賴兩個關鍵技術:
其中,async hook是node8.x以後版本推出的異步資源追蹤方案,直至今日node11.x,已經初步成熟。利用async hook能夠追蹤全部異步資源關係,而這正是自動探針的必備條件
不過這裏須要注意的是,nodejs目前沒有同步資源的追蹤方案(我沒有找到,若是有讀者知道有的,但願能告知,不勝感激),因此,目前在nodejs中,目前僅能夠對異步調用進行自動探針追蹤。可是其實問題不大,由於nodejs中幾乎全是異步資源,並且同步資源其實追蹤意義不大
function merging主要是用於在nodejs平臺中實現AOP,這裏主要須要一些語法技巧來實現,AOP對於自動探針很是關鍵,有了AOP,纔有跨服務追蹤的可能
追蹤服務用於接收探針上傳的span,很明顯,若是追蹤服務的架構設計很差,那這將會整個追蹤系統的性能瓶頸。而一個APM監控系統,自己怎麼性能瓶頸呢? 因此,追蹤服務集羣化是必然的。可是集羣化必定會面臨部署複雜難度高的問題,我不但願每個使用NodeTracing的人都感概其難以部署,並且開箱可生產是製做NodeTracing的初心。 因此,在這一步的選擇上,Docker Swarm是第一優先選擇,Docker Swarm的極簡優雅性實在使人印象深入,這一次,我依然決定使用容器集羣來解決追蹤服務的性能瓶頸問題
到可視化服務這一步,已經最後的闖關了,這意味着span數據已經收集完畢,咱們須要考慮如何將其持久化存儲和可視化展現提供決策 在這裏最後的一道門檻是持久化存儲,由於大規模數據的持久化存儲方案通常都是很是複雜的,單點數據庫性能有瓶頸,可是若是採用相似於分佈式數據庫之類的方案,會極大的提升部署難度。自己用戶要安裝自動探針,安裝追蹤服務已經要花些時間了,最後還須要部署一個分佈式數據庫? 很明顯這裏的平衡取捨是難點。可是所幸最終仍是找到了比較完美的解決方案,**那就是——LevelDB。這是一個谷歌實現的能支持十億級別數據規模的kv數據庫,特色是支持極高的寫入吞吐。**它的做者是Jeaf Dean和 Sanjay Ghemawat,是谷歌的傳奇工程師
LevelDB的發現讓我很激動,由於它幾乎就是爲分佈式追蹤系統而生的,全部特效都極其符合追蹤系統的持久化需求。**做爲內嵌型數據庫,能夠跟隨服務啓動,無需額外部署,寫入吞吐又極高。**在對LevelDB進行了基準測試後,我立馬採用了它做爲NodeTracing的持久化方案,由於它的性能表現實在太搶眼了
在第一版完成後,針對NodeTracing的span吞吐作了一些基準測試:
感謝你的閱讀,若是本文可以給你帶來幫助,但願你能在github上爲NodeTracing點亮一顆Star:) 本文完成之際,NodeTracing剛剛也經過了OpenTracing的registry註冊,能夠前往搜索查看 本文篇幅有限,沒有辦法詳細闡述NodeTracing全部配置和實現細節,有興趣的讀者可前往github閱讀詳情,也歡迎提出問題和看法