springboot(五)读写分离,多个读库,Druid监控--待整理
在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监控--待整理的更多相关文章
- SpringBoot Mybatis 读写分离配置(山东数漫江湖)
为什么需要读写分离 当项目越来越大和并发越来大的情况下,单个数据库服务器的压力肯定也是越来越大,最终演变成数据库成为性能的瓶颈,而且当数据越来越多时,查询也更加耗费时间,当然数据库数据过大时,可以采用 ...
- springboot实现读写分离(基于Mybatis,mysql)
近日工作任务较轻,有空学习学习技术,遂来研究如果实现读写分离.这里用博客记录下过程,一方面可备日后查看,同时也能分享给大家(网上的资料真的大都是抄来抄去,,还不带格式的,看的真心难受). 完整代码:h ...
- SpringBoot数据库读写分离之基于Docker构建主从数据库同步实例
看了好久的SpringBoot结合MyBatista实现读写,但是一直没有勇气实现他,今天终于接触到了读写分离的东西,读写分离就是讲读操作执行在Slave数据库(从数据库),写操作在Master数据库 ...
- Mysql8.0主从复制搭建,shardingsphere+springboot+mybatis读写分离
1.安装mysql8.0 首先需要在192.167.3.171上安装JDK. 下载mysql安装包,https://dev.mysql.com/downloads/,找到以下页面下载. 下载后放到li ...
- 搭建基于springboot轻量级读写分离开发框架
何为读写分离 读写分离是指对资源的修改和读取进行分离,能解决很多数据库瓶颈,以及代码混乱难以维护等相关的问题,使系统有更好的扩展性,维护性和可用性. 一般会分三个步骤来实现: 一. 主从数据库搭建 信 ...
- 快速掌握mongoDB(五)——读写分离的副本集实现和Sharing介绍
1 mongoDB副本集 1 副本集简介 前边我们介绍都是单机MongoDB的使用,在实际开发中很少会用单机MongoDB,因为使用单机会有数据丢失的风险,同时单台服务器无法做到高可用性(即当服务器宕 ...
- Sharding+SpringBoot+Mybatis 读写分离
基于Sharding JDBC的读写分离 1.引入pom.xml <dependencies> <!-- mybatis --> <dependency> < ...
- SpringBoot 整合 MyCat 实现读写分离
MyCat一个彻底开源的,面向企业应用开发的大数据库集群.基于阿里开源的Cobar产品而研发.能满足数据库数据大量存储:提高了查询性能.文章介绍如何实现MyCat连接MySQL实现主从分离,并集成Sp ...
- 重新学习Mysql数据13:Mysql主从复制,读写分离,分表分库策略与实践
一.MySQL扩展具体的实现方式 随着业务规模的不断扩大,需要选择合适的方案去应对数据规模的增长,以应对逐渐增长的访问压力和数据量. 关于数据库的扩展主要包括:业务拆分.主从复制.读写分离.数据库分库 ...
随机推荐
- mysql设置更改root密码、mysql服务器的连接、mysql常用命令
1.设置更改root密码 查看mysql 启动与否,若没启动就运行:/usr/local/mysql56/bin/mysqlps aux |grep mysql 或 netstat -tulnp ...
- java-方法重写的注意事项
1.父类中私有方法不能被重写.因为父类的私有方法子类根本就无法继承. 2.子类重写父类方法时,访问权限不能更低.最好就一致. 3.父类静态方法,子类也必须通过静态方法进行重写.其实这个算不上方法重写, ...
- C++学习(十九)(C语言部分)之 指针3
复习1.一级指针 int*p 指向int的指针 赋值 int x; p=&x;// *p=2; 指针指向的谁 解引用之后就是谁2.内存四区 堆区 需要自己手动申请内存 自己释放 (malloc ...
- 【java高级编程】jdk自带事件模型编程接口
事件类 java.util.EventObject java.beans.PropertyChangeEvent 事件监听接口 java.util.EventListener java.beans.P ...
- Java基础五(方法)
今日内容介绍1.方法基础知识2.方法高级内容3.方法案例 ###01方法的概述 * A: 为什么要有方法 * 提高代码的复用性 * B: 什么是方法 * 完成特定功能的代码块. ###02方法的定义格 ...
- 刚开始学java和刚去工作的时候,1.path路径 2.classpath路径 还有JAVA_HOME相当于/dgs这个路径
把里面bin文件夹下面的可执行文件都配置到path路径下了,以后只要在Dos窗口输入命令就可以运行 无论是在dos窗口下还是在eclispe中只需要配置这个path变量,不需要配置classpath ...
- linux下PHP手动添加扩展库
1.进入php源程序目录中的ext目录中,这里存放着各个扩展模块的源代码,选择你需要的模块,比如curl模块: cd curl 执行phpize生成编译文件,phpize在PHP安装目录的bin目录下 ...
- oracle-null和默认值
Oracle的默认值处理要当心,如果应用中使用的是ORM工具,则必须要考虑对于字段为Null的处理,必要时在ORM工具中将Null转换为default或插入时去掉值为Null的字段. 可以将下面的系统 ...
- apt-get update 与 apt-get upgrade 的区别
总而言之,update是更新软件列表,upgrade是更新软件:所以,这两命令都是一块用,update后再upgrade. update 是更新 /etc/apt/sources.list 和 /et ...
- uglifyjs-webpack-plugin 插件,drop_console 默认为 false(不清除 console 语句),drop_debugger 默认为 true(清除 debugger 语句)
uglifyjs-webpack-plugin 插件,drop_console 默认为 false(不清除console语句),drop_debugger 默认为 true(清除 debugger 语 ...