1.概述
Zipkin是根據Google Dapper的論文設計的全鏈路監控系統,由Twitter公司開發。
Zipkin 以 Trace 結構表示對一次請求的追蹤,又把每個 Trace 拆分為若干個有依賴關係的 Span。在微服務架構中,一次使用者請求可能會由後臺若干個服務負責處理,那麼每個處理請求的服務就可以理解為一個 Span(可以包括 API 服務,快取服務,資料庫服務以及報表服務等)。當然這個服務也可能繼續請求其他的服務,因此 Span 是一個樹形結構,以體現服務之間的呼叫關係。
2.架構圖
虛線為zipkin-server提供的功能,主要包括四個模組:
- Collector 接受收集zipkin的客戶端傳輸的資料。
- Storage 儲存收集來的資料,預設是Memory,可配置為MySQL,Cassandra,ES等。
- API 負責查詢Storage中儲存的資料,提供給UI使用。
- UI 提供簡單的web頁面。
Instrumented為採集資料並將資料傳送給zipkin的客戶端。其主要採集的資料結構為Trace和Span。
3.Brave
Brave是Java版的zipkin客戶端。
我們一般不會手動編寫Trace相關的程式碼,Brave提供可一些開箱即用的庫,幫助我們追蹤一些特定的請求。比如dubbo,grpc,servlet,mysql,httpClient,kafka,springMVC等。
/**
* 客戶端具體怎麼收集資料的demo
*/
public static void main(String[] args) throws Exception{
// 1.構建客戶端傳送工具
Sender sender = OkHttpSender.create("http://127.0.0.1:9411/api/v2/spans");
// 2.構建非同步reporter
AsyncReporter asyncReporter = AsyncReporter.builder(sender)
.closeTimeout(500, TimeUnit.MILLISECONDS)
.build(SpanBytesEncoder.JSON_V2);
// 3.構建tracing上下文
Tracing tracing = Tracing.newBuilder()
.localServiceName("myService")
.spanReporter(asyncReporter)
.propagationFactory(ExtraFieldPropagation.newFactory(B3Propagation.FACTORY,"shuang"))
.currentTraceContext(ThreadContextCurrentTraceContext.create())
.build();
// 4.使用tracer建立span並操作start()和finish()方法
Tracer tracer = tracing.tracer();
Span span = tracer.newTrace().name("total").start();
Span action_1 = tracer.newChild(span.context()).name("action-1").start();
try {
Thread.sleep(500);
}finally {
action_1.finish();
}
Span action_2 = tracer.newChild(span.context()).name("action-2").start();
try {
Thread.sleep(500);
}finally {
action_2.finish();
}
try {
Thread.sleep(2000);
}finally {
span.finish();
}
}
複製程式碼
3.Tracing簡介
Tracing中依賴的幾個重要類
- Endpoint IP,埠,應用服務名等資訊
- Reporter 採集資料提報器
- Sampler 取樣器,根據TraceId來判斷該Trace是否需要被取樣
- CurrentTraceContext 將TraceContext繫結到ThreadLocal中,以便當前執行緒獲取
- Propagation 是一個可以向資料攜帶的物件carrier上注入和提取資料的介面
3.1Sampler
public abstract class Sampler {
/**
* 根據traceId是否需要取樣
*/
public abstract boolean isSampled(long traceId);
}
複製程式碼
其提供了幾個預設的取樣器實現。ALWAYS_SAMPLE,NERVER_SAMPLE和一個可定義取樣率的取樣器CountingSampler。
4.Span簡介
採集資料和傳送資料的核心介面為Span.start()和Span.finish()。
/**
* RealSpan依賴於Trace上下文,Recorder。
*/
static RealSpan create(TraceContext context, Clock clock, Recorder recorder) {
return new AutoValue_RealSpan(context, clock, recorder);
}
public Span start(long timestamp) {
recorder().start(context(), timestamp);
return this;
}
public void finish(long timestamp) {
recorder().finish(context(), timestamp);
}
public void finish(TraceContext context, long finishTimestamp) {
MutableSpan span = spanMap.remove(context);
if(span == null || noop.get()) return;
synchronized (span) {
span.finsh(finishTimestamp);
reporter.report(span.toSpan());
}
}
複製程式碼
RealSpan呼叫start()和finish(),獲取和TraceContext繫結的Span資訊,記錄開始時間和結束時間,並在結束時,呼叫reporter的report方法,上報給zipkin。
5.與springMVC整合
Brave利用springMVC提供的攔截器機制,在攔截前後分別呼叫前面的span相關程式碼,即做到對一次請求的監控。
<!-- Brave提供的攔截器 -->
<mvc:interceptors>
<bean class="brave.spring.webmvc.TracingHandlerInterceptor" factory-method="create">
<constructor-arg type="brave.http.HttpTracing" ref="httpTracing"/>
</bean>
</mvc:interceptors>
複製程式碼
在preHandle() 和 afterCompletion() 中分別呼叫span.start()和span.finish()方法完成對zipkin服務端的上報。詳見相關程式碼。
6.與Mysql整合
Mysql也在執行sql前後也提供了一個攔截介面。
/**
* mysql-connector.jar中提供的攔截介面
*/
public interface StatementInterceptorV2 extends Extension {
void init(Connection var1, Properties var2) throws SQLException;
ResultSetInternalMethods preProcess(String var1, Statement var2, Connection var3) throws SQLException;
boolean executeTopLevelOnly();
void destroy();
ResultSetInternalMethods postProcess(String var1, Statement var2, ResultSetInternalMethods var3, Connection var4, int var5, boolean var6, boolean var7, SQLException var8) throws SQLException;
}
複製程式碼
在preProcess()中執行span.start(), 在postProcess()中執行span.finish()。詳見相關程式碼。
7.其他中介軟體整合略。
8.總結
對zipkin的簡單介紹。良好的框架設計會給開發者帶來許多便利。(zipkin的客戶端大多是基於攔截介面實現的。)