spring cloud sleuth在spring中创建span
在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相关推荐
- 还搭不出来服务链路追踪Spring Cloud Sleuth?
Spring Cloud Sleuth 作为Spring Cloud 的一个组件,其主要作用是解决分布式系统当中提供服务链路追踪的. 为什么要使用链路追踪? 在微服务系统中,一个来自用户的请求,请求先 ...
- Spring Cloud Sleuth介绍
背景 微服务架构下,一个请求可能会经过多个服务才会得到结果,如果在这个过程中出现了异常,就很难去定位问题.所以,必须要实现一个分布式链路跟踪的功能,直观的展示出完整的调用过程. 什么是Spring C ...
- Spring Cloud Sleuth 链路追踪
文章目录 1 概述 2 基本使用 3 异步任务 4 定时任务 5 Zipkin 5.1 准备工作 5.2 实践 学习在 Spring Cloud 中使用 Sleuth 实现链路追踪,包括基本使用.异步 ...
- Spring微服务实战第9章 使用Spring Cloud Sleuth和Zipkin进行分布式跟踪
文章目录 第9章 使用Spring Cloud Sleuth和Zipkin进行分布式跟踪 9.1 Spring Cloud Sleuth与关联ID 9.1.1 将Spring Cloud Sleuth ...
- spring cloud学习进阶篇:Spring Cloud Sleuth + Zipkin 实现分布式跟踪解决方案
2019独角兽企业重金招聘Python工程师标准>>> 简述 使用 spring cloud 用到最多的是各种rest服务调用,Twitter的Zipkin 是一种实现分布式跟踪解决 ...
- Spring Cloud Sleuth
1. 介绍 在微服务项目中,一个请求到达后端后,在处理业务的过程中,可能还会调用其他多个微服务来实现功能,在这个过程中,整个请求的链路追踪就非常重要,我们需要知道每个节点的调用信息.通过这些信息我们能 ...
- Spring Cloud Sleuth + zipkin + kafka
2.2.5.RELEASE 1 说明 Spring Cloud Sleuth 是Spring Cloud的分布式链路追踪实现. 1.1 术语 Spring Cloud Sleuth采用的是Google ...
- 当Spring Cloud Alibaba Sentinel碰上Spring Cloud Sleuth会擦出怎样的火花
前言 今年主要会做一个比较完整的微服务项目开源出来.目前已经开始了,刚兴趣的先Star一个吧. 项目:https://github.com/yinjihuan/kitty-cloud 基础框架:htt ...
- 五分钟学会 Spring Cloud Sleuth:分布式请求链路跟踪(小白必看,一看就会教程)
Spring Cloud Sleuth:分布式请求链路跟踪 Spring Cloud Sleuth 简介 给服务添加请求链路跟踪 整合Zipkin获取及分析日志 使用Elasticsearch存储跟踪 ...
最新文章
- perl 发送邮件脚本
- Linux添加vlan不通,如何处理Linux虚拟机跨VLAN ping不通问题
- Java 8中的java.util.Random
- shiro学习(18):使用注解实现权限认证和后台管理三
- clocks_per_sec 时间不正确_测血糖的正确做法:这4步一定别搞错了
- C#LeetCode刷题之#705-设计哈希集合​​​​​​​(Design HashSet)
- 华为Mate40 Pro渲染图曝光:刘海和瀑布屏一起消失
- python +selenium +chrome/firefox 环境配置
- 深度学习下,中文分词是否还有必要?——ACL 2019论文阅读笔记
- Oracle递归查询所有树结构,并确定其中的一条分支
- bad interpreter: No such file or directory
- 游竹林寺不得,谈封山收费
- 北京理工大学c语言作业三做一年级算术题,北京理工大学C语言编程题_答案
- 2005年linux手机系统,2005年智能手机点评之其他操作系统篇
- 线程的启动暂停和终止
- linux磁盘检测工具
- 百度竞价推广是什么?信息流推广以及网盟推广是什么?
- DXC IPS 440T 的Console连接配置与账户重置
- c#和python_Python3 与 C# 基础语法对比(就当Python和C#基础的普及吧)
- 机器“读懂”放射学报告
热门文章
- Django提交表单报错:CSRF token missing or incorrect.
- linux history 看更多历史记录_每周开源点评:定义云原生、拓展生态系统,以及更多的行业趋势 | Linux 中国...
- ES6——generator与yield
- linux 减小根分区大小_减小linux下根分区
- C语言CRC32 逆向算法源码
- Android小知识-了解下Android系统的显示原理
- Kai - Golang实现的目标检测云服务
- CSS cursor 和 opacity 属性
- Android内存泄漏的各种原因详解
- java-什么是实例初始化块?