本篇介绍一下OpenTraing-Java中比较重要的一个概念, Scope与ScopeManager;
PS: 原打算放到上篇文章中来写的,太长了,故单独又写了一篇。 转载请注明出处;喜欢请一键三连哦;
简单来说,OpenTracing-Java的实现中, 用Scope和ScopeManager 来处理了OpenTracing中的上下文 (即:get_current_span 过程);
Scope 对象是 Active Span的容器;通过Scope能拿到当前上下文内的Active Span; ThreadLocalScope 是Scope的一个实现,通过ThreadLocal 来存储;
看到这里, 不知道你是否和我有一样的疑问?
为什么要抽象出Scope的概念?直接使用ThreadLocal 存储Span不就可以了吗?
答: 首先理解Scope是什么?Scope 是Active Span的一个容器, Scope 代表着当前活跃的Span; 是对当前活跃Span的一个抽象, 代表了当前上下文所处于的一个过程;
另外, ThreadLocalScope 还记录了 toRestore Span, 这样结束时,可以恢复到上一个Span的状态;
我理解如果只是 get_current_span() 逻辑的话,直接把 span 塞到 ThreadLocal里就可以在线程内传递了;但是ScopeManager看代码是这样实现的,ScopeManager 包含一个 Scope, Scope 又包含了 当前Span, recover Scope;我理解它的好处是: 这样就保证了,如果开启一个子Span(子span 会产生孙子span), 这样 子span 结束后,还可以回到 父span (这样可以继续产生以 父span 为基础的兄弟span), 如果只是ThreadLocal 里塞一个当前span的话,是解决不了这种情况的。
按我的理解画了一幅图:
/**
* 激活当前 Span. 返回Scope(可以理解为 代表当前 Span 活跃的一个阶段)
*
* Span 活跃期结束后,需要关闭 Scope, 推荐使用 try-with-resources 关闭
*
*/
Scope activate(Span span);
/**
* 返回当前 激活(active)状态Span, 无则返回null
*/
Span activeSpan();
ThreadLocalScopeManager 是ScopeManager 的一个具体实现;使用原始的ThreadLocal 来存储 Active Span; ScopeManager中仅包含一个Scope( Active Span)
public class ThreadLocalScopeManager implements ScopeManager {
// 当前Scope: 即当前上下文中的 active span
final ThreadLocal<ThreadLocalScope> tlsScope = new ThreadLocal<ThreadLocalScope>();
@Override
public Scope activate(Span span) {
return new ThreadLocalScope(this, span);
}
@Override
public Span activeSpan() {
ThreadLocalScope scope = tlsScope.get();
return scope == null ? null : scope.span();
}
}
补充: JaegerTracer
Tracer中的start方法(开启一个Span),使用了scopeManager 来获取上下文,从而来处理父子关系;
@Override
public JaegerSpan start() {
JaegerSpanContext context;
// PS. 此处从ScopeManager获取上下文(线程)中,获取到激活的Span, 而后创建父子关系
// Check if active span should be established as CHILD_OF relationship
if (references.isEmpty() && !ignoreActiveSpan && null != scopeManager.activeSpan()) {
asChildOf(scopeManager.activeSpan());
}
if (references.isEmpty() || !references.get(0).getSpanContext().hasTrace()) {
context = createNewContext();
} else {
context = createChildContext();
}
...
return jaegerSpan;
}
实现自Scope, 用ThreadLocal 来存储;
public class ThreadLocalScope implements Scope {
private final ThreadLocalScopeManager scopeManager;
// 当前 Active Span
private final Span wrapped;
// 上一Active Span,wrapped 结束时,会恢复到此Span
// 为什么要有此字段,上面我们已经说过了
private final ThreadLocalScope toRestore;
ThreadLocalScope(ThreadLocalScopeManager scopeManager, Span wrapped) {
this.scopeManager = scopeManager;
this.wrapped = wrapped;
this.toRestore = scopeManager.tlsScope.get();
scopeManager.tlsScope.set(this);
}
@Override
public void close() {
if (scopeManager.tlsScope.get() != this) {
// This shouldn't happen if users call methods in the expected order. Bail out.
return;
}
scopeManager.tlsScope.set(toRestore);
}
Span span() {
return wrapped;
}
}
OpenTracing Scope