微服务请求跟踪链路
概述
为什么需要微服务请求跟踪链路?
项目庞大起来之后,可能页面的一个500错误,牵扯到几十个微服务,其中一个报错可能导致出错,引入sleuth之后可以快速定位问题.
Sleuth是什么?
是一套完整的微服务跟踪解决方案,内部兼容了 zipkin
作为显示框架
Zipkin 使用
- Github上下载源码并进行编译,网上提供的版本2.1.x版本 不支持es7.x
- Zipkin 下载地址.
- 浏览器查看
http://localhost:9411/zipkin/
基本概念
- span
工作的最小单元,一个RPC请求代表一个新的span.span由一个 64位的唯一id 和 一个 64位的唯一traceid 定位一个工作.
span可以被开启和关闭,如果开启了之后,在某一个地方一定要关闭
- trace
一组 span 的树状结构
基本使用
- pom文件
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-zipkin</artifactId>
</dependency>
- yml 编写
spring:
application:
name: provider
zipkin:
base-url: http://localhost:9411
sleuth:
sampler:
probability: 1
- 埋点
@Autowired
private Tracer tracer;
private void test02(){
Span span = tracer.nextSpan().name("myTrace").start();
try {
System.out.println("do something......");
}catch (Exception e){
span.error(e);
}finally {
span.finish();
}
}
- 创建父子span
@Test
public void testJoin() throws Exception{
ScopedSpan parentScope = tracing.tracer().startScopedSpan("parentScope");
Span job1 = tracing.tracer().nextSpan().name("任务1").start();
Thread.sleep(100);
Span job2 = tracing.tracer().newChild(job1.context()).name("job2").start();
Thread.sleep(50);
job2.finish();
job1.finish();
parentScope.finish();
}
Sleuth 原理
单线程之间的链路追踪
tracing.currentTraceContext() 内置了threadlocal
多线程之间链路追踪
sleuth 使用 InheritableThreadLocal 实现 多线程之间 trace 共享
tracing.currentTraceContext 里面的实现类
public static final class Default extends ThreadLocalCurrentTraceContext {
// Inheritable as Brave 3's ThreadLocalServerClientAndLocalSpanState was inheritable
static final InheritableThreadLocal<TraceContext> INHERITABLE = new InheritableThreadLocal<>();
}
runnable
callable
微服务之间链路追踪
通过 http请求头来定位服务链路
sleuth 会创建servlet拦截器,拦截到请求之后把请求头塞进去
HttpClient
把tracing传进去生成自己的client,发送的请求就会带有头信息
CloseableHttpClient client = TracingHttpClientBuilder.create(tracing).build();
Feign
根据feign的原理可知,feign通过创建 LoadBalancerFeignClient 实现微服务之间调用
sleuth 实现 TraceLoadBalancerFeignClient 类来完成头信息的传递
sleuth直接使用代理模式让原本的类去工作,自己只是添加了一个span记录
Sampler
sleuth 的采集服务.决定哪个请求需要被上报到zipkin中
SamplerFunction
类 返回bool值,决定是否要上报.
只能在span开始时决定,如果有父级,则不会去判断
在开始的时候判断入参,如果false,则整条链路都不上报.
ScopedSpan parentScope = tracing.tracer().startScopedSpan("parentScope",str->{
// 判断入参返回正确或者失败
return str.equals("fff");
},"入参");
Span start = tracing.tracer().nextSpan( ).name("job2221").start();
System.out.println("ddd");
start.finish();
parentScope.finish();
sleuthHttpClientSampler
在http请求中.sleuth帮我们创建了一个 sleuthHttpClientSampler
采集器,其中源码为
判断 skipPatten
请求中是否包含,如果返回false,则这次请求的整条链路都不会被记录.默认屏蔽url中 带有 /api-docs.*|/swagger.*|.*\\.png|.*\\.css|.*\\.js|.*\\.html|/favicon.ico|/hystrix.stream
的类型.如果想要更改修改配置文件 spring.sleuth.web.skip-patten
如果请求头中有 X-B3-Flags=1
则不论什么规则,都会记录下来
sleuth 通过kafka 发送数据到zipkin再存储到es中配置
- 客户端配置
sleuth 客户端会判断classpath 下有没有 web,kafka,activemq 等类,如果都有的话,则需要通过手动配置告诉sleuth发送的途径
spring:
zipkin:
base-url: http://localhost:9411
kafka:
topic: zipkin
sender:
type: kafka
POM 文件需要依赖
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-zipkin</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.kafka</groupId>
<artifactId>spring-kafka</artifactId>
</dependency>
- zipkin 配置(启动配置)
java -jar zipkin-server-2.12.9-exec.jar --STORAGE_TYPE=elasticsearch --ES_HOSTS=http://localhost:9200 --KAFKA_BOOTSTRAP_SERVERS=http://localhost:9092
在整合zipkin和 elasticsearch 7.10版本的时候,zipkin报错
invalid_index_template_exception. Failed: 1: index_pattern [zipkin:span-*] must not contain a ':';]"}]
需要在es手动创建索引 zipkin:*
现在kibana创建和配置文件创建还会报错,暂时卡住
github 上解决 zipkin无法兼容 es7.x版本.
3. 解决方法,直接在github上源码下载编译运行就可以了.使用 zipkin 2.22.0版本
4. 如果环境变量中有activemq kafka 等,需要指定选项 spring.zipkin.sender.type=kafka
5. 如果使用es作为存储,则zipkin上无法看到 dependencies 关系以来,需要下载 zipkin-dependencies. 然后执行 STORAGE_TYPE=elasticsearch ES_HOSTS=host1,host2 java -jar zipkin-dependencies.jar
,该任务是spark任务,会从es从拉取数据然后分析完成之后再存入es。这时候 zipkin 的依赖关系就是从es中获得