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