前言

  在上篇文章《SkyWalking系列学习之环境搭建以及分析如何类增强》中已经知道业务系统通过-javaagent引入代理,通过ByteBuddy对指定的类做增强处理。下面介绍Skywalking-Java怎样收集trace数据

实例方法增强入口InstMethodsInter#intercept

  1. 在入口打上条件断点,准确分析Skywalking-Java怎样收集trace数据
  2. 分析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.1 创建TracingContext,并初始化segment属性
    1.2 创建EntrySpan,放入activeSpanStack有序队列中。
  2. 业务方法内执行http远程请求前,创建ExitSpan,放入activeSpanStack有序队列中。执行完后从activeSpanStack取出
  3. 整个业务方法执行完后,将trace信息存入buffer中

创建EntrySpan

  1. EntrySpan是入口Span,用于服务提供者(Service Provider) ,例如Tomcat
  2. Agent 会在Tomcat 定义的方法切面,创建 EntrySpan对象,也会在SpringMVC定义的方法切面,创建EntrySpan对象
    2.1 Agent只会在第一个方法切面,生成 EntrySpan 对象,第二个方法切面,栈深度+ 1(通过这样的方式,保持一个 TraceSegment 有且仅有一个 EntrySpan 对象)

创建ExitSpan

  1. 在业务方法中发起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);
    
 }
  1. 创建ExitSpan,设置parentSpanId为activeSpanStack的最后一个span,并将ExitSpan放入activeSpanStack队列最后

InstMethodsInter后置处理

  1. 通过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();
}
  1. TraceSegmentServiceClient记录traceSegment,并存储在buffer中

ConsumerThread线程处理TraceSegment

  1. 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;
}
  1. 将TraceSegment转换为SegmentObject,通过grpc传输给Server端

束语

  本篇文章通过业务方法内发起http调用示例来debug SkyWalking的链路是如何形成以及怎样传递Server端。后续会分析Server端如何处理SegmentObject