使用 Zipkin 和 Brave 實現分布式系統追蹤
一、Zipkin
1.1 Zipkin
是一款開源的分布式實時數據追蹤系統(Distributed Tracking System),基於 Google Dapper 的論文設計而來,由 Twitter 公司開發貢獻。其主要功能是聚集來自各個異構系統的實時監控數據,用來追蹤微服務架構下的系統延時問題。
應用系統需要進行裝備(instrument)以向 Zipkin 報告數據。Zipkin 的用戶界面可以呈現一幅關聯圖表,以顯示有多少被追蹤的請求通過了每一層應用。 Zipkin 以 Trace 結構表示對一次請求的追蹤,又把每個 Trace 拆分為若幹個有依賴關系的 Span。在微服務架構中,一次用戶請求可能會由後臺若幹個服務負責處理,那麽每個處理請求的服務就可以理解為一個 Span(可以包括 API 服務,緩存服務,數據庫服務以及報表服務等)。當然這個服務也可能繼續請求其他的服務,因此 Span 是一個樹形結構,以體現服務之間的調用關系。 Zipkin 的用戶界面除了可以查看 Span 的依賴關系之外,還以瀑布圖的形式顯示了每個 Span 的耗時情況,可以一目了然的看到各個服務的性能狀況。打開每個 Span,還有更詳細的數據以鍵值對的形式呈現,而且這些數據可以在裝備應用的時候自行添加。 整個調用鏈中有兩個微服務 service1 和 service2,在 10ms(相對時間點)的時候,service1 作為客戶端向 service2 發送了一個請求(Client Send),之後 service2 服務於 19ms 的時候收到請求(Server Receive),並用了 12ms 的時間來處理,並於 31ms 時刻將數據返回(Server Send),最後 service1 服務於 1ms 以後接收到此數據(Client Receive),因此整個過程共耗時 22ms。圖中還給出了 service1 訪問 service2 服務前後 Http Client 連接池的狀態信息。
1.2、架構
Zipkin 主要由四部分構成:收集器、數據存儲、查詢以及 Web 界面。Zipkin 的收集器負責將各系統報告過來的追蹤數據進行接收;而數據存儲默認使用 Cassandra,也可以替換為 MySQL;查詢服務用來向其他服務提供數據查詢的能力,而 Web 服務是官方默認提供的一個圖形用戶界面。
1.3、運行
使用 Docker 運行 Zipkin 最為簡單,其過程如下:
gitclone https://github.com/openzipkin/docker-zipkin
cd docker-zipkin
docker-composeup
這樣啟動,默認會使用 Cassandra 數據庫,如果想改用 MySQL,可以換做以下命令啟動:
docker-compose -f docker-compose.yml -f docker-compose-mysql.yml up
啟動成功以後,可以通過 http:// :8080 來訪問。具體獲取 IP 地址的方法請參閱 Docker 的相關文檔。
二、Brave
2.1、簡介
Brave 是用來裝備 Java 程序的類庫,提供了面向 Standard Servlet、Spring MVC、Http Client、JAX RS、Jersey、Resteasy 和 MySQL 等接口的裝備能力,可以通過編寫簡單的配置和代碼,讓基於這些框架構建的應用可以向 Zipkin 報告數據。同時 Brave 也提供了非常簡單且標準化的接口,在以上封裝無法滿足要求的時候可以方便擴展與定制。
2.2、初始化
Brave 的初始化就是要構建 Brave 類的實例,該庫提供了 Builder 類用來完成這件事情。
註:下文中約定,大寫的 Brave 指該 Java 類庫,而 Brave 類指 com.github.kristofa.brave.Brave 類型,而小寫的 brave 指該類型的實例。
Brave.Builderbuilder = new Brave.Builder("serviceName");
Bravebrave = builder.build();
其中的 serviceName 是當前服務的名稱,這個名稱會出現在所有跟該服務有關的 Span 中。默認情況下,Brave 不會將收集到的監控數據發送給 Zipkin 服務器,而是會以日誌的形式打印到控制臺。如果需要將數據發送給服務器,就需要引入 HttpSpanCollector 類。當前版本(3.8.0)將這個類命名為 Collector,這個概念容易跟 Zipkin 自身的 Collector 相混淆,因此在 Issue #173 中官方建議將其更名為 Reporter,也就是說這個類是用來向 Zipkin 的 Collector 報告數據的。
使用 HttpSpanCollector 的方法如下:
Brave.Builderbuilder = new Brave.Builder("serviceName");
builder.spanCollector(HttpSpanCollector.create(
"http://localhost:9411",
new EmptySpanCollectorMetricsHandler()));
Bravebrave = builder.build();
使用 HttpSpanCollector.create 方法可以創建該類的一個對象,第一個參數就是 Zipkin 服務的地址(默認部署時的端口為 9411)。
如果使用 Spring 的話,為了方便擴展,建議添加一個名為 ZipkinBraveFactoryBean 的類,其內容大致如下:
package net.tangrui.example.brave;
// 省略所有的 import
public class ZipkinBraveFactoryBean implements FactoryBean<Brave> {
private final String serviceName;
private final String zipkinHost;
private Braveinstance;
public void setServiceName(final String serviceName) {
this.serviceName = serviceName;
}
public void setZipkinHost(final String zipkinHost) {
this.zipkinHost = zipkinHost;
}
private void createInstance() {
if (this.serviceName == null) {
throw new BeanInitializationException("Property serviceName
must be set.");
}
Brave.Builderbuilder = new Brave.Builder(this.serviceName);
if (this.zipkinHost != null && !"".equals(this.zipkinHost)) {
builder.spanCollector(HttpSpanCollector.create(
this.zipkinHost, new EmptySpanCollectorMetricsHandler()));
}
this.instance = builder.build();
}
@Override
public BravegetObject() throws Exception {
if (this.instance == null) {
this.createInstance();
}
return this.instance;
}
@Override
public Class<?> getObjectType() {
return Brave.class;
}
@Override
public boolean isSingleton() {
return true;
}
}
然後只需要在 application-context.xml 配置文件中使用該 FactoryBean 就可以了:
<beanid="brave"
class="net.tangrui.example.brave.ZipkinBraveFactoryBean"
p:serviceName="serviceName"
p:zipkinHost="http://localhost:9411"/>
2.3、裝備標準的 Servlet 應用
Brave 提供了 brave-web-servlet-filter 模塊,可以為標準的 Servlet 應用添加向 Zipkin 服務器報告數據的能力,需要做的就是在 web.xml 文件增加一個 BraveServletFilter。
不過這個 Filter 在初始化的時候需要傳入幾個參數,這些參數可以通過 brave 對象的對應方法獲得,但是註入這些構造參數,最簡單的辦法還是使用 Spring 提供的 DelegatingFilterProxy。
在 web.xml 中添加如下內容(最好配置為第一個 Filter,以便從請求最開始就記錄數據):
<filter>
<filter-name>braveFilter</filter-name>
<filter-class>
org.springframework.web.filter.DelegatingFilterProxy
</filter-class>
<init-param>
<param-name>targetFilterLifecycle</param-name>
<param-value>true</param-value>
</init-param>
</filter>
<filter-mapping>
<filter-name>braveFilter</filter-name>
<url-pattern>/*</url-pattern>
<dispatcher>REQUEST</dispatcher>
<dispatcher>FORWARD</dispatcher>
<dispatcher>INCLUDE</dispatcher>
<dispatcher>ERROR</dispatcher>
</filter-mapping>
然後在配置文件中添加以下內容(創建 brave Bean 的有關代碼請參考上文):
使用 Zipkin 和 Brave 實現分布式系統追蹤