zipkin-demo
ZIPKIN分布式系統(tǒng)調(diào)用鏈追蹤
在公司業(yè)務(wù)發(fā)展過程中悴务,剛開始的時候,我們可能比較關(guān)注單個請求的調(diào)用耗時情況适瓦,調(diào)用頻次統(tǒng)計等一些基本數(shù)據(jù)指標(biāo)土全,因為這個時候業(yè)務(wù)比較單一,系統(tǒng)相對來說較為簡單清晰蓄髓,調(diào)整和優(yōu)化起來相對來說比較容易一點叉庐。
但是隨著系統(tǒng)業(yè)務(wù)的不斷發(fā)展,需求的不斷增加会喝,整個系統(tǒng)逐漸變得越來越復(fù)雜陡叠,有可能還會涉及到外部系統(tǒng)以及公司內(nèi)部其他系統(tǒng)之間的一個交互玩郊。這個時候整個系統(tǒng)的調(diào)用鏈將會變得越來復(fù)雜(目前大多數(shù)都是分布式調(diào)用),更多的時候我們的一個前端請求可能最終需要經(jīng)過多次后端服務(wù)的調(diào)用才能得到我們想要的結(jié)果數(shù)據(jù)枉阵,而在這個過程中译红,如果調(diào)用超時或者調(diào)用變得異常的慢或者調(diào)用出現(xiàn)異常等等情況,一般情況下我們是無法準確快速得定位出這次請求出現(xiàn)的問題具體是因為那一部分出現(xiàn)了狀況引起的兴溜。
比如說具體是調(diào)用服務(wù)A出現(xiàn)了問題侦厚,還是服務(wù)B不可用亦或者是服務(wù)C調(diào)用超時等等我們是無法得知的。那這個時候就需要對整個調(diào)用鏈做一次完整的分析才能快速準確的定位出具體的問題拙徽,因而分布式調(diào)用追蹤就誕生了刨沦。
什么是分布式系統(tǒng)調(diào)用追蹤?
- 對多個相互協(xié)作的子系統(tǒng)之間的調(diào)用關(guān)系及依賴關(guān)系進行追蹤記錄
- 系統(tǒng)與系統(tǒng)之間的調(diào)用形式有多種:HTTP膘怕、RPC想诅、RMI等等
- 通過調(diào)用鏈的方式,將一次請求調(diào)用過程完整的串聯(lián)起來岛心,這樣就實現(xiàn)了對請求調(diào)用路徑的監(jiān)控
為什么需要進行分布式調(diào)用追蹤来破?
- 確定服務(wù)與服務(wù)之間的依賴關(guān)系,以便后期優(yōu)化
- 統(tǒng)計請求總耗時鹉梨,以及各個服務(wù)的調(diào)用耗時讳癌,以便解決系統(tǒng)瓶頸
- 當(dāng)請求變慢或系統(tǒng)出現(xiàn)異常時穿稳,需要盡快確定具體是哪個服務(wù)出現(xiàn)問題存皂,以便快速排查線上問題
分布式調(diào)用追蹤需要做什么?
- 記錄從上游到下游關(guān)鍵節(jié)點服務(wù)的日志信息逢艘,入?yún)⒌┐⒊鰠ⅰ惓6褩5刃畔?/li>
- 關(guān)鍵節(jié)點服務(wù)的響應(yīng)耗時
- 關(guān)鍵節(jié)點服務(wù)之間的依賴關(guān)系
- 將分散的請求串聯(lián)到一起
幾個關(guān)鍵概念
traceId:
標(biāo)識整個請求鏈它改,就是一個全局的跟蹤ID疤孕,是跟蹤的入口點,根據(jù)需求來決定在哪生成traceId央拖。比如一個http請求祭阀,首先入口是web應(yīng)用,一般看完整的調(diào)用鏈這里自然是traceId生成的起點鲜戒,結(jié)束點在web請求返回點专控,因此traceId將在這個調(diào)用鏈中進行傳遞。
spanId:
標(biāo)識一次分布式調(diào)用遏餐,這是下一層的請求跟蹤ID,這個也根據(jù)自己的需求伦腐,比如認為一次rpc,一次sql執(zhí)行等都可以是一個span失都。一個traceId包含一個以上的spanId柏蘑。
parentId:
上一次請求跟蹤ID幸冻,用來將前后的請求串聯(lián)起來。
cs:
客戶端發(fā)起請求的時間咳焚,比如dubbo調(diào)用端開始執(zhí)行遠程調(diào)用之前,標(biāo)志著Span的開始
cr:
客戶端收到處理完請求的時間,標(biāo)志著Span的結(jié)束
ss:
服務(wù)端處理完邏輯的時間
sr:
服務(wù)端收到調(diào)用端請求的時間
客戶端調(diào)用時間 = cr-cs
服務(wù)端處理時間 = sr-ss
sr-cs:表示網(wǎng)絡(luò)延遲和時鐘抖動
ss-sr:表示服務(wù)端處理請求耗時
cr-ss:表示網(wǎng)絡(luò)延遲和時鐘抖動
Zipkin概述
Zipkin官網(wǎng):https://zipkin.io/洽损。
各個業(yè)務(wù)系統(tǒng)在相互協(xié)作調(diào)用時,將特定的跟蹤消息傳遞至zipkin黔攒,zipkin在收集到跟蹤消息之后將其進行聚合處理趁啸,存儲,展示等督惰,用戶可通過web UI方便清晰獲得網(wǎng)絡(luò)延遲不傅、調(diào)用鏈路、系統(tǒng)依賴等赏胚。
Zipkin主要涉及到四大組件
- Collector收集器:負責(zé)接收各service傳輸?shù)臄?shù)據(jù)
- Cassandra存儲器:作為Storage的一種访娶,也可以是mysql等,默認存儲在內(nèi)存里面
- Query查詢器:負責(zé)查詢Storage中存儲的數(shù)據(jù)信息觉阅,并且提供簡單的JSON API接口獲取數(shù)據(jù)給WEB UI使用
- Web UI展示器:提供簡單的web可視化界面
安裝
- 執(zhí)行以下命令下載jar包
wget -O zipkin.jar 'https://search.maven.org/remote_content?g=io.zipkin.java&a=zipkin-server&v=LATEST&c=exec'
由于本身是一個spring boot項目崖疤,所以可以直接運行jar
nohup java -jar zipkin.jar &
瀏覽器訪問:http://127.0.0.1:9411
簡單demo
新建四個工程項目:service1,service2典勇,service3劫哼,service4
它們之間的依賴關(guān)系如下:
service1調(diào)用service2,service2調(diào)用service3和service4割笙,service3和service4互相沒有依賴权烧,直接返回。新建4個springboot項目:
pom.xml文件引入zipkin的相關(guān)坐標(biāo)依賴:
<dependency>
<groupId>io.zipkin.brave</groupId>
<artifactId>brave-core</artifactId>
<version>3.9.0</version>
</dependency>
<dependency>
<groupId>io.zipkin.brave</groupId>
<artifactId>brave-spancollector-http</artifactId>
<version>3.9.0</version>
</dependency>
<dependency>
<groupId>io.zipkin.brave</groupId>
<artifactId>brave-web-servlet-filter</artifactId>
<version>3.9.0</version>
</dependency>
<dependency>
<groupId>io.zipkin.brave</groupId>
<artifactId>brave-apache-http-interceptors</artifactId>
<version>3.9.0</version>
</dependency>
<dependency>
<groupId>org.apache.httpcomponents</groupId>
<artifactId>httpclient</artifactId>
</dependency>
- 編寫zipkin配置類
@Configuration
public class ZipkinConfig {
/**
* 配置收集器
* @return SpanCollector
*/
@Bean
public SpanCollector spanCollector(){
Config config = HttpSpanCollector.Config.builder()
.compressionEnabled(false)
.connectTimeout(5000)
.flushInterval(1)
.readTimeout(6000)
.build();
return HttpSpanCollector.create("http://10.0.4.62:9411", config, new EmptySpanCollectorMetricsHandler());
}
/**
* Brave各工具類的封裝
* @param spanCollector 收集器
* @return Brave
*/
@Bean
public Brave brave(SpanCollector spanCollector){
//指定ServiceName
Builder builder = new Builder("Zipkin-Service1");
builder.spanCollector(spanCollector);
//設(shè)置采集率
builder.traceSampler(Sampler.create(1));
return builder.build();
}
/**
* 攔截器伤溉,需要serverRequestInterceptor,serverResponseInterceptor 分別完成sr和ss操作
* @param brave
* @return
*/
@Bean
public BraveServletFilter braveServletFilter(Brave brave){
return new BraveServletFilter(brave.serverRequestInterceptor(),
brave.serverResponseInterceptor(), new DefaultSpanNameProvider());
}
/**
* httpClient客戶端般码,需要clientRequestInterceptor,clientResponseInterceptor分別完成cs和cr操作
* @param brave
* @return
*/
@Bean
public CloseableHttpClient closeableHttpClient(Brave brave){
CloseableHttpClient httpClient = HttpClients.custom()
.addInterceptorFirst(new BraveHttpRequestInterceptor(brave.clientRequestInterceptor(),
new DefaultSpanNameProvider()))
.addInterceptorFirst(new BraveHttpResponseInterceptor(brave.clientResponseInterceptor()))
.build();
return httpClient;
}
}
SpanCollector:
配置收集器。
Brave:
各工具類的封裝,其中builder.traceSampler(Sampler.ALWAYS_SAMPLE)設(shè)置采樣比率乱顾,0-1之間的百分比板祝。
BraveServletFilter:
作為攔截器,需要serverRequestInterceptor,serverResponseInterceptor 分別完成sr和ss操作
CloseableHttpClient:
添加攔截器走净,需要clientRequestInterceptor,clientResponseInterceptor 分別完成cs和cr操作,該功能由brave中的brave-okhttp模塊提供券时,同樣的道理如果需要記錄數(shù)據(jù)庫的延遲只要在數(shù)據(jù)庫操作前后完成cs和cr即可,當(dāng)然brave提供其封裝伏伯。
- 編寫各個項目的controller
注意事項:
service1橘洞、service2、service3和service4的服務(wù)端口號需要修改成不一樣(例如:8081,8082,8083,8084)舵鳞,不然啟動會報錯震檩。
ZipkinConfig配置類指定的應(yīng)用服務(wù)名稱需要改變
測試分布式追蹤
分別啟動四個服務(wù)。啟動完成之后,瀏覽器訪問:http://localhost:8081/service1
在Web UI界面即可看到調(diào)用鏈以及依賴關(guān)系:
Zipkin + Dubbo整合
基本原理:
通過編寫filter過濾器抛虏,在請求處理的前后發(fā)送日志數(shù)據(jù)博其,讓zipkin生成調(diào)用鏈數(shù)據(jù)。單獨抽離出一個項目迂猴,通過注解配置的方式實現(xiàn)調(diào)用鏈的追蹤慕淡。
- 編寫自動配置的注解類
- 編寫自動配置的實現(xiàn),主要是將特定配置節(jié)點的值讀取到上下文對象中
- 編寫調(diào)用追蹤配置類沸毁,主要用于配置追蹤的一些參數(shù)峰髓,zipkin地址
- 配置Spring自動加載
a. 在resources/META-INF目錄下創(chuàng)建spring.factories文件
b. spring.factories內(nèi)容為:
org.springframework.boot.autoconfigure.EnableAutoConfiguration=com.touna.loan.trace.config.EnableTraceAutoConfiguration
- 追蹤上下文
- 創(chuàng)建Zipkin日志收集器
使用http的方式將日志信息發(fā)送給zipkin服務(wù),中間可以配合壓縮等優(yōu)化手法
- 日志收集器代理
使用線程池異步執(zhí)行日志發(fā)送息尺,避免阻塞正常業(yè)務(wù)邏輯
Dubbo Fillter過濾器
消費端Filter
消費端作為調(diào)用鏈的入口携兵,需要判斷是首次調(diào)用,還是內(nèi)部多次調(diào)用搂誉。如果是首次調(diào)用則生成新的traceId和spanId,如果是內(nèi)部多次調(diào)用徐紧,那么就直接從TraceContext調(diào)用鏈上下文中獲取traceId和spanId。消費端需要通過Invocation的參數(shù)列表將生成的traceId和spanId傳遞到下游系統(tǒng)中炭懊。
服務(wù)端Filter
基本與消費端的邏輯類似并级,只是這里將服務(wù)端的日志信息發(fā)送給zipkin,服務(wù)端接收消費端從Invocation的參數(shù)列表中傳遞過來的traceId和spanId侮腹,從而將整個RPC的調(diào)用邏輯串聯(lián)起來嘲碧。
Filter應(yīng)用
消費端:
1.Application啟動類開啟自動追蹤注解@EnableTraceAutoConfigurationProperties
2.配置消費端過濾器
<dubbo:consumer filter="traceConsumerFilter"/>
服務(wù)端:
1.Application啟動類啟用自動追蹤注解@EnableTraceAutoConfigurationProperties
2.配置服務(wù)端過濾器
<dubbo:provider filter="traceProviderFilter" />
Demo代碼演示:
代碼已上傳至github:git@github.com:hu1991die/zipkin-demo.git