隨着微服務架構的流行,服務按照不一樣的維度進行拆分,一次請求每每須要涉及到多個服務。互聯網應用構建在不一樣的軟件模塊集上,這些軟件模塊,有多是由不一樣的團隊開發、可能使用不一樣的編程語言來實現、有可能布在了幾千臺服務器,橫跨多個不一樣的數據中心。所以,就須要一些能夠幫助理解系統行爲、用於分析性能問題的工具,以便發生故障的時候,可以快速定位和解決問題。html
全鏈路監控組件就在這樣的問題背景下產生了。最出名的是谷歌公開的論文提到的 Google Dapper。想要在這個上下文中理解分佈式系統的行爲,就須要監控那些橫跨了不一樣的應用、不一樣的服務器之間的關聯動做。前端
因此,在複雜的微服務架構系統中,幾乎每個前端請求都會造成一個複雜的分佈式服務調用鏈路。一個請求完整調用鏈可能以下圖所示:java
那麼在業務規模不斷增大、服務不斷增多以及頻繁變動的狀況下,面對複雜的調用鏈路就帶來一系列問題:mysql
- 如何快速發現問題?
- 如何判斷故障影響範圍?
- 如何梳理服務依賴以及依賴的合理性?
- 如何分析鏈路性能問題以及實時容量規劃?
同時咱們會關注在請求處理期間各個調用的各項性能指標,好比:吞吐量(TPS)、響應時間及錯誤記錄等。git
- 吞吐量,根據拓撲可計算相應組件、平臺、物理設備的實時吞吐量。
- 響應時間,包括總體調用的響應時間和各個服務的響應時間等。
- 錯誤記錄,根據服務返回統計單位時間異常次數。
全鏈路性能監控 從總體維度到局部維度展現各項指標,將跨應用的全部調用鏈性能信息集中展示,可方便度量總體和局部性能,而且方便找到故障產生的源頭,生產上可極大縮短故障排除時間。程序員
有了全鏈路監控工具,咱們可以達到:github
- 請求鏈路追蹤,故障快速定位:能夠經過調用鏈結合業務日誌快速定位錯誤信息。
- 可視化: 各個階段耗時,進行性能分析。
- 依賴優化:各個調用環節的可用性、梳理服務依賴關係以及優化。
- 數據分析,優化鏈路:能夠獲得用戶的行爲路徑,彙總分析應用在不少業務場景。
如上所述,那麼咱們選擇全鏈路監控組件有哪些目標要求呢?Google Dapper中也提到了,總結以下:redis
探針的性能消耗sql
APM組件服務的影響應該作到足夠小。服務調用埋點自己會帶來性能損耗,這就須要調用跟蹤的低損耗,實際中還會經過配置採樣率的方式,選擇一部分請求去分析請求路徑。在一些高度優化過的服務,即便一點點損耗也會很容易察覺到,並且有可能迫使在線服務的部署團隊不得不將跟蹤系統關停。編程
代碼的侵入性
即也做爲業務組件,應當儘量少入侵或者無入侵其餘業務系統,對於使用方透明,減小開發人員的負擔。
對於應用的程序員來講,是不須要知道有跟蹤系統這回事的。若是一個跟蹤系統想生效,就必須須要依賴應用的開發者主動配合,那麼這個跟蹤系統也太脆弱了,每每因爲跟蹤系統在應用中植入代碼的bug或疏忽致使應用出問題,這樣纔是沒法知足對跟蹤系統「無所不在的部署」這個需求。
可擴展性
一個優秀的調用跟蹤系統必須支持分佈式部署,具有良好的可擴展性。可以支持的組件越多固然越好。或者提供便捷的插件開發API,對於一些沒有監控到的組件,應用開發者也能夠自行擴展。
數據的分析
數據的分析要快 ,分析的維度儘量多。跟蹤系統能提供足夠快的信息反饋,就能夠對生產環境下的異常情況作出快速反應。分析的全面,可以避免二次開發。
通常的全鏈路監控系統,大體可分爲四大功能模塊:
埋點與生成日誌
埋點即系統在當前節點的上下文信息,能夠分爲 客戶端埋點、服務端埋點,以及客戶端和服務端雙向型埋點。埋點日誌一般要包含如下內容traceId、spanId、調用的開始時間,協議類型、調用方ip和端口,請求的服務名、調用耗時,調用結果,異常信息等,同時預留可擴展字段,爲下一步擴展作準備;
不能形成性能負擔:一個價值未被驗證,卻會影響性能的東西,是很難在公司推廣的!
由於要寫log,業務QPS越高,性能影響越重。經過採樣和異步log解決。
收集和存儲日誌
主要支持分佈式日誌採集的方案,同時增長MQ做爲緩衝;
- 每一個機器上有一個 deamon 作日誌收集,業務進程把本身的Trace發到daemon,daemon把收集Trace往上一級發送;
- 多級的collector,相似pub/sub架構,能夠負載均衡;
- 對聚合的數據進行 實時分析和離線存儲;
- 離線分析 須要將同一條調用鏈的日誌彙總在一塊兒;
分析和統計調用鏈路數據,以及時效性
調用鏈跟蹤分析:把同一TraceID的Span收集起來,按時間排序就是timeline。把ParentID串起來就是調用棧。
拋異常或者超時,在日誌裏打印TraceID。利用TraceID查詢調用鏈狀況,定位問題。
依賴度量:
- 強依賴:調用失敗會直接中斷主流程
- 高度依賴:一次鏈路中調用某個依賴的概率高
- 頻繁依賴:一次鏈路調用同一個依賴的次數多
離線分析:按TraceID彙總,經過Span的ID和ParentID還原調用關係,分析鏈路形態。
實時分析:對單條日誌直接分析,不作彙總,重組。獲得當前QPS,延遲。
展示以及決策支持
基本工做單元,一次鏈路調用(能夠是RPC,DB等沒有特定的限制)建立一個span,經過一個64位ID標識它,uuid較爲方便,span中還有其餘的數據,例如描述信息,時間戳,key-value對的(Annotation)tag信息,parent_id等,其中parent-id能夠表示span調用鏈路來源。
上圖說明了span在一次大的跟蹤過程當中是什麼樣的。Dapper記錄了span名稱,以及每一個span的ID和父ID,以重建在一次追蹤過程當中不一樣span之間的關係。若是一個span沒有父ID被稱爲root span。全部span都掛在一個特定的跟蹤上,也共用一個跟蹤id。
Span數據結構:
type Span struct {
TraceID int64 // 用於標示一次完整的請求id
Name string
ID int64 // 當前此次調用span_id
ParentID int64 // 上層服務的調用span_id 最上層服務parent_id爲null
Annotation []Annotation // 用於標記的時間戳
Debug bool
}
複製代碼
相似於 樹結構的Span集合,表示一次完整的跟蹤,從請求到服務器開始,服務器返回response結束,跟蹤每次rpc調用的耗時,存在惟一標識trace_id。好比:你運行的分佈式大數據存儲一次Trace就由你的一次請求組成。
每種顏色的note標註了一個span,一條鏈路經過TraceId惟一標識,Span標識發起的請求信息。樹節點是整個架構的基本單元,而每個節點又是對span的引用。節點之間的連線表示的span和它的父span直接的關係。雖然span在日誌文件中只是簡單的表明span的開始和結束時間,他們在整個樹形結構中倒是相對獨立的。
註解,用來記錄請求特定事件相關信息(例如時間),一個span中會有多個annotation註解描述。一般包含四個註解信息:
(1) cs:Client Start,表示客戶端發起請求 (2) sr:Server Receive,表示服務端收到請求 (3) ss:Server Send,表示服務端完成處理,並將結果發送給客戶端 (4) cr:Client Received,表示客戶端獲取到服務端返回信息
Annotation數據結構:
type Annotation struct {
Timestamp int64
Value string
Host Endpoint
Duration int32
}
複製代碼
請求調用示例
- 當用戶發起一個請求時,首先到達前端A服務,而後分別對B服務和C服務進行RPC調用;
- B服務處理完給A作出響應,可是C服務還須要和後端的D服務和E服務交互以後再返還給A服務,最後由A服務來響應用戶的請求;
調用過程追蹤
- 請求到來生成一個全局TraceID,經過TraceID能夠串聯起整個調用鏈,一個TraceID表明一次請求。
- 除了TraceID外,還須要SpanID用於記錄調用父子關係。每一個服務會記錄下parent id和span id,經過他們能夠組織一次完整調用鏈的父子關係。
- 一個沒有parent id的span成爲root span,能夠當作調用鏈入口。
- 全部這些ID可用全局惟一的64位整數表示;
- 整個調用過程當中每一個請求都要透傳TraceID和SpanID。
- 每一個服務將該次請求附帶的TraceID和附帶的SpanID做爲parent id記錄下,而且將本身生成的SpanID也記錄下。
- 要查看某次完整的調用則 只要根據TraceID查出全部調用記錄,而後經過parent id和span id組織起整個調用父子關係。
調用鏈核心工做
- 調用鏈數據生成,對整個調用過程的全部應用進行埋點並輸出日誌。
- 調用鏈數據採集,對各個應用中的日誌數據進行採集。
- 調用鏈數據存儲及查詢,對採集到的數據進行存儲,因爲日誌數據量通常都很大,不只要能對其存儲,還須要能提供快速查詢。
- 指標運算、存儲及查詢,對採集到的日誌數據進行各類指標運算,將運算結果保存起來。
- 告警功能,提供各類閥值警告功能。
總體部署架構
- 經過AGENT生成調用鏈日誌。
- 經過logstash採集日誌到kafka。
- kafka負責提供數據給下游消費。
- storm計算匯聚指標結果並落到es。
- storm抽取trace數據並落到es,這是爲了提供比較複雜的查詢。好比經過時間維度查詢調用鏈,能夠很快查詢出全部符合的traceID,根據這些traceID再去 Hbase 查數據就快了。
- logstash將kafka原始數據拉取到hbase中。hbase的rowkey爲traceID,根據traceID查詢是很快的。
AGENT無侵入部署
經過AGENT代理無侵入式部署,將性能測量與業務邏輯徹底分離,能夠測量任意類的任意方法的執行時間,這種方式大大提升了採集效率,而且減小運維成本。根據服務跨度主要分爲兩大類AGENT:
服務內AGENT,這種方式是經過 Java 的agent機制,對服務內部的方法調用層次信息進行數據收集,如方法調用耗時、入參、出參等信息。
跨服務AGENT,這種狀況須要對主流RPC框架以插件形式提供無縫支持。並經過提供標準數據規範以適應自定義RPC框架:
(1)Dubbo支持; (2)Rest支持; (3)自定義RPC支持; 複製代碼
調用鏈監控好處
- 準確掌握生產一線應用部署狀況;
- 從調用鏈全流程性能角度,識別對關鍵調用鏈,並進行優化;
- 提供可追溯的性能數據,量化 IT 運維部門業務價值;
- 快速定位代碼性能問題,協助開發人員持續性的優化代碼;
- 協助開發人員進行白盒測試,縮短系統上線穩按期;
市面上的全鏈路監控理論模型大多都是借鑑Google Dapper論文,本文重點關注如下三種APM組件:
- Zipkin:由Twitter公司開源,開放源代碼分佈式的跟蹤系統,用於收集服務的定時數據,以解決微服務架構中的延遲問題,包括:數據的收集、存儲、查找和展示。
- Pinpoint:一款對Java編寫的大規模分佈式系統的APM工具,由韓國人開源的分佈式跟蹤組件。
- Skywalking:國產的優秀APM組件,是一個對JAVA分佈式應用程序集羣的業務運行狀況進行追蹤、告警和分析的系統。
以上三種全鏈路監控方案須要對比的項提煉出來:
探針的性能
主要是agent對服務的吞吐量、CPU和內存的影響。微服務的規模和動態性使得數據收集的成本大幅度提升。
collector的可擴展性
可以水平擴展以便支持大規模服務器集羣。
全面的調用鏈路數據分析
提供代碼級別的可見性以便輕鬆定位失敗點和瓶頸。
對於開發透明,容易開關
添加新功能而無需修改代碼,容易啓用或者禁用。
完整的調用鏈應用拓撲
自動檢測應用拓撲,幫助你搞清楚應用的架構
比較關注探針的性能,畢竟APM定位仍是工具,若是啓用了鏈路監控組建後,直接致使吞吐量下降過半,那也是不能接受的。對skywalking、zipkin、pinpoint進行了壓測,並與基線(未使用探針)的狀況進行了對比。
選用了一個常見的基於Spring的應用程序,他包含Spring Boot, Spring MVC,redis客戶端,mysql。 監控這個應用程序,每一個trace,探針會抓取5個span(1 Tomcat, 1 SpringMVC, 2 Jedis, 1 Mysql)。這邊基本和 skywalkingtest 的測試應用差很少。
模擬了三種併發用戶:500,750,1000。使用jmeter測試,每一個線程發送30個請求,設置思考時間爲10ms。使用的採樣率爲1,即100%,這邊與生產可能有差異。pinpoint默認的採樣率爲20,即50%,經過設置agent的配置文件改成100%。zipkin默認也是1。組合起來,一共有12種。下面看下彙總表:
從上表能夠看出,在三種鏈路監控組件中,skywalking的探針對吞吐量的影響最小,zipkin的吞吐量居中。pinpoint的探針對吞吐量的影響較爲明顯,在500併發用戶時,測試服務的吞吐量從1385下降到774,影響很大。而後再看下CPU和memory的影響,在內部服務器進行的壓測,對CPU和memory的影響都差很少在10%以內。
collector的可擴展性,使得可以水平擴展以便支持大規模服務器集羣。
zipkin
開發zipkin-Server(其實就是提供的開箱即用包),zipkin-agent與zipkin-Server經過http或者mq進行通訊,http通訊會對正常的訪問形成影響,因此仍是推薦基於mq異步方式通訊,zipkin-Server經過訂閱具體的topic進行消費。這個固然是能夠擴展的,多個zipkin-Server實例進行異步消費mq中的監控信息。
skywalking
skywalking的collector支持兩種部署方式:單機和集羣模式。collector與agent之間的通訊使用了gRPC。
pinpoint
一樣,pinpoint也是支持集羣和單機部署的。pinpoint agent經過thrift通訊框架,發送鏈路信息到collector。
全面的調用鏈路數據分析,提供代碼級別的可見性以便輕鬆定位失敗點和瓶頸。
zipkin
zipkin的鏈路監控粒度相對沒有那麼細,從上圖能夠看到調用鏈中具體到接口級別,再進一步的調用信息並未涉及。
skywalking
skywalking 還支持20+的中間件、框架、類庫,好比:主流的dubbo、Okhttp,還有DB和消息中間件。上圖skywalking鏈路調用分析截取的比較簡單,網關調用user服務,因爲支持衆多的中間件,因此skywalking鏈路調用分析比zipkin完備些。
pinpoint
pinpoint應該是這三種APM組件中,數據分析最爲完備的組件。提供代碼級別的可見性以便輕鬆定位失敗點和瓶頸,上圖能夠看到對於執行的sql語句,都進行了記錄。還能夠配置報警規則等,設置每一個應用對應的負責人,根據配置的規則報警,支持的中間件和框架也比較完備。
對於開發透明,容易開關,添加新功能而無需修改代碼,容易啓用或者禁用。咱們指望功能能夠不修改代碼就工做並但願獲得代碼級別的可見性。
對於這一點,Zipkin 使用修改過的類庫和它本身的容器(Finagle)來提供分佈式事務跟蹤的功能。可是,它要求在須要時修改代碼。skywalking和pinpoint都是基於字節碼加強的方式,開發人員不須要修改代碼,而且能夠收集到更多精確的數據由於有字節碼中的更多信息。
自動檢測應用拓撲,幫助你搞清楚應用的架構。
上面三幅圖,分別展現了APM組件各自的調用拓撲,都能實現完整的調用鏈應用拓撲。相對來講,pinpoint界面顯示的更加豐富,具體到調用的DB名,zipkin的拓撲侷限於服務於服務之間。
Pinpoint 與 Zipkin 都是基於 Google Dapper 的那篇論文,所以理論基礎大體相同。二者都是將服務調用拆分紅若干有級聯關係的 Span,經過 SpanId 和 ParentSpanId 來進行調用關係的級聯;最後再將整個調用鏈流經的全部的 Span 匯聚成一個 Trace,報告給服務端的 collector 進行收集和存儲。
即使在這一點上,Pinpoint 所採用的概念也不徹底與那篇論文一致。好比他採用 TransactionId 來取代 TraceId,而真正的 TraceId 是一個結構,裏面包含了 TransactionId, SpanId 和 ParentSpanId。並且 Pinpoint 在 Span 下面又增長了一個 SpanEvent 結構,用來記錄一個 Span 內部的調用細節(好比具體的方法調用等等),所以 Pinpoint 默認會比 Zipkin 記錄更多的跟蹤數據。可是理論上並無限定 Span 的粒度大小,因此一個服務調用能夠是一個 Span,那麼每一個服務中的方法調用也能夠是個 Span,這樣的話,其實 Brave 也能夠跟蹤到方法調用級別,只是具體實現並無這樣作而已。
Pinpoint 實現了基於字節碼注入的 Java Agent 探針,而 Zipkin 的 Brave 框架僅僅提供了應用層面的 API,可是細想問題遠不那麼簡單。字節碼注入是一種簡單粗暴的解決方案,理論上來講不管任何方法調用,均可以經過注入代碼的方式實現攔截,也就是說沒有實現不了的,只有不會實現的。但 Brave 則不一樣,其提供的應用層面的 API 還須要框架底層驅動的支持,才能實現攔截。好比,MySQL 的 JDBC 驅動,就提供有注入 interceptor 的方法,所以只須要實現 StatementInterceptor 接口,並在 Connection String 中進行配置,就能夠很簡單的實現相關攔截;而與此相對的,低版本的 MongoDB 的驅動或者是 Spring Data MongoDB 的實現就沒有如此接口,想要實現攔截查詢語句的功能,就比較困難。
所以在這一點上,Brave 是硬傷,不管使用字節碼注入多麼困難,但至少也是能夠實現的,可是 Brave 卻有無從下手的可能,並且是否能夠注入,可以多大程度上注入,更多的取決於框架的 API 而不是自身的能力。
通過簡單閱讀 Pinpoint 和 Brave 插件的代碼,能夠發現二者的實現難度有天壤之別。在都沒有任何開發文檔支撐的前提下,Brave 比 Pinpoint 更容易上手。Brave 的代碼量不多,核心功能都集中在 brave-core 這個模塊下,一箇中等水平的開發人員,能夠在一天以內讀懂其內容,而且能對 API 的結構有很是清晰的認識。
Pinpoint 的代碼封裝也是很是好的,尤爲是針對字節碼注入的上層 API 的封裝很是出色,可是這依然要求閱讀人員對字節碼注入多少有一些瞭解,雖然其用於注入代碼的核心 API 並很少,但要想了解透徹,恐怕還得深刻 Agent 的相關代碼,好比很難一目瞭然的理解 addInterceptor 和 addScopedInterceptor 的區別,而這兩個方法就是位於 Agent 的有關類型中。
由於 Brave 的注入須要依賴底層框架提供相關接口,所以並不須要對框架有一個全面的瞭解,只須要知道能在什麼地方注入,可以在注入的時候取得什麼數據就能夠了。就像上面的例子,咱們根本不須要知道 MySQL 的 JDBC Driver 是如何實現的也能夠作到攔截 SQL 的能力。可是 Pinpoint 就否則,由於 Pinpoint 幾乎能夠在任何地方注入任何代碼,這須要開發人員對所需注入的庫的代碼實現有很是深刻的瞭解,經過查看其 MySQL 和 Http Client 插件的實現就能夠洞察這一點,固然這也從另一個層面說明 Pinpoint 的能力確實能夠很是強大,並且其默認實現的不少插件已經作到了很是細粒度的攔截。
針對底層框架沒有公開 API 的時候,其實 Brave 也並不徹底機關用盡,咱們能夠採起 AOP 的方式,同樣可以將相關攔截注入到指定的代碼中,並且顯然 AOP 的應用要比字節碼注入簡單不少。
以上這些直接關係到實現一個監控的成本,在 Pinpoint 的官方技術文檔中,給出了一個參考數據。若是對一個系統集成的話,那麼用於開發 Pinpoint 插件的成本是 100,將此插件集成入系統的成本是 0;但對於 Brave,插件開發的成本只有 20,而集成成本是 10。從這一點上能夠看出官方給出的成本參考數據是 5:1。可是官方又強調了,若是有 10 個系統須要集成的話,那麼總成本就是 10 * 10 + 20 = 120,就超出了 Pinpoint 的開發成本 100,並且須要集成的服務越多,這個差距就越大。
很顯然,這一點上 Pinpoint 徹底處於劣勢,從社區所開發出來的集成接口就可見一斑。
Pinpoint 的數據接口缺少文檔,並且也不太標準(參考論壇討論帖),須要閱讀不少代碼纔可能實現一個本身的探針(好比 Node 的或者 PHP 的)。並且團隊爲了性能考慮使用了 Thrift 做爲數據傳輸協議標準,比起 HTTP 和 JSON 而言難度增長了很多。
這一點也沒必要多說,Zipkin 由 Twitter 開發,能夠算得上是明星團隊,而 Naver 的團隊只是一個默默無聞的小團隊(從 #1759 的討論中能夠看出)。雖說這個項目在短時間內不太可能消失或中止更新,但畢竟不如前者用起來更加放心。並且沒有更多社區開發出來的插件,讓 Pinpoint 只依靠團隊自身的力量完成諸多框架的集成實屬困難,並且他們目前的工做重點依然是在提高性能和穩定性上。
Pinpoint 在實現之初就考慮到了性能問題,www.naver.com 網站的後端某些服務天天要處理超過 200 億次的請求,所以他們會選擇 Thrift 的二進制變長編碼格式、並且使用 UDP 做爲傳輸鏈路,同時在傳遞常量的時候也儘可能使用數據參考字典,傳遞一個數字而不是直接傳遞字符串等等。這些優化也增長了系統的複雜度:包括使用 Thrift 接口的難度、UDP 數據傳輸的問題、以及數據常量字典的註冊問題等等。
相比之下,Zipkin 使用熟悉的 Restful 接口加 JSON,幾乎沒有任何學習成本和集成難度,只要知道數據傳輸結構,就能夠輕易的爲一個新的框架開發出相應的接口。
另外 Pinpoint 缺少針對請求的採樣能力,顯然在大流量的生產環境下,不太可能將全部的請求所有記錄,這就要求對請求進行採樣,以決定什麼樣的請求是我須要記錄的。Pinpoint 和 Brave 都支持採樣百分比,也就是百分之多少的請求會被記錄下來。可是,除此以外 Brave 還提供了 Sampler 接口,能夠自定義採樣策略,尤爲是當進行 A/B 測試的時候,這樣的功能就很是有意義了。
從短時間目標來看,Pinpoint 確實具備壓倒性的優點:無需對項目代碼進行任何改動就能夠部署探針、追蹤數據細粒化到方法調用級別、功能強大的用戶界面以及幾乎比較全面的 Java 框架支持。可是長遠來看,學習 Pinpoint 的開發接口,以及將來爲不一樣的框架實現接口的成本都仍是個未知數。相反,掌握 Brave 就相對容易,並且 Zipkin 的社區更增強大,更有可能在將來開發出更多的接口。在最壞的狀況下,咱們也能夠本身經過 AOP 的方式添加適合於咱們本身的監控代碼,而並不須要引入太多的新技術和新概念。並且在將來業務發生變化的時候,Pinpoint 官方提供的報表是否能知足要求也很差說,增長新的報表也會帶來不能夠預測的工做難度和工做量。
Monitor可分爲系統監控和應用監控。系統監控好比CPU,內存,網絡,磁盤等等總體的系統負載的數據,細化可具體到各進程的相關數據。這一類信息是直接能夠從系統中獲得的。應用監控須要應用提供支持,暴露了相應的數據。好比應用內部請求的QPS,請求處理的延時,請求處理的error數,消息隊列的隊列長度,崩潰狀況,進程垃圾回收信息等等。Monitor主要目標是發現異常,及時報警。
Tracing的基礎和核心都是調用鏈。相關的metric大多都是圍繞調用鏈分析獲得的。Tracing主要目標是系統分析。提早找到問題比出現問題後再去解決更好。
Tracing和應用級的Monitor技術棧上有不少共同點。都有數據的採集,分析,存儲和展式。只是具體收集的數據維度不一樣,分析過程不同。