zipkin:打造自己的鏈路跟蹤系統(一)

在微服務盛行的時代,一個公司的應用數量動輒成百上千個。應用之間的依賴關係錯綜複雜,定位問題、排查問題是一件令人頭疼的事情。
爲了解決這個問題,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官網

架構

zipkin架構

  • reporter:上報鏈路數據的模塊,配置在具體的應用中
  • transport:傳輸鏈路數據的模塊,通常爲http、Kafka
  • collector:收集&消費鏈路數據的模塊,默認通過http收集,可以配置爲Kafka消費
  • storage:存儲鏈路數據的模塊,具體實例可以爲ES、Cassandra或者mysql

鏈路數據模型

[
    {
        "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

業界還有其它開源的鏈路跟蹤系統,爲什麼要選擇zipkin?

首先列舉自己的核心訴求:

  1. 性能影響小:能夠容忍輕微的性能損失
  2. 多語言支持:Java、Node、Go等
  3. 插件可擴展:可以定製化開發鏈路跟蹤插件
  4. 社區支持力度大:自己不需要過多的開發鏈路插件
  5. 接入成本小

業界開源的主流鏈路跟蹤系統:

  1. skywalking
  2. pinpoint
  3. zipkin
  4. jaeger

主要對比skywalking和zipkin

skywalking zipkin
內部實現方式 javaagent,字節碼增強 aop插件
語言支持 多語言 多語言
性能
插件擴展 困難 容易
接入成本 低,開發無感知 低,開發需要配置
社區支持

可以看到

  • skywalking相較於zipkin的優勢在於接入成本低、性能稍好
  • zipkin相較於skywalking的優勢在於插件擴展容易

我們最終選擇的是zipkin

zipkin和brave

首先說明一下zipkin和brave的關係:

  1. 從開頭的架構圖中可以看到:zipkin是服務端,用於查詢分析、收集和持久化鏈路數據
  2. brave則是zipkin官方出品的Java語言的鏈路數據採集插件。同理還有js、go版本的採集插件

搭建zipkin服務器

在官方的demo中提供了docker鏡像啓動和jar包啓動,但如果要做個性化開發的話必須通過自建項目然後引入zipkin server依賴進行啓動。
前面兩種啓動方式官網都有詳細的教程,這裏就不介紹了。下面主要介紹一下自建項目引入zipkin server依賴啓動的方式。

創建SpringBoot項目

創建好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>

配置kafka和es地址:

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的使用

使用brave所需要的maven依賴附在文章末尾

官方提供的brave插件列表非常多,基本上涵蓋了日常用到的鏈路:http、rpc、db等。這是官方支持的中間件插件:支持的插件

把Http API請求接入鏈路跟蹤

下面以將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);
  }
}

上面的代碼的核心配置爲:

  1. sender:發送到zipkin服務器的方式:http或者kafka
  2. reporter:reporter決定何時將本地的鏈路數據發送到zipkin server
  3. tracing:非常重要,所有的鏈路跟蹤組件都依賴它。包括自己開發的鏈路跟蹤組件
  4. httpTracing、filter:爲http請求添加鏈路信息

把dubbo rpc請求接入鏈路跟蹤

因爲上一個步驟已經把brave核心的配置都已經陪好了,所以把dubbo鏈路加入到鏈路跟蹤就非常簡單了,兩個步驟:

首先引入brave dubbo相關依賴:

        <!--  dubbo插件      -->
        <dependency>
            <groupId>io.zipkin.brave</groupId>
            <artifactId>brave-instrumentation-dubbo-rpc</artifactId>
        </dependency>

然後再將brave dubbo filter添加到dubbo的filter鏈中:

  • 方法1:在application.properties文件中添加兩行配置:
dubbo.consumer.filter=tracing
dubbo.provider.filter=tracing
  • 方法2:在dubbo xml配置文件中添加配置
<dubbo:consumer filter="tracing"/>
<dubbo:provider filter="tracing"/>

對,就這麼簡單!

把mysql操作添加到鏈路跟蹤

數據庫操作這一環在整個請求鏈路中是非常重要的,很多問題都是因爲數據庫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>.
 */

其它的鏈路

其它的鏈路接入的方法類似,參考官方文檔即可,這裏就不展開介紹了。

其它

zipkin系列的文章

  1. 爲阿里雲ons/RocketMQ添加鏈路跟蹤
  2. zipkin server個性化開發:統計報表功能、報警服務

zipkin相關的網站

使用到的brave依賴

		<!--  核心依賴 -->
        <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>
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章