1. Span
- Span代表一个完整的调用过程,类似于方法栈的栈针,如:helloService.hello()的栈针,包括了这个方法的出入参,环境等。Span可以表示一个RPC调用的栈针,包括请求、响应和调用上下文。
- SkyWalking将Span分为三种:
- EntrySpan
- 从字面理解这个是入口/进入Span。它是指谁的入口,又是进入谁呢?以客户端和服务端为例的话,它是服务端的概念,指服务端接收请求的入口,进入服务端。一般是将客户端的信息带入给服务端,返回响应数据给客户端。跨进程时使用。
- ExitSpan
- 从字面理解这个是出口/离开Span。还以客户端和服务端为例的话,它是客户端的概念,指客户端发送请求的出口,离开客户端。一般是将客户端信息带出给服务端,接收服务端响应数据。也是在跨进程时使用,与EntrySpan是一对。
- LocalSpan
- 从字面理解这个是本地Span。这个是在多线程时使用的,是一个子线程栈的栈顶元素,表示一个线程追踪的开始。跨线程时使用。
- EntrySpan
2. ContextCarrier
- 追踪上下文载体。跨进程时使用,用于装载上一个服务端系统的trace信息,传播给下一个服务端系统,使各个系统的trace信息可以关联。
- 举个例子:客户端调用服务端系统A,A又先后同步调用B,C。然后系统A返回响应给客户端。skywalking在系统A上生成分布式traceId,以及携带其他扩展或者用户的信息,装载到ContextCarrier对象上,传播给B,B会使用ContextCarrier上携带的分布式traceId,替换自己的分布式TraceId。这样A,B,C三个系统的追踪日志就关联上了。
3. ContextSnapshot
- 追踪上下文快照。跨线程时使用,用于记录主线程在创建Runnable/Callable/Supplier任务时主线程的追踪上下文数据,包括traceId等,set到子线程的任务Runnable/Callable中,用于多线程时关联追踪日志。
4. RunnableWrapper/CallableWrapper/SupplierWrapper
有没有想过如何拦截和增强多线程,使多线程可以被追踪?如果只是单纯的拦截所有jdk原生的Runnable/Callable,会导致除了业务系统以外的,不需要被追踪的线程也被追踪,浪费了大量的内存空间。如SkyWalking框架本身使用的线程池也会被追踪,但是这个数据对我们来说没有意义。
为了对业务线程任务和框架线程任务进行区分,SkyWalking定义了RunnableWrapper/CallableWrapper/SupplierWrapper这几个类,以及@TraceCrossThread注解,提供给业务线程使用,SkyWalking只对被Wrapper包装了的,和含有@TraceCrossThread注解的任务进行追踪。将需要追踪的线程任务包装成另一个类型,用来区分开jdk原生任务类型。
-
使用举例:
//方式1 @TraceCrossThread public static class MyCallable<String> implements Callable<String> { @Override public String call() throws Exception { return null; } } ExecutorService executorService = Executors.newFixedThreadPool(1); executorService.submit(new MyCallable()); //方式2 ExecutorService executorService = Executors.newFixedThreadPool(1); executorService.execute(RunnableWrapper.of(new Runnable() { @Override public void run() { //your code } })); //方式3 ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor(); executor.setThreadFactory(r -> new Thread(RunnableWrapper.of(r))); //其他方式等等
5. TraceId
- 唯一定位一个调用链信息的id。
- SkyWalking在业务上定义了两种TraceId
- TraceSegmentId: 对应一个线程的trace id。
- DistributedTraceId: 唯一定位一个分布式操作的trace id。包括发出一个请求到收到响应的过程中,经历的所有分布式系统的trace数据,通过该id进行关联。
6. TraceSegment
- 一个TraceSegment对应一个线程内所有的追踪数据,这里重点是一个线程,即单线程。如果有两个线程就会对应有两个TraceSegment。一个TraceSegment包含多个Span。
- 一个TraceSegment对应一个traceSegmentId,用来唯一定位该TraceSegment。还持有一到多个关联的分布式操作的DistributedTraceId。
7. TracingContext
Trace的上下文,主要持有一个TraceSegment,以及模拟的Span栈,实际上应该说是TraceSegment的上下文。代码结构:
public class TracingContext implements AbstractTracerContext {
//一个上下文持有一个TraceSegment
private TraceSegment segment;
//处于活动中,未完成状态的Span的栈集合
private LinkedList<AbstractSpan> activeSpanStack = new LinkedList<>();
//根据id升序,还原一个TraceSegment内,多个span的先后执行关系。
private int spanIdGenerator;
//其他省略。。。
private final CorrelationContext correlationContext;
private final ExtensionContext extensionContext;
private final SpanLimitWatcher spanLimitWatcher;
TracingContext(String firstOPName, SpanLimitWatcher spanLimitWatcher) {
//创建一个新的TraceSegment,包括新的traceId,DistributedTraceId。
this.segment = new TraceSegment();
//span id清零
this.spanIdGenerator = 0;
//默认同步模式,即单线程
isRunningInAsyncMode = false;
createTime = System.currentTimeMillis();
running = true;
this.correlationContext = new CorrelationContext();
this.extensionContext = new ExtensionContext();
this.spanLimitWatcher = spanLimitWatcher;
}
}