1. 程式人生 > >打造立體化監控體系與APM最佳實踐系列

打造立體化監控體系與APM最佳實踐系列

一、背景

年來在雲端計算、大資料等快速發展的時代下,產生了很多新的業務場景,同時很多企業傳統業務開始向網際網路的轉移。隨著企業業務的發展,規模擴大,業務越來越多,所採用的元件也越來越多開始走向分散式化,如微服務、訊息收發、分散式資料庫、分散式物件儲存、分散式快取、跨域呼叫等,這些元件共同構成了繁雜的分散式網路,一個業務請求可能會涉及到幾個、幾十個服務的協同處理,如何動態展示服務的鏈路?如何分析服務鏈路的瓶頸並對其進行調優?如何快速進行服務鏈路的故障發現?如何保障產品服務的使用者體驗?企業需要一個從程式碼端的視角來監控自己的應用進而確保自身的IT支撐系統得到高效的執行,同時需要一個強大的IT運維管理體系時刻監督IT環境各元件的效能質量,通過多維度實時分析異常並進行診斷以解決產品的問題。因此,應用效能管理(APM)將逐漸成為推廣中國IT技術進步與使用者體驗提升的標配。

當前大的網際網路公司都有自己的分散式跟蹤系統,比如Google的Dapper,Twitter的zipkin,Naver的pinpoint,淘寶的鷹眼,新浪的Watchman,京東的Hydra等,本文主要介紹zipkin的設計、安裝部署,並以一個簡單的案例演示zipkin的使用方法。

Google

二、概述

zipkin是一款開源的分散式實時資料追蹤系統(Distributed Tracking System),基於 Google Dapper的論文設計而來,由 Twitter 公司開發貢獻。其主要功能是聚集來自各個異構系統的實時監控資料。

三、使用場景

3.1 故障快速定位

通過分析呼叫鏈,可以將一次請求的邏輯軌跡完整清晰的展示出來,通過在開發中在業務日誌中新增呼叫鏈ID,可以通過呼叫鏈結合業務日誌快速定位錯誤資訊。

3.2 效能分析

在呼叫鏈的各個環節分別新增呼叫時延,可以分析系統的效能瓶頸,進行有針對性的優化。

3.3 服務可用性

通過分析各個環節的平均時延,QPS等資訊,可以找到系統的薄弱環節,對一些模組做調整,例如資料冗餘、鏈路可用等。

四、zipkin架構

4.1 架構

zipkin架構

如上圖所示,zipkin主要包括四個模組

Ø Collector接收各service傳輸的資料

Ø Storage儲存收集過來的資料,當前支援Cassandra,Redis,HBase,MySQL,PostgreSQL, SQLite等,預設儲存在記憶體中。

Ø API(Query)負責查詢Storage中儲存的資料,提供簡單的JSON API獲取資料,主要提供給web UI使用

Ø Web 提供簡單的web介面

各個異構的服務向zipkin報告資料的架構如下圖:

ipkin報告資料

上圖中的S表示傳送跟蹤資料的客戶端SDK或者Scribe客戶端(twitter內部採用scirbe來採集跟蹤資料)。

4.2 Span

Zipkin 以 Trace 結構表示對一次請求的追蹤,又把每個 Trace 拆分為若干個有依賴關係的 Span。在微服務架構中,一次使用者請求可能會由後臺若干個服務負責處理,那麼每個處理請求的服務就可以理解為一個 Span(可以包括 API 服務,快取服務,資料庫服務以及報表服務等)。當然這個服務也可能繼續請求其他的服務,因此 Span 是一個樹形結構,以體現服務之間的呼叫關係。

Zipkin的Span模型幾乎完全仿造了Dapper中Span模型的設計,我們知道,Span用來描述一次RPC呼叫,所以一個RPC呼叫只應該關聯一個spanId,Zipkin中的Span主要包含三個資料部分:

Ø 基礎資料,包括traceId、spanId、parentId、name、timestamp和duration,主要用於跟蹤樹中節點的關聯和介面展示。

u traceId:全域性跟蹤ID,用它來標記一次完整服務呼叫,所以和一次服務呼叫相關的span中的traceId都是相同的,Zipkin將具有相同traceId的span組裝成跟蹤樹來直觀的將呼叫鏈路圖展現在我們面前。

u spanid:span的id,理論上來說,span的id只要做到一個traceId下唯一就可以。

u parentId:父span的id,呼叫有層級關係,所以span作為呼叫節點的儲存結構,也有層級關係,跟蹤鏈是採用跟蹤樹的形式來展現的,樹的根節點就是呼叫的頂點,其中parentId為null的Span將成為跟蹤樹的根節點來展示,當然它也是呼叫鏈的起點。

u name:span的名稱,主要用於在介面上展示,一般是介面方法名,name的作用是讓人知道它是哪裡採集的span。

u timestamp:span建立時的時間戳,用來記錄採集的時刻。

u duration:持續時間,即span的建立到span完成最終的採集所經歷的時間,除去span自己邏輯處理的時間,該時間段可以理解成對於該跟蹤埋點來說服務呼叫的總耗時,timestamp+duration將表示成呼叫的結束時間。

Ø Annotation,註解,用來記錄請求特定事件相關資訊(例如時間),通常包含四個註解資訊

cs – Client Start,表示客戶端發起請求

sr – Server Receive,表示服務端收到請求

ss – Server Send,表示服務端完成處理,並將結果傳送給客戶端

cr – Client Received,表示客戶端獲取到服務端返回資訊

Ø BinaryAnnotation,提供一些額外資訊,一般以key-value對出現。

如下圖是一個呼叫鏈路示例:

在本文後續章節中將會對該例項進行詳細介紹。

4.3 客戶端SDK

在上一節中我們知道對於一個APM來說,提供多種型別的客戶端SDK(instrument)是很重要的,支援的客戶端SDK越多,推廣起來也越方便,使用人群也會越多。

跟蹤資訊是使用instrument庫進行收集併發送給zipkin,截止目前,zipkin官方支援的客戶端SDK如下:

除了官方庫之後,社群也提供了instrument支援,社群支援庫如下:

instrument

五、zipkin部

zipkin支援兩種方式部署,docker容器以及jar包執行,

docker容器方式執行命令如下:

docker容器執行

jar包直接執行命令如下(要求java8及以上版本):

5.2 服務開發

本文中以java語言開發一個簡單demo來演示zipkin的使用,Brave 是用來裝備 Java 程式的類庫,提供了面向 Standard Servlet、Spring MVC、Http Client、JAX RS、Jersey、Resteasy 和 MySQL 等介面的裝備能力,可以通過編寫簡單的配置和程式碼,讓基於這些框架構建的應用可以向 Zipkin 報告資料。同時 Brave 也提供了非常簡單且標準化的介面,在以上封裝無法滿足要求的時候可以方便擴充套件與定製。

服務呼叫關係如下:

新建名為service1、service2、service3、service4四個spring boot型別的專案,下面以service1專案來描述專案詳細配置,其他專案的配置類似,在此不一一詳述。

pom.xml新增如下依賴:

application.properties中增加如下配置:

配置檔案中指定了本服務的服務名以及服務埠,zipkin服務的地址。

服務定義:

當請求該服務(請求/start時),服務會請求下級的localhost:9090/foo服務-對應service2服務。

UI 啟動後主介面如下:

UI

啟動服務service1、service2、service3、service4,並通過瀏覽器請求服務service1。

zipkin頁面檢視服務

服務呼叫鏈如下:

檢視服務依賴關係:

完整的服務跟蹤鏈資訊如下:

[

{

“traceId”: “122ecddc1769c0da”,

“id”: “122ecddc1769c0da”,

“name”: “get”,

“timestamp”: 1494383123630139,

“duration”: 2832405,

“annotations”: [

{

“timestamp”: 1494383123630139,

“value”: “sr”,

“endpoint”: {

“serviceName”: “service1”,

“ipv4”: “192.168.1.10”

}

},

{

“timestamp”: 1494383126462544,

“value”: “ss”,

“endpoint”: {

“serviceName”: “service1”,

“ipv4”: “192.168.1.10”

}

}

],

“binaryAnnotations”: [

{

“key”: “http.status_code”,

“value”: “200”,

“endpoint”: {

“serviceName”: “service1”,

“ipv4”: “192.168.1.10”

}

},

{

“key”: “http.url”,

“value”: “/start”,

“endpoint”: {

“serviceName”: “service1”,

“ipv4”: “192.168.1.10”

}

}

]

},

{

“traceId”: “122ecddc1769c0da”,

“id”: “d92eb6cbae9b4787”,

“name”: “get”,

“parentId”: “122ecddc1769c0da”,

“timestamp”: 1494383123974246,

“duration”: 2475470,

“annotations”: [

{

“timestamp”: 1494383123974246,

“value”: “cs”,

“endpoint”: {

“serviceName”: “service1”,

“ipv4”: “192.168.1.10”

}

},

{

“timestamp”: 1494383124351184,

“value”: “sr”,

“endpoint”: {

“serviceName”: “service2”,

“ipv4”: “192.168.1.10”

}

},

{

“timestamp”: 1494383126443649,

“value”: “ss”,

“endpoint”: {

“serviceName”: “service2”,

“ipv4”: “192.168.1.10”

}

},

{

“timestamp”: 1494383126449716,

“value”: “cr”,

“endpoint”: {

“serviceName”: “service1”,

“ipv4”: “192.168.1.10”

}

}

],

“binaryAnnotations”: [

{

“key”: “http.status_code”,

“value”: “200”,

“endpoint”: {

“serviceName”: “service2”,

“ipv4”: “192.168.1.10”

}

},

{

“key”: “http.url”,

“value”: “/foo”,

“endpoint”: {

“serviceName”: “service2”,

“ipv4”: “192.168.1.10”

}

},

{

“key”: “http.url”,

“value”: “http://localhost:9090/foo”,

“endpoint”: {

“serviceName”: “service1”,

“ipv4”: “192.168.1.10”

}

}

]

},

{

“traceId”: “122ecddc1769c0da”,

“id”: “eba2687430a3f56c”,

“name”: “get”,

“parentId”: “d92eb6cbae9b4787”,

“timestamp”: 1494383124477917,

“duration”: 558367,

“annotations”: [

{

“timestamp”: 1494383124477917,

“value”: “cs”,

“endpoint”: {

“serviceName”: “service2”,

“ipv4”: “192.168.1.10”

}

},

{

“timestamp”: 1494383124881960,

“value”: “sr”,

“endpoint”: {

“serviceName”: “service3”,

“ipv4”: “192.168.1.10”

}

},

{

“timestamp”: 1494383125033410,

“value”: “ss”,

“endpoint”: {

“serviceName”: “service3”,

“ipv4”: “192.168.1.10”

}

},

{

“timestamp”: 1494383125036284,

“value”: “cr”,

“endpoint”: {

“serviceName”: “service2”,

“ipv4”: “192.168.1.10”

}

}

],

“binaryAnnotations”: [

{

“key”: “http.status_code”,

“value”: “200”,

“endpoint”: {

“serviceName”: “service3”,

“ipv4”: “192.168.1.10”

}

},

{

“key”: “http.url”,

“value”: “http://localhost:9091/bar”,

“endpoint”: {

“serviceName”: “service2”,

“ipv4”: “192.168.1.10”

}

},

{

“key”: “http.url”,

“value”: “/bar”,

“endpoint”: {

“serviceName”: “service3”,

“ipv4”: “192.168.1.10”

}

}

]

},

{

“traceId”: “122ecddc1769c0da”,

“id”: “3b0df0a2f1ea18b2”,

“name”: “get”,

“parentId”: “d92eb6cbae9b4787”,

“timestamp”: 1494383125298903,

“duration”: 1117321,

“annotations”: [

{

“timestamp”: 1494383125298903,

“value”: “cs”,

“endpoint”: {

“serviceName”: “service2”,

“ipv4”: “192.168.1.10”

}

},

{

“timestamp”: 1494383125529695,

“value”: “sr”,

“endpoint”: {

“serviceName”: “service4”,

“ipv4”: “192.168.1.10”

}

},

{

“timestamp”: 1494383126416224,

“value”: “cr”,

“endpoint”: {

“serviceName”: “service2”,

“ipv4”: “192.168.1.10”

}

},

{

“timestamp”: 1494383126424903,

“value”: “ss”,

“endpoint”: {

“serviceName”: “service4”,

“ipv4”: “192.168.1.10”

}

}

],

“binaryAnnotations”: [

{

“key”: “http.status_code”,

“value”: “200”,

“endpoint”: {

“serviceName”: “service4”,

“ipv4”: “192.168.1.10”

}

},

{

“key”: “http.url”,

“value”: “http://localhost:9092/tar”,

“endpoint”: {

“serviceName”: “service2”,

“ipv4”: “192.168.1.10”

}

},

{

“key”: “http.url”,

“value”: “/tar”,

“endpoint”: {

“serviceName”: “service4”,

“ipv4”: “192.168.1.10”

}

}

]

}

]

六、結束語

目前市面上已知APM包括zipkin、pinpoint、appdash、cat、hydra、EagleEye等,其中cat、hydra、EagleEye分別為美團、京東以及阿里內部使用的系統,有些未開源或者開源後不再更新,pinpoint由韓國的naver開源,zipkin由Twitter開源,當前zipkin以及pinpoint社群均非常活躍,版本釋出也比較頻繁,使用者較多,生態系統也較為完善。

相對pinpoint的部署來說,zipkin部署比較簡單-執行jar包即可,pinpoint主要通過Plugin來支援眾多的模組,包括tomcat、okhttpclient等中介軟體,基本不用修改原始碼和配置檔案,對於運維人員來講最為方便,zipkin通過instrument libraries來支援眾多的中介軟體,並且有Twitter等大公司的支援,但是開發時需要對Spring、web.xml之類的配置檔案做修改。

文章來自微信公眾號:雲技術實踐