在微服务盛行的时代,一个公司的应用数量动辄成百上千个。应用之间的依赖关系错综复杂,定位问题、排查问题是一件令人头疼的事情。 为了解决这个问题,Google的Dapper论文应运而生。Twitter基于该论文打造了自己的链路跟踪系统(也就是本文章的主角):zipkin并将其开源
Zipkin is a distributed tracing system. It helps gather timing data needed to troubleshoot latency problems in service architectures. Features include both the collection and lookup of this data. Zipkin是一个分布式追踪系统。它有助于收集解决服务架构中的延迟问题所需的计时数据。功能包括收集和查找此数据。
简单的介绍一下zipkin,详细的介绍请移步: zipkin官网
[ { "traceId":"5982fe77008310cc80f1da5e10147517", "name":"get", "id":"bd7a977555f6b982", "timestamp":1458702548467000, "duration":386000, "localEndpoint":{ "serviceName":"zipkin-query", "ipv4":"192.168.1.2", "port":9411 }, "annotations":[ { "timestamp":1458702548467000, "value":"sr" }, { "timestamp":1458702548853000, "value":"ss" } ] } ]
其它更多有关于zipkin的信息请移步:
业界还有其它开源的链路跟踪系统,为什么要选择zipkin?
首先列举自己的核心诉求:
业界开源的主流链路跟踪系统:
主要对比skywalking和zipkin
skywalking | zipkin | |
---|---|---|
内部实现方式 | javaagent,字节码增强 | aop插件 |
语言支持 | 多语言 | 多语言 |
性能 | 好 | 好 |
插件扩展 | 困难 | 容易 |
接入成本 | 低,开发无感知 | 低,开发需要配置 |
社区支持 | 好 | 好 |
可以看到
我们最终选择的是zipkin
首先说明一下zipkin和brave的关系:
在官方的demo中提供了docker镜像启动和jar包启动,但如果要做个性化开发的话必须通过自建项目然后引入zipkin server依赖进行启动。 前面两种启动方式官网都有详细的 教程 ,这里就不介绍了。下面主要介绍一下自建项目引入zipkin server依赖启动的方式。
创建好SpringBoot项目后,引入zipkin server相关jar包:
<!-- zipkin 核心依赖 --> <dependency> <groupid>io.zipkin.java</groupid> <artifactid>zipkin-server</artifactid> <version>${zipkin-server.version}</version> </dependency> <!-- ui界面 可选 --> <dependency> <groupid>io.zipkin.java</groupid> <artifactid>zipkin-autoconfigure-ui</artifactid> <version>${zipkin-server.version}</version> </dependency> <!-- es存储 可选 --> <dependency> <groupid>io.zipkin.java</groupid> <artifactid>zipkin-autoconfigure-storage-elasticsearch</artifactid> <version>${zipkin-server.version}</version> </dependency> <!-- kafka collector 可选 --> <dependency> <groupid>io.zipkin.java</groupid> <artifactid>zipkin-autoconfigure-collector-kafka</artifactid> <version>${zipkin-server.version}</version> </dependency>
zipkin.collector.kafka.bootstrap-servers= zipkin.collector.kafka.topic= zipkin.collector.kafka.groupId= zipkin.storage.type=elasticsearch zipkin.storage.elasticsearch.hosts=
@EnableZipkinServer
注解 最后在SpringBoot启动类上配置 @EnableZipkinServer
注解
@EnableZipkinServer @SpringBootApplication public class ServerApplication { public static void main(String[] args) { SpringApplication.run(ServerApplication.class, args); } }
至此,一个可运行的zipkin服务器就搭建完成了;定制化开发:报警、性能分析下篇博客再介绍。下面介绍一下brave的使用;
官方提供的brave插件列表非常多,基本上涵盖了日常用到的链路:http、rpc、db等。这是官方支持的中间件插件:
下面以将Http API接入链路跟踪为例,介绍需要配置的地方: 官方demo
/** * This adds tracing configuration to any web mvc controllers or rest template clients. */ @Configuration // Importing a class is effectively the same as declaring bean methods @Import(SpanCustomizingAsyncHandlerInterceptor.class) public class TracingConfiguration extends WebMvcConfigurerAdapter { /** * Configuration for how to send spans to Zipkin * 配置如何发送到zipkin服务器,这里使用http的方式发送 */ @Bean Sender sender() { return OkHttpSender.create("http://127.0.0.1:9411/api/v2/spans"); } /** * Configuration for how to buffer spans into messages for Zipkin * 配置reporter,何时发送到zipkin。触发方式:定时、size大小等 * */ @Bean AsyncReporter<span> spanReporter() { return AsyncReporter.create(sender()); } /** * Controls aspects of tracing such as the service name that shows up in the UI * 发送到zipkin的服务名,同一个应用的多个实例服务名应该相同 */ @Bean Tracing tracing(@Value("${spring.application.name}") String serviceName) { return Tracing.newBuilder() .localServiceName(serviceName) .propagationFactory(ExtraFieldPropagation.newFactory(B3Propagation.FACTORY, "user-name")) .currentTraceContext(ThreadLocalCurrentTraceContext.newBuilder() // puts trace IDs into logs // 可以通过MDC.get("traceId")的方式拿到链路ID .addScopeDecorator(MDCScopeDecorator.create()) .build() ) .spanReporter(spanReporter()).build(); } /** * Allows someone to add tags to a span if a trace is in progress * 允许添加自定义tag到链路中 * */ @Bean SpanCustomizer spanCustomizer(Tracing tracing) { return CurrentSpanCustomizer.create(tracing); } /** Decides how to name and tag spans. By default they are named the same as the http method */ @Bean HttpTracing httpTracing(Tracing tracing) { return HttpTracing.create(tracing); } /** * Creates server spans for http requests * 为http请求自动创建链路或者span * */ @Bean Filter tracingFilter(HttpTracing httpTracing) { return TracingFilter.create(httpTracing); } /** * 为RestTemplate发起的请求自动创建zipkin的链路信息 * */ @Bean RestTemplateCustomizer useTracedHttpClient(HttpTracing httpTracing) { final CloseableHttpClient httpClient = TracingHttpClientBuilder.create(httpTracing).build(); return new RestTemplateCustomizer() { @Override public void customize(RestTemplate restTemplate) { restTemplate.setRequestFactory(new HttpComponentsClientHttpRequestFactory(httpClient)); } }; } @Autowired SpanCustomizingAsyncHandlerInterceptor webMvcTracingCustomizer; /** Decorates server spans with application-defined web tags */ @Override public void addInterceptors(InterceptorRegistry registry) { registry.addInterceptor(webMvcTracingCustomizer); } }
上面的代码的核心配置为:
因为上一个步骤已经把brave核心的配置都已经陪好了,所以把dubbo链路加入到链路跟踪就非常简单了,两个步骤:
首先引入brave dubbo相关依赖:
<!-- dubbo插件 --> <dependency> <groupid>io.zipkin.brave</groupid> <artifactid>brave-instrumentation-dubbo-rpc</artifactid> </dependency>
然后再将brave dubbo filter添加到dubbo的filter链中:
dubbo.consumer.filter=tracing dubbo.provider.filter=tracing
<dubbo:consumer filter="tracing" /> <dubbo:provider filter="tracing" />
对,就这么简单!
数据库操作这一环在整个请求链路中是非常重要的,很多问题都是因为数据库sql执行超时引起的。所以非常有必要把数据库操作给监控起来,具体的配置参考官方就行:
/** * A MySQL exception interceptor that will annotate spans with SQL error codes. * * <p>To use it, both TracingQueryInterceptor and TracingExceptionInterceptor must be added by * appending <code>?queryInterceptors=brave.mysql8.TracingQueryInterceptor&exceptionInterceptors=brave.mysql8.TracingExceptionInterceptor</code>. */
其它的链路接入的方法类似,参考官方文档即可,这里就不展开介绍了。
<!-- 核心依赖 --> <dependency> <groupid>io.zipkin.brave</groupid> <artifactid>brave</artifactid> </dependency> <!-- reporter --> <dependency> <groupid>io.zipkin.reporter2</groupid> <artifactid>zipkin-sender-okhttp3</artifactid> </dependency> <dependency> <groupid>io.zipkin.reporter2</groupid> <artifactid>zipkin-sender-kafka</artifactid> </dependency> <!-- 日志依赖 --> <!-- Integrates so you can use log patterns like %X{traceId}/%X{spanId} --> <dependency> <groupid>io.zipkin.brave</groupid> <artifactid>brave-context-slf4j</artifactid> </dependency> <!-- spring mvc项目支持 --> <dependency> <groupid>io.zipkin.brave</groupid> <artifactid>brave-spring-beans</artifactid> </dependency> <!-- mvc插件 --> <!-- Adds the MVC class and method names to server spans --> <dependency> <groupid>io.zipkin.brave</groupid> <artifactid>brave-instrumentation-spring-webmvc</artifactid> </dependency> <!-- httpclient插件 --> <!-- Instruments the underlying HttpClient requests that call the backend --> <dependency> <groupid>io.zipkin.brave</groupid> <artifactid>brave-instrumentation-httpclient</artifactid> </dependency> <!-- dubbo插件 --> <dependency> <groupid>io.zipkin.brave</groupid> <artifactid>brave-instrumentation-dubbo-rpc</artifactid> </dependency> <!-- mysql插件 --> <dependency> <groupid>io.zipkin.brave</groupid> <artifactid>brave-instrumentation-mysql8</artifactid> </dependency>