Tracer 這個(gè)類主要用于做 Span 的相關(guān)操作。
核心方法有一下幾個(gè):
public Span newChild(TraceContext parent);//新建一個(gè)子跨度黑竞,有parent
public Span newTrace();//新建一個(gè)跨度捕发,無(wú)parent
public Span nextSpan();//新建一個(gè)跨度,根據(jù)當(dāng)前作用域判斷是新建自跨度還是新建無(wú)關(guān)聯(lián)的跨度
public final Span joinSpan(TraceContext context);//加入一個(gè)跨度很魂,這種情況一般是用于 Client -> Server 訪問(wèn)時(shí)出現(xiàn)的扎酷,在客戶端有一條trace記錄,在服務(wù)端進(jìn)行trace記錄時(shí)候莫换,使用了該方法,服務(wù)端會(huì)生成一條有同樣trace ID的記錄骤铃,合并客戶端與服務(wù)端這兩條trace信息可以則可以得到一條完整的trace日志拉岁。
public SpanInScope withSpanInScope(@Nullable Span span);//定義一個(gè)跨度的作用域,就是TreadLocal的原理惰爬,在該作用域下喊暖,你可以隨時(shí)獲得當(dāng)前 TraceContext
接下來(lái)一個(gè)個(gè)看:
public Span nextSpan() {
//獲取當(dāng)前 TraceContext
TraceContext parent = currentTraceContext.get();
//若存在則新建一個(gè)自跨度,否則新建一個(gè)無(wú)關(guān)聯(lián)跨度
return parent != null ? newChild(parent) : newTrace();
}
public Span newTrace() {
return _toSpan(newRootContext());//直接新建一個(gè)TraceContext撕瞧,新建一個(gè)span
}
Span _toSpan(TraceContext decorated) {
if (isNoop(decorated)) return new NoopSpan(decorated);
// 獲取或者創(chuàng)建一個(gè)掛起的跨度
//這里多了一個(gè)新建的對(duì)象叫 PendingSpan 陵叽,用于收集一條trace上暫時(shí)被掛起的未完成的span
PendingSpan pendingSpan = pendingSpans.getOrCreate(decorated, false);
//新建一個(gè)跨度(RealSpan是Span的一個(gè)實(shí)現(xiàn))
return new RealSpan(decorated, pendingSpans, pendingSpan.state(), pendingSpan.clock(),
finishedSpanHandler);
}
//可看出 nextSpan 就是調(diào)用 newChild 或者 newTrace而已
public Span nextSpan() {
TraceContext parent = currentTraceContext.get();
return parent != null ? newChild(parent) : newTrace();
}
//這個(gè)方法注解足夠,直接看就行了
public final Span joinSpan(TraceContext context) {
if (context == null) throw new NullPointerException("context == null");
if (!supportsJoin) return newChild(context);
int flags = InternalPropagation.instance.flags(context);
if (alwaysSampleLocal && (flags & FLAG_SAMPLED_LOCAL) != FLAG_SAMPLED_LOCAL) {
flags |= FLAG_SAMPLED_LOCAL;
}
// If we are joining a trace, we are sharing IDs with the caller
// If the sampled flag was left unset, we need to make the decision here
if ((flags & FLAG_SAMPLED_SET) != FLAG_SAMPLED_SET) { // cheap check for not yet sampled
// then the caller didn't contribute data
flags = InternalPropagation.sampled(sampler.isSampled(context.traceId()), flags);
} else if ((flags & FLAG_SAMPLED) == FLAG_SAMPLED) {
// we are recording and contributing to the same span ID
flags = flags | FLAG_SHARED;
}
context = InternalPropagation.instance.newTraceContext(
flags | FLAG_LOCAL_ROOT,
context.traceIdHigh(),
context.traceId(),
context.spanId(), // local root
context.parentIdAsLong(),
context.spanId(),
context.extra()
);
//返回一個(gè)跨度丛版,使用 propagationFactory.decorate() 裝飾一個(gè)跨度巩掺,實(shí)際上該出的代碼什么也沒(méi)裝飾,直接返回 context
return _toSpan(propagationFactory.decorate(context));
}
//新建作用域就一句代碼页畦,返回一個(gè) SpanInScope對(duì)象胖替,該對(duì)象用于定義一個(gè)作用域,調(diào)用它的 close 方法即可結(jié)束作用域
public SpanInScope withSpanInScope(@Nullable Span span) {
return new SpanInScope(currentTraceContext.newScope(span != null ? span.context() : null));
}
//看看currentTraceContext.newScope 做了什么
//首先它擁有一個(gè)內(nèi)部對(duì)象, local独令,就是一個(gè) TreadLocal 對(duì)象端朵,所以可以解釋作用域?yàn)槭裁纯梢噪S時(shí)獲取到所需的 TraceContext
final ThreadLocal<TraceContext> local;
public Scope newScope(@Nullable TraceContext currentSpan) {
//獲取之前作用域的 context
final TraceContext previous = local.get();
//設(shè)置當(dāng)前作用域的 context
local.set(currentSpan);
//接下來(lái)定義一個(gè)作用域的內(nèi)部類,它的close方法將之前作用域的 context 設(shè)置為當(dāng)前的
//也就是當(dāng)結(jié)束一個(gè)作用域的時(shí)候燃箭,將上一個(gè)作用域還原回來(lái)冲呢,如此控制
class ThreadLocalScope implements Scope {
@Override public void close() {
local.set(previous);
}
}
//新建一個(gè)作用域
Scope result = new ThreadLocalScope();
//調(diào)用方法裝飾這個(gè)作用域并返回(利用已設(shè)置好的裝飾器)
return decorateScope(currentSpan, result);
}
protected Scope decorateScope(@Nullable TraceContext currentSpan, Scope scope) {
int length = scopeDecorators.size();
//遍歷裝飾器,對(duì)作用域進(jìn)行裝飾包裝招狸,可忽略這部分拓展敬拓,一般使用并不會(huì)用上裝飾器,有興趣的同學(xué)可以研究下該部分
for (int i = 0; i < length; i++) {
scope = scopeDecorators.get(i).decorateScope(currentSpan, scope);
}
return scope;
}
到此為止瓢颅,Tracer的基本工作已經(jīng)大體清楚恩尾。
下一章將解讀跨度( Span )的內(nèi)容。