導(dǎo)讀:
一個分布式系統(tǒng)由若干分布式服務(wù)構(gòu)成,每一個請求會經(jīng)過多個業(yè)務(wù)系統(tǒng)并留下足跡,但是這些分散的數(shù)據(jù)對于問題排查,或是流程優(yōu)化都很有限,要能做到追蹤每個請求的完整鏈路調(diào)用,收集鏈路調(diào)用上每個服務(wù)的性能數(shù)據(jù),計算性能數(shù)據(jù)和比對性能指標(biāo)(SLA),甚至能夠再反饋到服務(wù)治理中,那么這就是分布式跟蹤的目標(biāo)。在業(yè)界:淘寶的鷹眼, 京東的Hydra實(shí)現(xiàn)了這個目標(biāo),這里要介紹的是twitter 的 zipkin。
ZipKin介紹
1、ZipKin簡介
1、Zipkin是一個致力于收集分布式服務(wù)的時間數(shù)據(jù)的分布式跟蹤系統(tǒng)。
2、Zipkin 主要涉及四個組件:collector(數(shù)據(jù)采集),storage(數(shù)據(jù)存儲),search(數(shù)據(jù)查詢),UI(數(shù)據(jù)展示)。
3、github源碼地址:https://github.com/openzipkin/zipkin。
4、Zipkin提供了可插拔數(shù)據(jù)存儲方式:In-Memory,MySql, Cassandra, Elasticsearch;本文為了測試方便以In-Memory方式進(jìn)行存儲,個人推薦Elasticsearch,關(guān)于更多的存儲方式可以參考github。
5、ZipKin運(yùn)行環(huán)境需要Jdk8支持。
6、下載并啟動ZipKin:
下載并規(guī)劃成如下目錄結(jié)構(gòu),可以自行更改。
說明:bin目錄為啟動腳本所在目錄,lib目錄為zipkin-server jar所在目錄。
附下Jdk8下啟動腳本,存儲方式不是本篇重點(diǎn),要更換存儲方式請參考github。
運(yùn)行時參數(shù):
通過http://XXX:9411就可以訪問zipkin UI控制臺
2、ZipKin數(shù)據(jù)模型
Trace:一組代表一次用戶請求所包含的spans,其中根span只有一個。
Span: 一組代表一次HTTP/RPC請求所包含的annotations。
annotation:包括一個值,時間戳,主機(jī)名(留痕跡)。
3?ZipKin生成調(diào)用鏈
把一些輕量級的TraceID和Span ID在服務(wù)之間傳遞,服務(wù)之間將信息報告給Zipkin,ZipKin將服務(wù)之間調(diào)用關(guān)系有效組成一個完整的調(diào)用鏈。
注:TraceId:全局ID, spanId-每個方法調(diào)用的id,parentSpanId-父SpanId, sampled-是否需要采樣。
1、 客戶端:
客戶端需要把Trace(traceId,spanId,parentSpanId,sampled)信息放在Request/ThreadLocal中。
如果需要進(jìn)行HTTP/RPC調(diào)用,需要把Trace的信息放在協(xié)議中,例如http header/Rpc Extraparams。
2、 服務(wù)端:
檢查http header/Rpc Extraparams中是否存在Trace信息,如果存在就把這些信息取出來,然后存入到Request/ThreadLocal中,進(jìn)而把請求串接起來。如果不存在,那么就開始一個新的Trace。
?Brave介紹
1、?Brave簡介
Brave 是用來裝備 Java 程序的類庫,提供了面向標(biāo)準(zhǔn)Servlet、Spring MVC、Http Client、JAX RS、Jersey、Resteasy 和 MySQL 等接口的裝備能力,可以通過編寫簡單的配置和代碼,讓基于這些框架構(gòu)建的應(yīng)用可以向 Zipkin 報告數(shù)據(jù)。同時 Brave 也提供了非常簡單且標(biāo)準(zhǔn)化的接口,在以上封裝無法滿足要求的時候可以方便擴(kuò)展與定制。
雖然Brave提供了默認(rèn)的實(shí)現(xiàn),結(jié)合項目實(shí)際情況,基本上是需要定制才能滿足要求的,本文針對默認(rèn)實(shí)現(xiàn)就不再啰嗦,直接針對定制進(jìn)行講解。
由于項目中用到SpringMvc,HttpClient,Jprotobuf-Rpc-Socket,本文主要介紹針對SpringMvc,HttpClient,Jprotobuf-Rpc-Socket的擴(kuò)展與定制。
2、服務(wù)調(diào)用常用的兩種方式
1、服務(wù)以Http方式提供Rest接口,服務(wù)與服務(wù)之間通過HttpClient互相調(diào)用,對外以Http方式提供Rest接口,這里Rest以SpringMvc為例。
2、服務(wù)以jprotobufrpcsocket方式提供Rpc接口,服務(wù)與服務(wù)之間通過RPC互相調(diào)用,對外以Http方式提供Rest接口,這里Rest以SpringMvc為例,RPC以jprotobufrpcsocket為例。
3、Brave環(huán)境準(zhǔn)備
1、Maven引入
2、通過實(shí)現(xiàn)FactoryBean接口,創(chuàng)建Brave實(shí)例,同時為Brave實(shí)例設(shè)置Http采集器,默認(rèn)采用日志打印方式。
FactoryBean:
注:FactoryBean的作用在于更靈活創(chuàng)建Brave實(shí)例,serviceName為對應(yīng)用服務(wù)的名稱,ZipkinSetting為HttpSpanCollector實(shí)例需要的參數(shù)配置,以Http方式采集數(shù)據(jù),就需要例如超時時間等這樣的參數(shù)配置。
ZipkinSetting:
Brave環(huán)境的準(zhǔn)備就講到這里了。
4、HttpClient裝配
Brave默認(rèn)提供了OkHttpClient的支持,但是對于一個完成了的項目來說并不合適,因此我需要對HttpClient定制,對Http請求增加攔截功能,能在請求前后埋點(diǎn)。
1、HttpInvokeInteceptor:這個接口的作用在通過HttpClient請求前和請求后埋點(diǎn)。
2、HTTPClient:這個類的作用在于每次請求都會調(diào)用HttpClient execute方法,因此在execute方法體,我們可以在請求前和請求后埋點(diǎn)實(shí)現(xiàn)鏈路跟蹤;在這個類持有HttpInvokeInteceptor的引用,完成請求前和請求后攔截。
3、HTTPRequest:這個類是POST,GET,DELETE,PUT等請求的父類,這里定義了URL和Method,以便在請求前埋點(diǎn)處留下更清晰的足跡,目的在于在Zipkin能留下url,method信息。
4、HTTPResponse:請求返回數(shù)據(jù),在獲取到HTTPResponse以后用來在請求完成后埋點(diǎn)留下更清晰的足跡。
5、FormPost:HTTPRequest的子類,真實(shí)的請求類。
6、BraveHttpClientInteceptor:BraveHttpClientInteceptor是HttpInvokeInteceptor的實(shí)現(xiàn)類,真實(shí)的HttpClient裝配的實(shí)現(xiàn)。
請求前通過實(shí)現(xiàn)ClientRequestAdapter接口,請求后通過實(shí)現(xiàn)ClientResponseAdapter接口完成定制。
以上完成了HttpClient的裝配。
5、SpringMvc裝配
Brave庫本身提供了SpringMVC攔截器針對Controller處理前后埋點(diǎn)的支持,接下來這是在此基礎(chǔ)上做了改寫:
讓埋點(diǎn)信息更完善
增加了訪問記錄采集,有了訪問記錄,為服務(wù)治理做好準(zhǔn)備(服務(wù)治理不作為講解的范疇)
BraveHttpServerInterceptor:SpringMvc標(biāo)準(zhǔn)攔截器,主要用于在Controller處理前和處理后埋點(diǎn)實(shí)現(xiàn)請求跟蹤。主要用于Server端裝配。
6、JprotobufRpcSocket裝配
1、版本選擇:JprotobufRpcSocket3.4.4
2、JprotobufRpcSocket3.4.4 BUG: 客戶端攔截器不生效。
原因:
HaProtobufRpcProxyBean繼承HaProtobufRpcProxy,HaProtobufRpcProxy的onBuildProtobufRpcProxy方法如下:
HaProtobufRpcProxyBean有對該方法重寫,重寫的方法如下:
方法重寫以后對應(yīng)的攔截器沒有往下傳,導(dǎo)致攔截器不可用,這個bug修復(fù)如果通過修改源代碼的方式比較麻煩,建議在項目中按照下面方式修改,不會涉及到Jprotobuf-Rpc-Socket依賴的更改與管理。
FIX:
Fix后的代碼結(jié)構(gòu)如下:
保持包名不變,對HaProtobufRpcProxyBean更名為MatrixHaProtobufRpcProxyBean ,HaProtobufRpcProxy更名為MatrixHaProtobufRpcProxy,HaRpcProxyFactoryBean更名為MatrixHaRpcProxyFactoryBean。
MatrixHaRpcProxyFactoryBean代碼做如下改動:
MatrixHaProtobufRpcProxy代碼做如下改動:
MatrixHaProtobufRpcProxyBean將攔截器往下傳,代碼做如下改動:
MatrixHaProtobufRpcProxy繼承NamingServiceChangeListener取代HaProtobufRpcProxy。
在客戶端使用的時候通過MatrixHaRpcProxyFactoryBean取代HaRpcProxyFactoryBean創(chuàng)建接口代理。
3、JprotobufRpcSocket3.4.4 Extraparams坑: Jprotobuf-Rpc-Socket 攔截器InvokerInterceptor可以通過MethodInvocation以Aop的方式往服務(wù)端傳值。
需要通過SerializationUtils序列化與反序列化。
序列化的數(shù)據(jù)結(jié)構(gòu)需要是Map。
查看RemoteExcuteInvokeRpcHandler 82行源代碼發(fā)現(xiàn):有通過SerializationUtils反序列化并且強(qiáng)轉(zhuǎn)為MAP:
4、RPC裝配源碼介紹:RpcPrepareInteceptor
RpcPrepareInteceptor: BraveRpcClientInterceptor與BraveRpcServerInteceptor的父類。
BraveServerRequest, BraveClientRequest,BraveServerResponse,BraveClientResponse ? RPC擴(kuò)展點(diǎn)。
5、RPC客戶端裝配BraveRpcClientInterceptor源碼介紹:
6、RPC服務(wù)端裝配BraveRpcServerInteceptor源碼介紹:
未完待續(xù)~~~
本文作者:秦瑜 Chris.Qin(點(diǎn)融黑幫),來自點(diǎn)融BE Team,2015年10月加入點(diǎn)融,負(fù)責(zé)多個項目的架構(gòu)與設(shè)計,多年大并發(fā)分布式互聯(lián)網(wǎng)架構(gòu)經(jīng)驗。