在前一期我们简单的介绍了作为分布式追踪系统的zipkin,并以一个demo简单演示了zipkin的使用,本文我们来从整体介绍一下zipkin的技术架构,以期能够更好的理解和使用zipkin。
一、理论基础
zipkin的理论基础是Google在2010年发表的论文《Dapper,a Large-Scale Distributed Systems Tracing Infrastructure》,这篇论文的原型是Google开源的Dapper链路追踪组件,这篇文章在业内链路追踪领域可谓是最经典的理论指导了,具有非常大的学习参考意义,建议有时间的朋友可以好好读一读。
基于链路追踪的一些理论基础,市场上出现了很多优秀的链路追踪组件,除了Google Dapper,如本文的主角Twitter Zipkin,阿里的EagleEye,华为(apache)的SkyWalking等。
这些链路追踪组件的出现有一个共同的原因:现代微服务框架的盛行,导致服务件调用问题排查的困难。
二、架构组成
回到本文的主角zipkin,一起来剖析一下zipkin的架构。如下图所示,zipkin包含4个组件,分别负责不同的功能:
1、collector
zipkin collector是zipkin的一个常驻进程组件,负责提供对外部的接口用以收集、校验和存储、索引追踪数据。
2、storage
存储模块,真正服务zipkin收集到的追踪数据的存储,zipkin默认存储在内存中,目前主要支持Cassandra、Elasticsearch和MySQL,对于这些存储源的支持都是可插拔的。
3、search
search组件提供的是查询服务,追踪数据被存储和索引以后就需要提供对外的查询服务,zipkin提供的JSON API用于数据检索。
4、web UI
zipkin提供了简洁而又直观的web UI用于进行数据的检索和调用链路展示,可以通过trace ID以及其他的条件来过滤查询,在页面上展示调用链路上各个节点所消耗的时间,方便用于判断性能瓶颈。
以上可以看到,其实zipkin的架构组成是比较简单的,部署亦并不复杂。架构简单、部署方便、存储插拔可选、UI简洁高效,这也是会被广泛应用的原因。
二、数据收集流程
在zipkin中每一次请求链路的追踪都是一个Trace,通过一个trace ID来标志。每个Trace可以拆分为有若干个存在依赖关系的Span,在微服务的架构下,一般请求会涉及到多个服务之间的调用或者是服务与中间件之间的交互,这里的每次请求调用就可以理解为一个Span,因此Span是一个树形的结构,每个Span都记录了相关的信息,使用一个64位的ID对一个Span进行标志。
Span通常包含其他的信息,如时间戳、parent id、annotation等信息。
- Trace
Span的集合,表示一次调用链路,使用traceId唯一标志。 - Annotation
记录请求特定事件信息,例如记录链路上各节点的时间,一般有以下几个分类:
1、cs(Client Start):客户端发起了请求
2、sr(Server Receive):服务端收到了请求
3、ss(Server Send):服务端返回处理结果给客户端
4、cr(Client Received):客户端收到服务端的响应
数据来源通常称为reporter,一般来说reporter由应用程序集成zipkin提供的客户端包来实现,数据的发送是轻量和异步的,不会阻塞应用程序的运行,对应用程序的负载很小。
在普通的请求传递时zipkin不会发送独立的请求收集详细的数据,而是在请求头中添加一些参数,例如,下图所示,添加trace请求头:
X-B3-TraceId: aa
X-B3-SpanId: 6b
在最终收到响应之后,异步的发送span详情到zipkin collector,由于是异步的,不会导致原业务代码的延迟。
三、流行的客户端组件
zipkin支持多语言和多场景,官方有很多流行的语言支持,例如:java语言中的brave,JavaScript语言中的zipkin-js。
社区也提供了很多的支持,例如Go语言的go-zipkin,Python语言的py_zipkin,Scala语言的kamon-zipkin等等,zipkin有非常好的社区支持生态。
四、使用brave向zipkin上报数据
业界针对常见的java应用程序框架提供了开源的分布式追踪组件brave,brave提供了面向Standard Servlet、Spring MVC、Http Client、JAX RS、Jersey、MySQL等接口集成能力,通过简单的配置就可以完成向zipkin上报数据的功能,无需对代码有过多的侵入。
同时zipkin提供了扩展的能力,当标准的功能无法满足需要时,可以通过接口进行扩展。
默认情况下,Brave将收集到的数据打印在日志中,当需要向zipkin报告数据时,需要引入zipkin 的Reporter,Reporter会将数据异步发送到zipkin Collector。
五、增加Log4j支持
appender.console.layout.pattern=%d{ABSOLUTE}[%X{traceId}/%X{spanId}] %-5p[%t]%C{2}{%F:%L}-%m%n LocalSpanAPI.start("localA")'...LocalSpanAPI.finish();