Zipkin

Published: by Creative Commons Licence

  • Tags:

Zipkin

Zipkin是一个分布式追踪系统,它能帮助收集用于发现微服务架构中的延迟问题原因所需的时间数据。它负责管理收集和查询数据。Zipkin的设计是基于Google Dapper论文。

应用向Zipkin报告时间数据。Zipkin UI也可以展现一幅依赖图,用于展现在各个应用之间有多少被追踪的请求来往。如果你正在查找延迟问题或错误的原因,你可以根据应用、轨迹的长度、注解、时间戳对所有的轨迹进行排序。如果你选中了一个轨迹,你可以看到每一个分块花费的时间在总的轨迹时间中的占比,这些将会帮助你确定出现问题的应用。

快速开始

有三种启动zipkin的方式:

  • 使用Java
  • 使用Docker
  • 使用源码

无论哪种启动方式,浏览http://your_host:9411去查看你所有的轨迹。

Docker

Docker Zipkin项目能够构建docker镜像,提供了脚本和docker-compose.yml用于启动预构建的镜像。最快速的方式是直接运行最新的镜像:

docker run -d -p 9411:9411 openzipkin/zipkin

Java

如果你在本地安装了Java 8或者更新版本,那么最快的启动方式就是获取最新版本的可执行Jar文件:

curl -sSL https://zipkin.io/quickstart.sh | bash -s
java -jar zipkin.jar

源代码

如果你在开发新特性,Zipkin可以从源代码启动。你需要先获得Zipkin的代码并且编译它:

# get the latest source
git clone https://github.com/openzipkin/zipkin
cd zipkin
# Build the server and also make its dependencies
./mvnw -DskipTests --also-make -pl zipkin-server clean install
# Run the server
java -jar ./zipkin-server/target/zipkin-server-*exec.jar

架构

总览

Tracer存在于应用中,并且记录发生的操作的时间和元数据。他们通常提供了增强库,所以他们的使用对于用户来说是透明的。比如说,一个被增强的网站服务器在它收到请求和发送响应时会记录信息。被收集的轨迹数据称为跨度(Span)。

被写入的增强要求在生产环境是安全的,并且间接费用低廉。由于这个原因,他们仅在信道中传播ID,告诉接收者一个tracer处于进度中。完整的跨度将通过其他信道发送给Zipkin。

比如说,当一个操作被追踪,并且它需要对外发送HTTP请求,一部分请求头将被加入到传播的ID中。请求头不会带上类似于操作名称之类的细节。

被增强的应用中的发送数据到Zipkin的模块被称为报告者(Reporter)。报告者通过数个信道中的一个将轨迹数据发送给Zipkin的收集器(collector),而收集器将轨迹数据持久化到存储中。之后,存储将被提供给UI的API所查询,用于向UI提供数据。

下面是描述流程的图:

architecture-1

样例流程

如总览中所提到的,标识符(identifier)在同一信道传输,而细节将通过其他信道发送。在两种情况中,增强代码需要负责创建有效的轨迹,并且合适地展示它们。比如,一个tracer确保它发送给下流(downstream)和Zipkin的数据是等价的。

下面是一个HTTP追踪的样例流程,而用户代码访问了/foo,这导致了一个单独的跨度的产生,在用户代码收到HTTP响应后异步发送给Zipkin。

┌─────────────┐ ┌───────────────────────┐  ┌─────────────┐  ┌──────────────────┐
│ User Code   │ │ Trace Instrumentation │  │ Http Client │  │ Zipkin Collector │
└─────────────┘ └───────────────────────┘  └─────────────┘  └──────────────────┘
       │                 │                         │                 │
           ┌─────────┐
       │ ──┤GET /foo ├─▶ │ ────┐                   │                 │
           └─────────┘         │ record tags
       │                 │ ◀───┘                   │                 │
                           ────┐
       │                 │     │ add trace headers │                 │
                           ◀───┘
       │                 │ ────┐                   │                 │
                               │ record timestamp
       │                 │ ◀───┘                   │                 │
                             ┌─────────────────┐
       │                 │ ──┤GET /foo         ├─▶ │                 │
                             │X-B3-TraceId: aa │     ────┐
       │                 │   │X-B3-SpanId: 6b  │   │     │           │
                             └─────────────────┘         │ invoke
       │                 │                         │     │ request   │
                                                         │
       │                 │                         │     │           │
                                 ┌────────┐          ◀───┘
       │                 │ ◀─────┤200 OK  ├─────── │                 │
                           ────┐ └────────┘
       │                 │     │ record duration   │                 │
            ┌────────┐     ◀───┘
       │ ◀──┤200 OK  ├── │                         │                 │
            └────────┘       ┌────────────────────────────────┐
       │                 │ ──┤ asynchronously report span     ├────▶ │
                             │                                │
                             │{                               │
                             │  "traceId": "aa",              │
                             │  "id": "6b",                   │
                             │  "name": "get",                │
                             │  "timestamp": 1483945573944000,│
                             │  "duration": 386000,           │
                             │  "annotations": [              │
                             │--snip--                        │
                             └────────────────────────────────┘

增强代码异步报告操作信息,以防止由于追踪系统或用户的问题代码造成延迟或失败。

传输方式

被增强库发送的跨度信息,必须从被追踪的服务传输到Zipkin收集器。这里有三种传输方式:HTTP,Kafka和Scribe。

组件

有4种组件组成了Zipkin:

  • collector
  • storage
  • search
  • web UI

Zipkin收集器

一旦轨迹数据抵达Zipkin收集器后台服务,它将被校验,保存,并且建立索引,用于之后的查询。

存储

Zipkin一开始是在Cassandra上存储数据,因为Cassandra可伸缩,有灵活的模式,并且在推特中重度使用。然而,我们将这个组件设计为可插件化。除了Cassandra外,我们原生支持EasticSearch和MySQL。其他后端可以以第三方扩展的形式提供。

Zipkin查询服务

一旦数据被存储和索引后,我们需要一种提取它的方式。查询服务提供一个简单的JSON API用于查找和获取轨迹。该API的主要的消费者就是我们的Web UI。

Web UI

我们创建了一个GUI,用于提供一个良好的方式查看数据。该web UI提供了基于服务,时间和注解的查看轨迹的方式。

增强库

Java-Brave

Brave是一个用于捕获分布式操作的延迟信息的库,它将数据作为跨度(Span)报告给Zipkin。

Brave是一个无依赖的轨迹追踪器包,可以在JRE6+环境工作。

Brave支持了主流的远程调用服务,包括dubbo,httpasyncclient,httpclient,mysql,netty,servlet,rabbitmq,spring-webmvc等。

依赖管理

当使用多个brave组件,你会希望将这些组件的版本放在一起统一管理,这样可以避免冲突。

下面的内容可以导入brave组件的版本等信息的声明。

  <dependencyManagement>
    <dependencies>
      <dependency>
        <groupId>io.zipkin.brave</groupId>
        <artifactId>brave-bom</artifactId>
        <version>${brave.version}</version>
        <type>pom</type>
        <scope>import</scope>
      </dependency>
    </dependencies>
  </dependencyManagement>

接下来实际使用组件,比如

<dependency>
  <groupId>io.zipkin.brave</groupId>
  <artifactId>brave-instrumentation-okhttp3</artifactId>
</dependency>