在spring-cloud-sleuth的META-INF里的spring.factories里设置了一下:

org.springframework.boot.env.EnvironmentPostProcessor=\
org.springframework.cloud.sleuth.autoconfig.TraceEnvironmentPostProcessor

这样,TraceEnvironmentPostProcessor被配置在了ioc容器初始化之前。

public void postProcessEnvironment(ConfigurableEnvironment environment,SpringApplication application) {Map<String, Object> map = new HashMap<String, Object>();// This doesn't work with all logging systems but it's a useful default so you see// traces in logs without having to configure it.if (Boolean.parseBoolean(environment.getProperty("spring.sleuth.enabled", "true"))) {map.put("logging.pattern.level","%5p [${spring.zipkin.service.name:${spring.application.name:-}},%X{X-B3-TraceId:-},%X{X-B3-SpanId:-},%X{X-Span-Export:-}]");}// TODO: Remove this in 2.0.x. For compatibility we always set to trueif (!environment.containsProperty(SPRING_AOP_PROXY_TARGET_CLASS)) {map.put(SPRING_AOP_PROXY_TARGET_CLASS, "true");}addOrReplace(environment.getPropertySources(), map);
}

在TraceEnvironmentPostProcessor的postProcessEnvironment()方法里保证了两件事情,设置了spring.aop.proxyTargetClass参数为true保证了cglib代理的开启,并加入了日志的追踪打印的模板。

而后在配置类TraceAutoConfiguration中生成了Tracer,默认实现为DefaultTraces,作用为正式创建一个工作单元span。

紧随其后的配置类为SleuthAnnotationAutoConfiguration,此处根据之前生成的Tracer进一步创建了其包装类SpanCreator,并最重要的是生成了代理的配置类SleuthAdvisorConfig。

@PostConstruct
public void init() {this.pointcut = buildPointcut();this.advice = buildAdvice();if (this.advice instanceof BeanFactoryAware) {((BeanFactoryAware) this.advice).setBeanFactory(this.beanFactory);}
}

其初始化方法中,完成了切面与切面增强的创建。

其buildPointcut()方法:

private Pointcut buildPointcut() {return new AnnotationClassOrMethodOrArgsPointcut();
}
private final class AnnotationClassOrMethodOrArgsPointcut extendsDynamicMethodMatcherPointcut {@Overridepublic boolean matches(Method method, Class<?> targetClass, Object... args) {return getClassFilter().matches(targetClass);}@Override public ClassFilter getClassFilter() {return new ClassFilter() {@Override public boolean matches(Class<?> clazz) {return new AnnotationClassOrMethodFilter(NewSpan.class).matches(clazz) ||new AnnotationClassOrMethodFilter(ContinueSpan.class).matches(clazz);}};}}private final class AnnotationClassOrMethodFilter extends AnnotationClassFilter {private final AnnotationMethodsResolver methodResolver;AnnotationClassOrMethodFilter(Class<? extends Annotation> annotationType) {super(annotationType, true);this.methodResolver = new AnnotationMethodsResolver(annotationType);}@Overridepublic boolean matches(Class<?> clazz) {return super.matches(clazz) || this.methodResolver.hasAnnotatedMethods(clazz);}}

显而易见,切面的匹配通过目标类是否满足使用了NewSpan或者ContinueSpan注解。

切面的增强则通过buildAdvice来构造Interceptor来通过代理使用invoke()方法来完成生成span的目的。

private Advice buildAdvice() {return new SleuthInterceptor();
}@Override
public Object invoke(MethodInvocation invocation) throws Throwable {Method method = invocation.getMethod();if (method == null) {return invocation.proceed();}Method mostSpecificMethod = AopUtils.getMostSpecificMethod(method, invocation.getThis().getClass());NewSpan newSpan = SleuthAnnotationUtils.findAnnotation(mostSpecificMethod, NewSpan.class);ContinueSpan continueSpan = SleuthAnnotationUtils.findAnnotation(mostSpecificMethod, ContinueSpan.class);if (newSpan == null && continueSpan == null) {return invocation.proceed();}Span span = tracer().getCurrentSpan();String log = log(continueSpan);boolean hasLog = StringUtils.hasText(log);try {if (newSpan != null) {span = spanCreator().createSpan(invocation, newSpan);}if (hasLog) {logEvent(span, log + ".before");}spanTagAnnotationHandler().addAnnotatedParameters(invocation);addTags(invocation, span);return invocation.proceed();} catch (Exception e) {if (logger.isDebugEnabled()) {logger.debug("Exception occurred while trying to continue the pointcut", e);}if (hasLog) {logEvent(span, log + ".afterFailure");}errorParser().parseErrorTags(tracer().getCurrentSpan(), e);throw e;} finally {if (span != null) {if (hasLog) {logEvent(span, log + ".after");}if (newSpan != null) {tracer().close(span);}}}
}

当通过代理走到此处的invoke()方法说明此时涉及到了与别的服务的调用,需要生成新的spanId,那么就在这里newSpan注解生成新的spanId,如果该方法实现了ContinueSpan注解,那么就在现有的spanId。

如果采用newSpan注解,那么这里需要通过之前的在配置类中生成的tracer的getCurrentSpan()方法获取当前的span。

具体的实现在SpanContextHolder的getCurrentSpan()方法中。

static Span getCurrentSpan() {return isTracing() ? CURRENT_SPAN.get().span : null;
}

通过ThreadLoacl来获得当前的span,也就是说,当新的trace请求到来时,可以通过ThreadLoacl来存储。

紧接着通过spanCreator的createSpan()方法来证实获得新的span。

@Override public Span createSpan(MethodInvocation pjp, NewSpan newSpanAnnotation) {String name = StringUtils.isEmpty(newSpanAnnotation.name()) ?pjp.getMethod().getName() : newSpanAnnotation.name();String changedName = SpanNameUtil.toLowerHyphen(name);if (log.isDebugEnabled()) {log.debug("For the class [" + pjp.getThis().getClass() + "] method "+ "[" + pjp.getMethod().getName() + "] will name the span [" + changedName + "]");}return createSpan(changedName);
}private Span createSpan(String name) {if (this.tracer.isTracing()) {return this.tracer.createSpan(name, this.tracer.getCurrentSpan());}return this.tracer.createSpan(name);
}

Span的名字在注解中的名字和方法名中有限选择前者,而后根据通过tracer的createSpan()来获得span。

@Override
public Span createSpan(String name, Span parent) {if (parent == null) {return createSpan(name);}return continueSpan(createChild(parent, name));
}

如果此时没有任何span存在,那么直接通过createSpan().

@Override
public Span createSpan(String name) {return this.createSpan(name, this.defaultSampler);
}@Override
public Span createSpan(String name, Sampler sampler) {String shortenedName = SpanNameUtil.shorten(name);Span span;if (isTracing()) {span = createChild(getCurrentSpan(), shortenedName);}else {long id = createId();span = Span.builder().name(shortenedName).traceIdHigh(this.traceId128 ? createTraceIdHigh() : 0L).traceId(id).spanId(id).build();if (sampler == null) {sampler = this.defaultSampler;}span = sampledSpan(span, sampler);this.spanLogger.logStartedSpan(null, span);}return continueSpan(span);
}

这里可以看到spanId的构造,如果当时是首次构建spanId,那么首先会创建一个traceId,作为本次跟踪流 的id。并与第一次的spanID相同。

但是,此时若是已经存在span,也就是说这并不是第一次,那么就没有必要将traceId设为该次创建的spanId,而是在createChild()方法中,记录当前的traceId为原来收到的traceId,并将收到的spanId作为parentId,并将savedSpan指向原来的span,重新生成一个spanId,并将新的span作为当前的span。

在完成了span的创建后,则会经过sample的判断,此次是否要使用span记录,可以根据配置修改sample的类型,如果采用了百分比类型的,那么可能不会记录下来,完全复制一份span,但是把其exportable属性改为false。

spring cloud sleuth在spring中创建span相关推荐

  1. 还搭不出来服务链路追踪Spring Cloud Sleuth?

    Spring Cloud Sleuth 作为Spring Cloud 的一个组件,其主要作用是解决分布式系统当中提供服务链路追踪的. 为什么要使用链路追踪? 在微服务系统中,一个来自用户的请求,请求先 ...

  2. Spring Cloud Sleuth介绍

    背景 微服务架构下,一个请求可能会经过多个服务才会得到结果,如果在这个过程中出现了异常,就很难去定位问题.所以,必须要实现一个分布式链路跟踪的功能,直观的展示出完整的调用过程. 什么是Spring C ...

  3. Spring Cloud Sleuth 链路追踪

    文章目录 1 概述 2 基本使用 3 异步任务 4 定时任务 5 Zipkin 5.1 准备工作 5.2 实践 学习在 Spring Cloud 中使用 Sleuth 实现链路追踪,包括基本使用.异步 ...

  4. Spring微服务实战第9章 使用Spring Cloud Sleuth和Zipkin进行分布式跟踪

    文章目录 第9章 使用Spring Cloud Sleuth和Zipkin进行分布式跟踪 9.1 Spring Cloud Sleuth与关联ID 9.1.1 将Spring Cloud Sleuth ...

  5. spring cloud学习进阶篇:Spring Cloud Sleuth + Zipkin 实现分布式跟踪解决方案

    2019独角兽企业重金招聘Python工程师标准>>> 简述 使用 spring cloud 用到最多的是各种rest服务调用,Twitter的Zipkin 是一种实现分布式跟踪解决 ...

  6. Spring Cloud Sleuth

    1. 介绍 在微服务项目中,一个请求到达后端后,在处理业务的过程中,可能还会调用其他多个微服务来实现功能,在这个过程中,整个请求的链路追踪就非常重要,我们需要知道每个节点的调用信息.通过这些信息我们能 ...

  7. Spring Cloud Sleuth + zipkin + kafka

    2.2.5.RELEASE 1 说明 Spring Cloud Sleuth 是Spring Cloud的分布式链路追踪实现. 1.1 术语 Spring Cloud Sleuth采用的是Google ...

  8. 当Spring Cloud Alibaba Sentinel碰上Spring Cloud Sleuth会擦出怎样的火花

    前言 今年主要会做一个比较完整的微服务项目开源出来.目前已经开始了,刚兴趣的先Star一个吧. 项目:https://github.com/yinjihuan/kitty-cloud 基础框架:htt ...

  9. 五分钟学会 Spring Cloud Sleuth:分布式请求链路跟踪(小白必看,一看就会教程)

    Spring Cloud Sleuth:分布式请求链路跟踪 Spring Cloud Sleuth 简介 给服务添加请求链路跟踪 整合Zipkin获取及分析日志 使用Elasticsearch存储跟踪 ...

最新文章

  1. perl 发送邮件脚本
  2. Linux添加vlan不通,如何处理Linux虚拟机跨VLAN ping不通问题
  3. Java 8中的java.util.Random
  4. shiro学习(18):使用注解实现权限认证和后台管理三
  5. clocks_per_sec 时间不正确_测血糖的正确做法:这4步一定别搞错了
  6. C#LeetCode刷题之#705-设计哈希集合​​​​​​​(Design HashSet)
  7. 华为Mate40 Pro渲染图曝光:刘海和瀑布屏一起消失
  8. python +selenium +chrome/firefox 环境配置
  9. 深度学习下,中文分词是否还有必要?——ACL 2019论文阅读笔记
  10. Oracle递归查询所有树结构,并确定其中的一条分支
  11. bad interpreter: No such file or directory
  12. 游竹林寺不得,谈封山收费
  13. 北京理工大学c语言作业三做一年级算术题,北京理工大学C语言编程题_答案
  14. 2005年linux手机系统,2005年智能手机点评之其他操作系统篇
  15. 线程的启动暂停和终止
  16. linux磁盘检测工具
  17. 百度竞价推广是什么?信息流推广以及网盟推广是什么?
  18. DXC IPS 440T 的Console连接配置与账户重置
  19. c#和python_Python3 与 C# 基础语法对比(就当Python和C#基础的普及吧)
  20. 机器“读懂”放射学报告

热门文章

  1. Django提交表单报错:CSRF token missing or incorrect.
  2. linux history 看更多历史记录_每周开源点评:定义云原生、拓展生态系统,以及更多的行业趋势 | Linux 中国...
  3. ES6——generator与yield
  4. linux 减小根分区大小_减小linux下根分区
  5. C语言CRC32 逆向算法源码
  6. Android小知识-了解下Android系统的显示原理
  7. Kai - Golang实现的目标检测云服务
  8. CSS cursor 和 opacity 属性
  9. Android内存泄漏的各种原因详解
  10. java-什么是实例初始化块?