spring-cloud-sleuth+zipkin源码探究
1. spring-cloud-sleuth+zipkin源码探究
1.1. 前言
粗略看了下spring cloud sleuth core源码,发现内容真的有点多,它支持了很多类型的链路追踪,我就找其中一个比较有代表性的深入剖析下源码结构和内容
1.2. spring-cloud-sleuth-core源码解析
1.2.1. 结构

- 可以看到源码中支持的追踪类型有很多,支持async,hystrix,websocket,rxjava,Spring mvc,servlet,spring restTemplate,feign,zuul等等,这里我着重探讨spring web mvc的链路追踪
- 打开web包,找到TraceWebAutoConfiguration,这里配置了主要的初始化类

1.2.2. 过滤器注册
- 当启动初始化程序时,跟踪代码如下
@Bean
public FilterRegistrationBean traceWebFilter(TraceFilter traceFilter) {
FilterRegistrationBean filterRegistrationBean = new FilterRegistrationBean(
traceFilter);
filterRegistrationBean.setDispatcherTypes(ASYNC, ERROR, FORWARD, INCLUDE,
REQUEST);
filterRegistrationBean.setOrder(TraceFilter.ORDER);
return filterRegistrationBean;
}
@Bean
@ConditionalOnMissingBean
public TraceFilter traceFilter(BeanFactory beanFactory,
SkipPatternProvider skipPatternProvider) {
return new TraceFilter(beanFactory, skipPatternProvider.skipPattern());
}
- 初始化traceFilter,进行过滤器注册
1.2.3. 拦截器注册
- 然后看
TraceWebMvcConfigurer类,它会进行拦截器的注册
@Configuration
class TraceWebMvcConfigurer extends WebMvcConfigurerAdapter {
@Autowired BeanFactory beanFactory;
@Bean
public TraceHandlerInterceptor traceHandlerInterceptor(BeanFactory beanFactory) {
return new TraceHandlerInterceptor(beanFactory);
}
@Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(this.beanFactory.getBean(TraceHandlerInterceptor.class));
}
}
- 在
TraceHandlerInterceptor类中,preHandle,afterCompletion方法可以看出,这是对请求进行拦截进行span的包装
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response,
Object handler) throws Exception {
String spanName = spanName(handler);
boolean continueSpan = getRootSpanFromAttribute(request) != null;
Span span = continueSpan ? getRootSpanFromAttribute(request) : getTracer().createSpan(spanName);
if (log.isDebugEnabled()) {
log.debug("Handling span " + span);
}
addClassMethodTag(handler, span);
addClassNameTag(handler, span);
setSpanInAttribute(request, span);
if (!continueSpan) {
setNewSpanCreatedAttribute(request, span);
}
return true;
}
@Override
public void afterCompletion(HttpServletRequest request, HttpServletResponse response,
Object handler, Exception ex) throws Exception {
if (isErrorControllerRelated(request)) {
if (log.isDebugEnabled()) {
log.debug("Skipping closing of a span for error controller processing");
}
return;
}
Span span = getRootSpanFromAttribute(request);
if (ex != null) {
getErrorParser().parseErrorTags(span, ex);
}
if (getNewSpanFromAttribute(request) != null) {
if (log.isDebugEnabled()) {
log.debug("Closing span " + span);
}
Span newSpan = getNewSpanFromAttribute(request);
getTracer().continueSpan(newSpan);
getTracer().close(newSpan);
clearNewSpanCreatedAttribute(request);
}
}
1.2.4. zipkin端点提交
- 这里首先会初始化
HttpZipkinSpanReporter类,,用来进行span端点提交,然后初始化ZipkinSpanListenerspan的监听器,用来监听并调用端点提交,以上配置再下图位置

1.2.5. 调用http接口时,进入过滤器
- 首先进入
TraceFilter中的过滤方法doFilter,这里会做span的创建
private Span createSpan(HttpServletRequest request,
boolean skip, Span spanFromRequest, String name) {
if (spanFromRequest != null) {
if (log.isDebugEnabled()) {
log.debug("Span has already been created - continuing with the previous one");
}
return spanFromRequest;
}
//加入调用链路ZipkinHttpSpanExtractor,此链路在TraceHttpAutoConfiguration中配置实例化,调用链还没有时,返回为空,作为头节点
Span parent = spanExtractor().joinTrace(new HttpServletRequestTextMap(request));
if (parent != null) {
if (log.isDebugEnabled()) {
log.debug("Found a parent span " + parent + " in the request");
}
addRequestTagsForParentSpan(request, parent);
spanFromRequest = parent;
tracer().continueSpan(spanFromRequest);
if (parent.isRemote()) {
parent.logEvent(Span.SERVER_RECV);
}
request.setAttribute(TRACE_REQUEST_ATTR, spanFromRequest);
if (log.isDebugEnabled()) {
log.debug("Parent span is " + parent + "");
}
} else {
if (skip) {
spanFromRequest = tracer().createSpan(name, NeverSampler.INSTANCE);
}
else {
String header = request.getHeader(Span.SPAN_FLAGS);
if (Span.SPAN_SAMPLED.equals(header)) {
spanFromRequest = tracer().createSpan(name, new AlwaysSampler());
} else {
//创建span节点
spanFromRequest = tracer().createSpan(name);
}
}
spanFromRequest.logEvent(Span.SERVER_RECV);
request.setAttribute(TRACE_REQUEST_ATTR, spanFromRequest);
if (log.isDebugEnabled()) {
log.debug("No parent span present - creating a new span");
}
}
return spanFromRequest;
}
1.2.6. 进入拦截器
- 在
preHandle方法中,对span进行包装,然后把span放入请求头header中 - 最后再
DefaultTracer中进行span的关闭和spanReporter的提交
参考:https://blog.csdn.net/zhllansezhilian/article/details/83001870
spring-cloud-sleuth+zipkin源码探究的更多相关文章
- 分布式链路追踪之Spring Cloud Sleuth+Zipkin最全教程!
大家好,我是不才陈某~ 这是<Spring Cloud 进阶>第九篇文章,往期文章如下: 五十五张图告诉你微服务的灵魂摆渡者Nacos究竟有多强? openFeign夺命连环9问,这谁受得 ...
- Spring Cloud Alibaba学习笔记(23) - 调用链监控工具Spring Cloud Sleuth + Zipkin
随着业务发展,系统拆分导致系统调用链路愈发复杂一个前端请求可能最终需要调用很多次后端服务才能完成,当整个请求陷入性能瓶颈或不可用时,我们是无法得知该请求是由某个或某些后端服务引起的,这时就需要解决如何 ...
- Spring Cloud 学习 之 Spring Cloud Eureka(源码分析)
Spring Cloud 学习 之 Spring Cloud Eureka(源码分析) Spring Boot版本:2.1.4.RELEASE Spring Cloud版本:Greenwich.SR1 ...
- Spring Cloud Sleuth + Zipkin 链路监控
原文:https://blog.csdn.net/hubo_88/article/details/80878632 在微服务系统中,随着业务的发展,系统会变得越来越大,那么各个服务之间的调用关系也就变 ...
- Spring Cloud 微服务六:调用链跟踪Spring cloud sleuth +zipkin
前言:随着微服务系统的增加,服务之间的调用关系变得会非常复杂,这给运维以及排查问题带来了很大的麻烦,这时服务调用监控就显得非常重要了.spring cloud sleuth实现了对分布式服务的监控解决 ...
- Spring Cloud Sleuth+ZipKin+ELK服务链路追踪(七)
序言 sleuth是spring cloud的分布式跟踪工具,主要记录链路调用数据,本身只支持内存存储,在业务量大的场景下,为拉提升系统性能也可通过http传输数据,也可换做rabbit或者kafka ...
- Spring Cloud Sleuth Zipkin - (2)
在上一节<spring-cloud-sleuth+zipkin追踪服务实现(一)>中,我们使用zipkin-server.provider.consumer三个程序实现了使用http方式进 ...
- Spring Cloud Sleuth Zipkin - (1)
最近在学习spring cloud构建微服务,很多大牛都提供很多入门的例子帮助我们学习,对于我们这种英语不好的码农来说,效率着实提高不少.这两天学习到追踪微服务rest服务调用链路的问题,接触到zip ...
- Spring Cloud Netflix Eureka源码导读与原理分析
Spring Cloud Netflix技术栈中,Eureka作为服务注册中心对整个微服务架构起着最核心的整合作用,因此对Eureka还是有很大的必要进行深入研究. 本文主要分为四部分,一是对项目构建 ...
随机推荐
- IntelliJ IDEA 如何设置类头注释和方法注释
从VS转过来的,ide的差距很大的,所以...特意折腾了很久,结果还是没有VS的 '///' 好用 一.类头注释 打开file -> setting -> Editor -> Fil ...
- 【Linux】日志分析工具grep sed sort
遇到一个问题,在查询日志时发现,服务器上打印的文件有很多个,每个都存储了一部分日志, 需要将日志按照时间排序,并显示所有日志. 原命令: grep -h searchContent */*log 搜 ...
- thinkphp5简单使用redis缓存
<?php namespace app\index\controller; use think\Controller; use think\Cache\Driver\Redis; class I ...
- spring boot+mybatis+quartz项目的搭建完整版
1. 利用spring boot提供的工具(http://start.spring.io/)自动生成一个标准的spring boot项目架构 2. 因为这里我们是搭建spring boot+mybat ...
- ELK配置
安装logstash docker pull logstash docker run -it --rm logstash -e 'input { stdin { } } output { stdout ...
- Semantic difference between object expressions and declarations
object expressions are executed (and initialized) immediately, where they are used; object declarati ...
- 对接携程供应商php加密解密类
php加密解密类 <?php class Aes{ private $key = '6b4d63211b4ba869'; private $iv = 'dbbf079b95004f65'; pu ...
- iperf3 使用
iperf 分client 和 server server: iperf3 -s -p 5000 -i 1 client: iperf3 -c 192.168.31.11 -p 5000 Serv ...
- 20190312_浅谈go&java差异(一)
多线程 java java中对于大量的比较耗时的任务多采用多线程对方式对任务进行处理,同时由于进程和线程 本身是通过宿主机OS进行管理的,当在cpu核数较少或线程分配不当 会导致多线程的效果不佳的事常 ...
- Python函数zip-map
>>> list(zip([1,3,5,7],[2,4,6,8]))[(1, 2), (3, 4), (5, 6), (7, 8)] 把两个列表合成一个 也可以用map函数: > ...