前言
在上篇文章《SkyWalking系列学习之环境搭建以及分析如何类增强》中已经知道业务系统通过-javaagent引入代理,通过ByteBuddy对指定的类做增强处理。下面介绍Skywalking-Java怎样收集trace数据
实例方法增强入口InstMethodsInter#intercept
- 在入口打上条件断点,准确分析Skywalking-Java怎样收集trace数据
- 分析InstMethodsInter#intercept整体结构发现,就是在原有方法前后做了拦截处理(重点)
InstMethodsInter前置处理
public void beforeMethod(EnhancedInstance objInst, Method method, Object[] allArguments, Class<?>[] argumentsTypes,
MethodInterceptResult result) throws Throwable {
// 构建操作名,例如发送get请求 --> GET:/demo/hello
String operationName = this.buildOperationName(method, httpServletRequest.getMethod(),
(EnhanceRequireObjectCache) objInst.getSkyWalkingDynamicField());
// 创建EntrySpan
AbstractSpan span = ContextManager.createEntrySpan(operationName, contextCarrier);
//为EntrySpan打url的tag
Tags.URL.set(span, httpServletRequest.getRequestURL().toString());
//为EntrySpan打http.method的tag
Tags.HTTP.METHOD.set(span, httpServletRequest.getMethod());
}
- 在执行业务方法前,
1.1 创建TracingContext,并初始化segment属性
1.2 创建EntrySpan,放入activeSpanStack有序队列中。 - 业务方法内执行http远程请求前,创建ExitSpan,放入activeSpanStack有序队列中。执行完后从activeSpanStack取出
- 整个业务方法执行完后,将trace信息存入buffer中
创建EntrySpan
- EntrySpan是入口Span,用于服务提供者(Service Provider) ,例如Tomcat
- Agent 会在Tomcat 定义的方法切面,创建 EntrySpan对象,也会在SpringMVC定义的方法切面,创建EntrySpan对象
2.1 Agent只会在第一个方法切面,生成 EntrySpan 对象,第二个方法切面,栈深度+ 1(通过这样的方式,保持一个 TraceSegment 有且仅有一个 EntrySpan 对象)
创建ExitSpan
- 在业务方法中发起http请求,执行httpClient.execute(httpGet)时会被HttpClientExecuteInterceptor拦截
public void beforeMethod(EnhancedInstance objInst, Method method, Object[] allArguments, Class<?>[] argumentsTypes,
MethodInterceptResult result) throws Throwable {
// operationName = /v1.0/menu/viewCount
String operationName = requestURI;
// 创建ExitSpan
AbstractSpan span = ContextManager.createExitSpan(operationName, contextCarrier, remotePeer);
}
- 创建ExitSpan,设置parentSpanId为activeSpanStack的最后一个span,并将ExitSpan放入activeSpanStack队列最后
InstMethodsInter后置处理
- 通过ContextManager#stopSpan停止链路追踪,最终在创建EntrySpan地方完全终止(例如tomcat)
1.1 如果stackDepth-1为0表示已经完成,将该span添加到TraceSegment中,并从队列StackBasedTracingSpan里弹出
// TracingContext
public boolean stopSpan(AbstractSpan span) {
AbstractSpan lastSpan = peek();
if (lastSpan == span) {
if (lastSpan instanceof AbstractTracingSpan) {
AbstractTracingSpan toFinishSpan = (AbstractTracingSpan) lastSpan;
// 判断EntrySpan的stackDepth-1后是否为0,是则将该span添加到TraceSegment中
if (toFinishSpan.finish(segment)) {
// 从队列里弹出 --> StackBasedTracingSpan#finish
pop();
}
} else {
pop();
}
} else {
throw new IllegalStateException("Stopping the unexpected span = " + span);
}
// 如果结束则trace线程记录在buffer中
finish();
return activeSpanStack.isEmpty();
}
- TraceSegmentServiceClient记录traceSegment,并存储在buffer中
ConsumerThread线程处理TraceSegment
- ConsumerThread线程拉去buffer数据,让TraceSegmentServiceClient消费处理
private boolean consume(List<T> consumeList) {
for (DataSource dataSource : dataSources) {
// 从buffer中读取数据存放在consumeList中
dataSource.obtain(consumeList);
}
if (!consumeList.isEmpty()) {
try {
// 消费者消费数据
consumer.consume(consumeList);
} catch (Throwable t) {
consumer.onError(consumeList, t);
} finally {
consumeList.clear();
}
return true;
}
consumer.nothingToConsume();
return false;
}
- 将TraceSegment转换为SegmentObject,通过grpc传输给Server端
束语
本篇文章通过业务方法内发起http调用示例来debug SkyWalking的链路是如何形成以及怎样传递Server端。后续会分析Server端如何处理SegmentObject