Twitter zipkin 分布式跟踪系统的设计与实现

概述

Twitter的zipkin是一个致力于收集Twitter所有的分布式服务的时间数据的分布式跟踪系统。它提供了收集数据,和查询数据两大服务。系统的理论模型来自于Google Dapper 论文。Dapper这篇论文可以点击这里

为什么需要分布式跟踪系统?

通过采集跟踪数据可以帮助开发者深入了解在分布式系统中某一个特定的请求时如何执行的。假如说,我们现在有一个用户请求超时,我们就可以将这个超时的请求调用链展示在UI当中。我们可以很快度的定位到导致响应很慢的服务究竟是什么。如果对这个服务细节也很很清晰,那么我们还可以定位是服务中的哪个问题导致超时。

架构

一般来说,分布式跟踪系统由以下组件构成。

封装基础框架

跟踪的信息是通过封装好的框架,部署在每台机器上面,然后发送到zipkin。当主机对某一个服务发起请求时,请求所经过的路径都会被跟踪记录下来,并且最终会在zipkin 的服务器端把这些调用链组装起来。Twitter对一些的基础框架作了封装,以便于对请求做拦截,并且在拦截的同时,还会把 Trace header 传递到下一层。

Finagle

Finagle 是Twitter的 SOA 框架,支持Java/Scala两种语言。

Finagle 在Twitter被广泛使用,通过finagle来支持跟踪是一个非常自然的入口点。目前twitter可以使它支持客户端/服务端的Thrift协议和HTTP协议,对于Memecache和Redis仅能做到客户端支持。使用Scala语言来建立一个 Finagle Server,非常简单,如下。想要加入跟踪功能,只需要把tracer 作为参数传入,构建即可。

1ServerBuilder().codec(ThriftServerFramedCodec()) 2 .bindTo(serverAddr) 3 .name("servicename") 4 .tracer(ZipkinTracer.mk()) 5 .build(new SomeService.FinagledService(queryService, new TBinaryProtocol.Factory()))

跟踪客户端也是类似的。一旦这样做了之后,请求就会被自动记录下来。包括客户端请求的开始和结束、服务端的开始和结束。

如果你想记录一些自定义的内容,zipkin还提供了如下的API:记录 timestamp / value

Trace.record("starting that extremely expensive computation")

记录key/value

Trace.recordBinary("http.response.code", "500")

Ruby Thrift

Ruby thrift 是对ruby语言的支持。

Querulous

Querulous 是一个Scala的访问数据的框架,可以理解为Java中的hibernate,用它就可以跟踪所有的sql访问情况。

Cassie

Cassie是一个基于Finagle的Cassandra客户端。可以和Finagle一样设置跟踪功能。

KeyspaceBuilder.cluster.keyspace(keyspace).tracer(ZipkinTracer.mk()) 

传输

Twitter使用scirbe来把所有的跟踪数据传输到zipkin的后端和hadoop文件系统。scribe是facebook开发的,运行在每台机器上面。

Zipkin collector 进程

一旦搜集的数据到达zipkin的后端,服务器端会对数据进行校验、存储,索引。

Storage

数据最开始是放在cassandra数据库中,当然zipkin在存储这块,做到可插播,目前已经做到支持redis,Hbase,MySQL,PostgreSQL, SQLite, and H2。

Zipkin query 进程

当数据被索引和存储之后,zipkin还提供一个查询进程用于获取数据。查询接口也是基于Thrift协议来实现了,做到了与语言无关性。

UI

zipkin的数据可视化是通过一个叫做D3的js库来实现的。

模块

怎么去封装一个框架

在Twitter,zipkin已经被集成到一些基础的框架和类库之中,对于开发者来说,如果想要封装其他的框架,就需要对跟踪的数据模型有一个清晰的认识:

  • annotation :包括一个值,时间戳,主机名
  • span :一组代表一次RPC请求所包含的annotations
  • Trace :一组代表一次用户请求所包含的spans,其中根span只有一个。

另外,还需要把一些轻量级的TraceID和Span ID在服务之间传递,这样才能形成一个完整的调用链。

跟踪的头信息包括以下:

  • Trace Id - 全局的 id
  • Span Id - 每个方法调用的id
  • Optional Parent Span Id - 当前方法调用的父方法的 span id
  • Sampled boolean - 是否需要采样

以HTTP协议为例子,具体的封装步骤如下:

服务端

  1. 检查http header中是否存在Trace header信息,如果存在就把这些信息取出来,然后存入到ThreadLocal中,进而把请求串接起来;

  2. 如果不存在,那么就开始一个新的Trace

客户端

  1. 客户端需要把Trace header的信息放在ThreadLocal中,

  2. 如果需要进行RPC调用,需要把Trace header的信息放在协议中,例如http header。

github 地址

Twitter zipkin github 地址 https://github.com/twitter/zipkin

点赞
收藏

评论区

加载中...

相关推荐

MySQL:[Err] 1292 - Incorrect datetime value: ‘0000-00-00 00:00:00‘ for column ‘CREATE_TIME‘ at row 1

文章目录问题用navicat导入数据时,报错:原因这是因为当前的MySQL不支持datetime为0的情况。解决修改sql\mode:sql\mode:SQLMode定义了MySQL应支持的SQL语法、数据校验等,这样可以更容易地在不同的环境中使用MySQL。全局s

Oracle 分组与拼接字符串同时使用

SELECTT.,ROWNUMIDFROM(SELECTT.EMPLID,T.NAME,T.BU,T.REALDEPART,T.FORMATDATE,SUM(T.S0)S0,MAX(UPDATETIME)CREATETIME,LISTAGG(TOCHAR(

MySQL部分从库上面因为大量的临时表tmp_table造成慢查询

背景描述Time:20190124T00:08:14.70572408:00User@Host:@Id:Schema:sentrymetaLast_errno:0Killed:0Query_time:0.315758Lock_

皕杰报表之UUID

​在我们用皕杰报表工具设计填报报表时,如何在新增行里自动增加id呢?能新增整数排序id吗?目前可以在新增行里自动增加id,但只能用uuid函数增加UUID编码,不能新增整数排序id。uuid函数说明:获取一个UUID,可以在填报表中用来创建数据ID语法:uuid()或uuid(sep)参数说明:sep布尔值,生成的uuid中是否包含分隔符'',缺省为

手写Java HashMap源码

HashMap的使用教程HashMap的使用教程HashMap的使用教程HashMap的使用教程HashMap的使用教程22

2020年前端实用代码段,为你的工作保驾护航

有空的时候,自己总结了几个代码段,在开发中也经常使用,谢谢。1、使用解构获取json数据let jsonData  id: 1,status: "OK",data: 'a', 'b';let  id, status, data: number   jsonData;console.log(id, status, number )