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

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

这样,TraceEnvironmentPostProcessor被配置在了ioc容器初始化之前。spring-cloud-sleuth-core包的org.springframework.cloud.sleuth.annotation.TraceEnvironmentPostProcessor.java

public class TraceEnvironmentPostProcessor implements EnvironmentPostProcessor {

    private static final String PROPERTY_SOURCE_NAME = "defaultProperties";
private static final String SPRING_AOP_PROXY_TARGET_CLASS = "spring.aop.proxyTargetClass"; @Override
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 true
if (!environment.containsProperty(SPRING_AOP_PROXY_TARGET_CLASS)) {
map.put(SPRING_AOP_PROXY_TARGET_CLASS, "true");
}
addOrReplace(environment.getPropertySources(), map);
}
//...
}

在TraceEnvironmentPostProcessor的postProcessEnvironment()方法里保证了两件事情:

1、设置了spring.aop.proxyTargetClass参数为true保证了cglib代理的开启,并加入了日志的追踪打印的模板。
2、而后在配置类TraceAutoConfiguration中生成了Tracer,默认实现为DefaultTraces,作用为正式创建一个工作单元span。

紧随其后的配置类为SleuthAnnotationAutoConfiguration,见spring-cloud-sleuth-core包的org.springframework.cloud.sleuth.annotation.SleuthAnnotationAutoConfiguration.java

@Configuration
@ConditionalOnBean(Tracer.class)
@ConditionalOnProperty(name = "spring.sleuth.annotation.enabled", matchIfMissing = true)
@AutoConfigureAfter(TraceAutoConfiguration.class)
@EnableConfigurationProperties(SleuthAnnotationProperties.class)
public class SleuthAnnotationAutoConfiguration { @Bean
@ConditionalOnMissingBean
SpanCreator spanCreator(Tracer tracer) {
return new DefaultSpanCreator(tracer);
}
//...
    @Bean
    SleuthAdvisorConfig sleuthAdvisorConfig() {
        return new SleuthAdvisorConfig();
    }
}

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

class SleuthAdvisorConfig  extends AbstractPointcutAdvisor implements BeanFactoryAware {

    private Advice advice;

    private Pointcut pointcut;

    private BeanFactory beanFactory;

    @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();
} /**
* Checks if a class or a method is is annotated with Sleuth related annotations
*/
private final class AnnotationClassOrMethodOrArgsPointcut extends
DynamicMethodMatcherPointcut { @Override
public 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);
} @Override
public boolean matches(Class<?> clazz) {
return super.matches(clazz) || this.methodResolver.hasAnnotatedMethods(clazz);
} }

显而易见,切面的匹配通过目标类是否满足使用了NewSpan或者ContinueSpan注解。
切面的增强则通过buildAdvice来构造Interceptor来通过代理使用invoke()方法来完成生成span的目的。

    private Advice buildAdvice() {
return new SleuthInterceptor();
}
class SleuthInterceptor  implements IntroductionInterceptor, BeanFactoryAware  {

    private static final Log logger = LogFactory.getLog(MethodHandles.lookup().lookupClass());
private static final String CLASS_KEY = "class";
private static final String METHOD_KEY = "method"; private BeanFactory beanFactory;
private SpanCreator spanCreator;
private Tracer tracer;
private SpanTagAnnotationHandler spanTagAnnotationHandler;
private ErrorParser errorParser; @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()方法中。

class SpanContextHolder {

    private static final Log log = org.apache.commons.logging.LogFactory
.getLog(SpanContextHolder.class);
private static final ThreadLocal<SpanContext> CURRENT_SPAN = new NamedThreadLocal<>(
"Trace Context"); /**
* Get the current span out of the thread context
*/
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。

springboot(五)读写分离,多个读库,Druid监控--待整理的更多相关文章

  1. SpringBoot Mybatis 读写分离配置(山东数漫江湖)

    为什么需要读写分离 当项目越来越大和并发越来大的情况下,单个数据库服务器的压力肯定也是越来越大,最终演变成数据库成为性能的瓶颈,而且当数据越来越多时,查询也更加耗费时间,当然数据库数据过大时,可以采用 ...

  2. springboot实现读写分离(基于Mybatis,mysql)

    近日工作任务较轻,有空学习学习技术,遂来研究如果实现读写分离.这里用博客记录下过程,一方面可备日后查看,同时也能分享给大家(网上的资料真的大都是抄来抄去,,还不带格式的,看的真心难受). 完整代码:h ...

  3. SpringBoot数据库读写分离之基于Docker构建主从数据库同步实例

    看了好久的SpringBoot结合MyBatista实现读写,但是一直没有勇气实现他,今天终于接触到了读写分离的东西,读写分离就是讲读操作执行在Slave数据库(从数据库),写操作在Master数据库 ...

  4. Mysql8.0主从复制搭建,shardingsphere+springboot+mybatis读写分离

    1.安装mysql8.0 首先需要在192.167.3.171上安装JDK. 下载mysql安装包,https://dev.mysql.com/downloads/,找到以下页面下载. 下载后放到li ...

  5. 搭建基于springboot轻量级读写分离开发框架

    何为读写分离 读写分离是指对资源的修改和读取进行分离,能解决很多数据库瓶颈,以及代码混乱难以维护等相关的问题,使系统有更好的扩展性,维护性和可用性. 一般会分三个步骤来实现: 一. 主从数据库搭建 信 ...

  6. 快速掌握mongoDB(五)——读写分离的副本集实现和Sharing介绍

    1 mongoDB副本集 1 副本集简介 前边我们介绍都是单机MongoDB的使用,在实际开发中很少会用单机MongoDB,因为使用单机会有数据丢失的风险,同时单台服务器无法做到高可用性(即当服务器宕 ...

  7. Sharding+SpringBoot+Mybatis 读写分离

    基于Sharding JDBC的读写分离 1.引入pom.xml <dependencies> <!-- mybatis --> <dependency> < ...

  8. SpringBoot 整合 MyCat 实现读写分离

    MyCat一个彻底开源的,面向企业应用开发的大数据库集群.基于阿里开源的Cobar产品而研发.能满足数据库数据大量存储:提高了查询性能.文章介绍如何实现MyCat连接MySQL实现主从分离,并集成Sp ...

  9. 重新学习Mysql数据13:Mysql主从复制,读写分离,分表分库策略与实践

    一.MySQL扩展具体的实现方式 随着业务规模的不断扩大,需要选择合适的方案去应对数据规模的增长,以应对逐渐增长的访问压力和数据量. 关于数据库的扩展主要包括:业务拆分.主从复制.读写分离.数据库分库 ...

随机推荐

  1. HPU第四次积分赛-L:A Winged Steed(完全背包)

    A Winged Steed 描述 有n种千里马,每一种都有若干匹,第i种马的颜值ai​,价格di​.现有m个牧马人要去挑选千里马,每一位牧马人对马的颜值都有要求:{所选马的颜值总和} ⩾Ai​.现在 ...

  2. 2017.4.7 Sprng MVC工作流程描述图

    图一: 图二: Spring工作流程描述         1. 用户向服务器发送请求,请求被Spring 前端控制Servelt DispatcherServlet捕获:       2. Dispa ...

  3. shutil模块(高级的文件、文件夹、压缩包处理模块)

    shutil 模块 高级的 文件.文件夹.压缩包 处理模块 shutil.copyfileobj(fsrc, fdst[, length])将文件内容拷贝到另一个文件中 import shutil s ...

  4. javascrpit的理解

    1.什么是Javascrpt? 轻量级 .编程语言 HTML+css -->设计 参数的默认值设置 函数的闭包: 浏览器加载整个页面的过程 浏览器:多线程 1.js引擎 2.UI渲染 3.事件线 ...

  5. Go Example--通道非阻塞

    package main import ( "fmt" ) func main() { messages := make(chan string) signals := make( ...

  6. Java解析property文件(和静哥说的,SQL执行限定时间写在xml中,增加扩展,在不改源代码基础上)

    在Java项目中一些配置参数保存在Property文件中,这样能保证不修改原代码直接修改Property文件. 简单的很,就是在java文件中读取外界的properyt配置文件 PropertyPar ...

  7. java中的Object类和其clone()

    1.Object是所有类的父类,任何类都默认继承Object,即直接或间接的继承java.lang.Object类.由于所有的类都继承在Object类,因此省略了extends Object关键字. ...

  8. Java中的继承抽象类和接口

    一.总结 1.使用extends关键字继承,eg: class Student extends Persion { ...}; 2.Java编程规范中类的首字母大写,方法的首字母小写单词首字母代谢,e ...

  9. Kafka Consumer API样例

    Kafka Consumer API样例 1. 自动确认Offset 说明参照:http://blog.csdn.net/xianzhen376/article/details/51167333 Pr ...

  10. vsphere和vmware快照的不足之处

    当快照创建时虚拟机执行一个读操作,hypervisor会检查快照VMDK,查看是否有被读取的区块存在.如果有,则从快照中为虚拟机提供这个区块,如果没有,虚拟机还需要去读取基础VMDK.如果只有一个快照 ...