Sleuth集成Zipkin
Spring Cloud
中有 Spring Cloud Sleuth组件,Zipkin
的客户端都是通过Sleuth
集成在项目中,ZipkinServer
启动在独立进程中。
什么是Zipkin
Zipkin
是 Twitter
的一个开源项目,基于 Google Dapper
实现。可以使用它来收集各个服务器上请求链路的跟踪数据,数据可以存储到外部服务当中,如ES
等,并通过它提供的 REST API
接口来辅助我们查询跟踪数据以实现对分布式系统的监控程序,从而及时地发现系统中出现的延迟升高问题并找出系统性能瓶颈的根源。除了面向开发的API
接口之外,它也提供了简单的 UI
组件帮助我们直观的搜索跟踪信息和分析请求链路明细。
还有一些比较优秀的链路跟踪系统,如SkyWalking,Pinpoint,CAT等等,有空也可以看看,其实相比Zipkin
,它们的侵入性相对更小,基于javaagent
技术,不用在源码中显式调用。
启动Zipkin服务器
Zipkin
可以微服务启动并通过注册中心注册,也可以用Docker
启动,这里以微服务启动为例,编写一个Zipkin
微服务
引入依赖:
<dependency>
<groupId>io.zipkin.java</groupId>
<artifactId>zipkin-server</artifactId>
<version>${zipkin-server.version}</version>
<exclusions>
<exclusion>
<artifactId>spring-boot-starter-log4j2</artifactId>
<groupId>org.springframework.boot</groupId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>io.zipkin.java</groupId>
<artifactId>zipkin-autoconfigure-ui</artifactId>
<version>${zipkin-server.version}</version>
</dependency>
<dependency>
<groupId>io.zipkin.java</groupId>
<artifactId>zipkin-autoconfigure-storage-elasticsearch-http</artifactId>
<version>${zipkin-elasticsearch.version}</version>
</dependency>
配置application.yml
:
spring.application.name: cloud-service-zipkin
server:
port: 9411
# 注册中心配置(根据实际情况配置)
spring:
cloud:
consul:
host: xxxx
port: 8500
# Elasticsearch配置,存储Zipkin数据
zipkin:
storage:
type: elasticsearch
elasticsearch:
hosts: xxx.xxx.xxx.xxx:9200
启动代码:
@EnableZipkinServer
@EnableDiscoveryClient
@SpringBootApplication
public class CloudServiceZipkinApplication {
public static void main(String[] args) {
SpringApplication.run(CloudServiceZipkinApplication.class, args);
}
}
目前启动到:http://localhost:9411/,可以用浏览器查看。
配置客户端
Spring Cloud
中有 Spring Cloud Sleuth组件,可以支持Zipkin
,默认微服务之间的调用已经是支持的了,不过如果需要支持更多监控项目,可以到GitHub
上openzipkin
查找相关官方提供组件。
参考地址:https://zipkin.io/pages/tracers_instrumentation.html
Github地址:https://github.com/openzipkin/brave
以import
的方式添加父级pom
,注意直接引入spring-cloud-starter-sleuth之后就可以了,不用再引入brave-bom等,重复引入依赖也没有问题,不过不能和已经存在的依赖版本不一致,否者可能因为版本问题报错,sleuth已经引入蛮多的brave包,如果需要更多brave,根据实际情况引入。
在各个微服务中配置:
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-sleuth</artifactId>
</dependency>
<!--sleuth已经包含下面的brave配置了,不用再引入,只记录下,如果有些工具类没有使用sleuth编译环境可能有用-->
<dependency>
<groupId>io.zipkin.brave</groupId>
<artifactId>brave-bom</artifactId>
<version>${brave.version}</version>
<type>pom</type>
<scope>import</scope>
</dependency>
<dependency>
<groupId>io.zipkin.brave</groupId>
<artifactId>brave</artifactId>
</dependency>
配置yml配置文件
# 使用注册中心方式
spring:
zipkin:
enabled: true
discovery-client-enabled: true
# 使用URL方式(二选一)
spring:
zipkin:
enabled: true
base-url: http://localhost:9411
# 如果想要提高采样率,能快速看到结果,采样率最大为1
spring:
sleuth:
sampler:
probability: 1
调用后,可以看到微服务调用的链路信息,耗时,地址等信息都有:
点开还有更详细信息:
HttpClient的Brave集成
HttpClient组件是brave官方自带组件,默认sleuth已经引入,检查依赖如果没有,也可以尝试手动引入:
参考文档:https://github.com/openzipkin/brave/tree/master/instrumentation/httpclient
<dependency>
<groupId>io.zipkin.brave</groupId>
<artifactId>brave-instrumentation-httpclient</artifactId>
<version>${brave.version}</version>
</dependency>
主要流程就是通过TracingHttpClientBuilder
构建一个HttpClientBuilder
,然后用HttpClientBuilder#build
方法获取CloseableHttpClient
,如果还需要其他定制化,可以在HttpClientBuilder
中添加更多配置信息:
@Autowired(required = false)
protected Sender sender;
@Autowired(required = false)
protected CurrentTraceContext currentTraceContext;
@Override
public HttpClientBuilder getHttpClientBuilder(String name) {
Tracing brave = Tracing
.newBuilder()
.localServiceName(name) // 名字
.spanReporter(AsyncReporter.builder(sender).build()) // 发送上报
.sampler(Sampler.ALWAYS_SAMPLE) // 采样配置
.currentTraceContext(currentTraceContext) // 本地TraceContext,ThreadLocal
.build();
// 如果需要更多信息,可以使用HttpClientParser来定制
HttpTracing httpTracing = HttpTracing.newBuilder(brave)
.clientParser(new HttpClientSpanParser()).build();
return TracingHttpClientBuilder.create(httpTracing);
}
使用的地方:
HttpClientBuilder httpClientBuilder = clientTraceProvider.getHttpClientBuilder("http-client");
CloseableHttpClient httpClient = httpClientBuilder.build();
实际示例效果:
CXF的Brave集成
CXF
的brave
组件不是官方支持组件,是第三方开发组件,需要单独引入,以前老版本的CXF
有个brave-cxf3
组件,不过已经不用了,新版使用cxf-integration-tracing-brave
,使用和CXF
对应的版本就行。
参考文档:https://cxf.apache.org/docs/using-openzipkin-brave.html
<dependency>
<groupId>org.apache.cxf</groupId>
<artifactId>cxf-integration-tracing-brave</artifactId>
<version>${cxf.version}</version>
<!--如果自带的brave依赖版本和已经存在版本不一致,可以考虑排除相关依赖,否者可能会启动报错-->
<exclusions>
<exclusion>
<groupId>io.zipkin.brave</groupId>
<artifactId>brave</artifactId>
</exclusion>
<exclusion>
<groupId>io.zipkin.brave</groupId>
<artifactId>brave-instrumentation-http</artifactId>
</exclusion>
</exclusions>
</dependency>
加入配置,注意Sender
和CurrentTraceContext
是从已有的sleuth
环境中注入,不然TraceID
会生成一个新的,整个调用过程就串联不起来,这里注入的其实就是ThreadLocalCurrentTraceContext
。
注意:客户端使用BraveClientFeature
,服务端使用BraveFeature
即可,比较方便集成。
@Autowired(required = false)
protected Sender sender;
@Autowired(required = false)
protected CurrentTraceContext currentTraceContext;
// Sender和CurrentTraceContext为自动注入, name需要自定义
protected Feature getBraveFeature(String name) {
Tracing brave = Tracing
.newBuilder()
.localServiceName(name)
.spanReporter(AsyncReporter.builder(sender).build())
.sampler(Sampler.ALWAYS_SAMPLE)
.currentTraceContext(currentTraceContext)
.build();
return new BraveClientFeature(brave);
}
然后在构建FactoryBean
的地方添加对应的Feature
就可以了。
@Bean
public SomeWebService someWebService() {
JAXRSClientFactoryBean bean = new JAXRSClientFactoryBean();
bean.setAddress(address);
bean.setServiceClass(SomeWebService.class);
bean.getFeatures().add(getBraveFeature(SomeWebService.class.getSimpleName())); // 这里引入brave的feature
SomeWebService client = bean.create(SomeWebService.class);
return client; // 直接返回client
}
尝试调用后可以在zipkin中看到:
CXF的brave集成完成。
常见错误
- 如果多个brave版本,可能引起下面的错误,建议排除一些依赖版本,保持统一的brave版本:
Caused by: java.lang.ClassNotFoundException: brave.TracingCustomizer
at java.base/jdk.internal.loader.BuiltinClassLoader.loadClass(BuiltinClassLoader.java:582)
at java.base/jdk.internal.loader.ClassLoaders$AppClassLoader.loadClass(ClassLoaders.java:178)
at java.base/java.lang.ClassLoader.loadClass(ClassLoader.java:521)
at java.base/java.lang.Class.forName0(Native Method)
at java.base/java.lang.Class.forName(Class.java:398)
at java.base/sun.reflect.generics.factory.CoreReflectionFactory.makeNamedType(CoreReflectionFactory.java:114)
... 110 common frames omitted
- 如果引入较新版本的brave,与内部版本不兼容,可能还有一下错误:
Description:
An attempt was made to call a method that does not exist. The attempt was made from the following location:
org.springframework.cloud.sleuth.autoconfig.TraceAutoConfiguration.sleuthCurrentTraceContextBuilder(TraceAutoConfiguration.java:193)
The following method did not exist:
brave.propagation.ThreadLocalCurrentTraceContext.newBuilder()Lbrave/propagation/CurrentTraceContext$Builder;
The method's class, brave.propagation.ThreadLocalCurrentTraceContext, is available from the following locations:
jar:file:/C:/Users/gary.fu/.m2/repository/io/zipkin/brave/brave/5.13.10/brave-5.13.10.jar!/brave/propagation/ThreadLocalCurrentTraceContext.class
It was loaded from the following location:
file:/C:/Users/gary.fu/.m2/repository/io/zipkin/brave/brave/5.13.10/brave-5.13.10.jar