一、Zipkin 介紹
Zipkin 是什麼?
Zipkin的官方介紹:https://zipkin.apache.org/
Zipkin是一款開源的分散式實時資料追蹤系統(Distributed Tracking System),基於 Google Dapper的論文設計而來,由 Twitter 公司開發貢獻。其主要功能是聚集來自各個異構系統的實時監控資料。分散式跟蹤系統還有其他比較成熟的實現,例如:Naver的Pinpoint、Apache的HTrace、阿里的鷹眼Tracing、京東的Hydra、新浪的Watchman,美團點評的CAT,skywalking等。
為什麼用 Zipkin?
隨著業務越來越複雜,系統也隨之進行各種拆分,特別是隨著微服務架構和容器技術的興起,看似簡單的一個應用,後臺可能有幾十個甚至幾百個服務在支撐;一個前端的請求可能需要多次的服務呼叫最後才能完成;當請求變慢或者不可用時,我們無法得知是哪個後臺服務引起的,這時就需要解決如何快速定位服務故障點,Zipkin分散式跟蹤系統就能很好的解決這樣的問題。
Zipkin的一些基本概念?
Brave
Brave 是用來裝備 Java 程式的類庫,提供了面向 Standard Servlet、Spring MVC、Http Client、JAX RS、Jersey、Resteasy 和 MySQL 等介面的裝備能力,可以通過編寫簡單的配置和程式碼,讓基於這些框架構建的應用可以向 Zipkin報告資料。同時 Brave 也提供了非常簡單且標準化的介面,在以上封裝無法滿足要求的時候可以方便擴充套件與定製。
如下圖是 Brave 的結構圖。Brave 利用 reporter 向 Zipkin的 Collector 傳送 trace 資訊。
Brave 主要是利用攔截器在請求前和請求後分別埋點。例如 Spingmvc 監控使用 Interceptors,Mysql 監控使用 statementInterceptors。同理 Dubbo 的監控是利用 com.alibaba.dubbo.rpc.Filter 來過濾生產者和消費者的請求。
traceId
一次請求全域性只有一個traceId。用來在海量的請求中找到同一鏈路的幾次請求。比如servlet伺服器接收到使用者請求,呼叫dubbo服務,然後將結果返回給使用者,整條鏈路只有一個traceId。開始於使用者請求,結束於使用者收到結果。
spanId
一個鏈路中每次請求都會有一個spanId。例如一次rpc,一次sql都會有一個單獨的spanId從屬於traceId。
cs
Clent Sent 客戶端發起請求的時間,比如 dubbo 呼叫端開始執行遠端呼叫之前。
cr
Client Receive 客戶端收到處理完請求的時間。
ss
Server Receive 服務端處理完邏輯的時間。
sr
Server Receive 服務端收到呼叫端請求的時間。
sr - cs = 請求在網路上的耗時
ss - sr = 服務端處理請求的耗時
cr - ss = 回應在網路上的耗時
cr - cs = 一次呼叫的整體耗時
Zipkin的工作過程
當使用者發起一次呼叫時,Zipkin 的客戶端會在入口處為整條呼叫鏈路生成一個全域性唯一的 trace id,併為這條鏈路中的每一次分散式呼叫生成一個 span id。span 與 span 之間可以有父子巢狀關係,代表分散式呼叫中的上下游關係。span 和 span 之間可以是兄弟關係,代表當前呼叫下的兩次子呼叫。一個 trace 由一組 span 組成,可以看成是由 trace 為根節點,span 為若干個子節點的一棵樹。
Zipkin 會將 trace 相關的資訊在呼叫鏈路上傳遞,並在每個呼叫邊界結束時非同步的把當前呼叫的耗時資訊上報給 Zipkin Server。Zipkin Server 在收到 trace 資訊後,將其儲存起來。隨後 Zipkin 的 Web UI 會通過 API 訪問的方式從儲存中將 trace 資訊提取出來分析並展示。
二、Zipkin的部署與執行
Zipkin的 github 地址:https://github.com/apache/incubator-zipkin
Docker 方式
docker run -d -p 9411:9411 openzipkin/zipkin
Jar 包方式(JDK8)
curl -sSL https://zipkin.apache.org/quickstart.sh | bash -s
java -jar zipkin.jar
注意:以上方式的 Zipkin 都是基於記憶體儲存,Zipkin 重啟後資料會丟失,建議測試環境使用。Zipkin 支援的儲存型別有 inMemory、MySql、Cassandra、以及 ElasticsSearch 幾種方式。正式環境推薦使用 Cassandra 和 ElasticSearch。
三、Zipkin 與 Dubbo 和 Springmvc 的整合
上面我們搭建好了 Zipkin 伺服器,現在的任務就是如何把我們系統內產生的請求資料包送給 Zipkin 伺服器,以便在 UI 上渲染出來。
1. pom.xml
<!-- 使用 okhttp3 作為 reporter -->
<dependency>
<groupId>io.zipkin.reporter2</groupId>
<artifactId>zipkin-sender-okhttp3</artifactId>
<version>2.8.2</version>
</dependency>
<!-- brave 對 dubbo 的整合 -->
<dependency>
<groupId>io.zipkin.brave</groupId>
<artifactId>brave-instrumentation-dubbo-rpc</artifactId>
<version>5.6.3</version>
</dependency>
<!-- brave 對 mvc 的整合 -->
<dependency>
<groupId>io.zipkin.brave</groupId>
<artifactId>brave-instrumentation-spring-webmvc</artifactId>
<version>5.6.3</version>
</dependency>
2. application.yml
zipkin:
url: http://127.0.0.1:9411/api/v2/spans
connectTimeout: 5000
readTimeout: 10000
# 取樣率,指的是多次請求中有百分之多少傳到zipkin。例如 1.0 是全部取樣,0.5是 50% 取樣
rate: 1.0f
3. ZipkinProperties.java
@Configuration
@ConfigurationProperties("zipkin")
public class ZipkinProperties {
@Value("${spring.application.name}")
private String serviceName;
private String url;
private Long connectTimeout;
private Long readTimeout;
private Float rate;
/*getter and setter*/
}
注意:記得在 SpringBoot 的啟動類上加上 @EnableConfigurationProperties 註解才能使 @ConfigurationProperties("zipkin") 生效哦!
4. ZipkinConfig.java
@Configuration
public class ZipkinConfig {
@Autowired
private ZipkinProperties zipkinProperties;
/**
* 為了實現 dubbo rpc呼叫的攔截
*
* @return
*/
@Bean
public Tracing tracing() {
Sender sender = OkHttpSender.create(zipkinProperties.getUrl());
AsyncReporter reporter = AsyncReporter.builder(sender)
.closeTimeout(zipkinProperties.getConnectTimeout(), TimeUnit.MILLISECONDS)
.messageTimeout(zipkinProperties.getReadTimeout(), TimeUnit.MILLISECONDS)
.build();
Tracing tracing = Tracing.newBuilder()
.localServiceName(zipkinProperties.getServiceName())
.propagationFactory(ExtraFieldPropagation.newFactory(B3Propagation.FACTORY, "shiliew"))
.sampler(Sampler.create(zipkinProperties.getRate()))
.spanReporter(reporter)
.build();
return tracing;
}
/**
* MVC Filter,為了實現 SpringMvc 呼叫的攔截
* @param tracing
* @return
*/
@Bean
public Filter tracingFilter(Tracing tracing) {
HttpTracing httpTracing = HttpTracing.create(tracing);
httpTracing.toBuilder()
.serverParser(new HttpServerParser() {
@Override
public <Req> String spanName(HttpAdapter<Req, ?> adapter, Req req) {
return adapter.path(req);
}
})
.clientParser(new HttpClientParser() {
@Override
public <Req> String spanName(HttpAdapter<Req, ?> adapter, Req req) {
return adapter.path(req);
}
}).build();
return TracingFilter.create(httpTracing);
}
}
如此,我們就把 Zipkin 和 Dubbo 以及 Springmvc 的整合做好了:
踩過的坑如下,共勉:
- Zipkin Server 一定要在呼叫後才會產生資料,不會先把服務的資訊註冊上去。
- MVC 的攔截,span 的名字是以請求方式命名的,如下:
如果仍然想檢視,某個請求路徑的呼叫情況呢?
github 原始碼:https://github.com/JMCuixy/dubbo-demo