服務鏈路追蹤(Spring Cloud Sleuth)


sleuth:英 [slu:θ] 美 [sluθ] n.足跡,警犬,偵探vi.做偵探

  微服務架構是一個分布式架構,它按業務划分服務單元,一個分布式系統往往有很多個服務單元。由於服務單元數量眾多,業務的復雜性,如果出現了錯誤和異常,很難去定位。主要體現在,一個請求可能需要調用很多個服務,而內部服務的調用復雜性,決定了問題難以定位。所以微服務架構中,必須實現分布式鏈路追蹤,去跟進一個請求到底有哪些服務參與,參與的順序又是怎樣的,從而達到每個請求的步驟清晰可見,出了問題,很快定位。

舉幾個例子:

1、在微服務系統中,一個來自用戶的請求,請求先達到前端A(如前端界面),然后通過遠程調用,達到系統的中間件B、C(如負載均衡、網關等),最后達到后端服務D、E,后端經過一系列的業務邏輯計算最后將數據返回給用戶。對於這樣一個請求,經歷了這么多個服務,怎么樣將它的請求過程的數據記錄下來呢?這就需要用到服務鏈路追蹤。

2、分析微服務系統在大壓力下的可用性和性能。

Zipkin可以結合壓力測試工具一起使用,分析系統在大壓力下的可用性和性能。

設想這么一種情況,如果你的微服務數量逐漸增大,服務間的依賴關系越來越復雜,怎么分析它們之間的調用關系及相互的影響?

spring boot對zipkin的自動配置可以使得所有RequestMapping匹配到的endpoints得到監控,以及強化了RestTemplate,對其加了一層攔截器,使得由它發起的http請求也同樣被監控。

 

Google開源的 Dapper鏈路追蹤組件,並在2010年發表了論文《Dapper, a Large-Scale Distributed Systems Tracing Infrastructure》,這篇文章是業內實現鏈路追蹤的標桿和理論基礎,具有非常大的參考價值。

目前,鏈路追蹤組件有Google的Dapper,Twitter 的Zipkin,以及阿里的Eagleeye (鷹眼)等,它們都是非常優秀的鏈路追蹤開源組件。

本文主要講述如何在Spring Cloud Sleuth中集成Zipkin。在Spring Cloud Sleuth中集成Zipkin非常的簡單,只需要引入相應的依賴和做相關的配置即可。

一、簡介

Spring Cloud Sleuth 主要功能就是在分布式系統中提供追蹤解決方案,並且兼容支持了 zipkin,你只需要在pom文件中引入相應的依賴即可。

二、服務追蹤分析

微服務架構上通過業務來划分服務的,通過REST調用,對外暴露的一個接口,可能需要很多個服務協同才能完成這個接口功能,如果鏈路上任何一個服務出現問題或者網絡超時,都會形成導致接口調用失敗。隨着業務的不斷擴張,服務之間互相調用會越來越復雜。

Paste_Image.png

隨着服務的越來越多,對調用鏈的分析會越來越復雜。它們之間的調用關系也許如下:

Paste_Image.png

三、術語

Spring Cloud Sleuth采用的是Google的開源項目Dapper的專業術語。

  • Span:基本工作單元,例如,在一個新建的span中發送一個RPC等同於發送一個回應請求給RPC,span通過一個64位ID唯一標識,trace以另一個64位ID表示,span還有其他數據信息,比如摘要、時間戳事件、關鍵值注釋(tags)、span的ID、以及進度ID(通常是IP地址) 
    span在不斷的啟動和停止,同時記錄了時間信息,當你創建了一個span,你必須在未來的某個時刻停止它。
  • Trace:一系列spans組成的一個樹狀結構,例如,如果你正在跑一個分布式大數據工程,你可能需要創建一個trace。
  • Annotation:用來及時記錄一個事件的存在,一些核心annotations用來定義一個請求的開始和結束 
    • cs - Client Sent -客戶端發起一個請求,這個annotion描述了這個span的開始
    • sr - Server Received -服務端獲得請求並准備開始處理它,如果將其sr減去cs時間戳便可得到網絡延遲
    • ss - Server Sent -注解表明請求處理的完成(當請求返回客戶端),如果ss減去sr時間戳便可得到服務端需要的處理請求時間
    • cr - Client Received -表明span的結束,客戶端成功接收到服務端的回復,如果cr減去cs時間戳便可得到客戶端從服務端獲取回復的所有所需時間 

將Span和Trace在一個系統中使用Zipkin注解的過程圖形化:

Paste_Image.png

四、sleuth與Zipkin關系?

spring cloud提供了spring-cloud-sleuth-zipkin來方便集成zipkin實現(指的是Zipkin Client,而不是Zipkin服務器),該jar包可以通過spring-cloud-starter-zipkin依賴來引入。

五、Zipkin

Zipkin是什么
Zipkin分布式跟蹤系統;它可以幫助收集時間數據,解決在microservice架構下的延遲問題;它管理這些數據的收集和查找;Zipkin的設計是基於谷歌的Google Dapper論文。
每個應用程序向Zipkin報告定時數據,Zipkin UI呈現了一個依賴圖表來展示多少跟蹤請求經過了每個應用程序;如果想解決延遲問題,可以過濾或者排序所有的跟蹤請求,並且可以查看每個跟蹤請求占總跟蹤時間的百分比。

為什么使用Zipkin
隨着業務越來越復雜,系統也隨之進行各種拆分,特別是隨着微服務架構和容器技術的興起,看似簡單的一個應用,后台可能有幾十個甚至幾百個服務在支撐;一個前端的請求可能需要多次的服務調用最后才能完成;當請求變慢或者不可用時,我們無法得知是哪個后台服務引起的,這時就需要解決如何快速定位服務故障點,Zipkin分布式跟蹤系統就能很好的解決這樣的問題。

Zipkin原理

針對服務化應用全鏈路追蹤的問題,Google發表了Dapper論文,介紹了他們如何進行服務追蹤分析。基本思路是在服務調用的請求和響應中加入ID,標明上下游請求的關系。利用這些信息,可以可視化地分析服務調用鏈路和服務間的依賴關系

對應Dpper的開源實現是Zipkin,支持多種語言包括JavaScript,Python,Java, Scala, Ruby, C#, Go等。其中Java由多種不同的庫來支持

Spring Cloud Sleuth是對Zipkin的一個封裝,對於Span、Trace等信息的生成、接入HTTP Request,以及向Zipkin Server發送采集信息等全部自動完成。Spring Cloud Sleuth的概念圖見上圖。

Zipkin架構

跟蹤器(Tracer)位於你的應用程序中,並記錄發生的操作的時間和元數據,提供了相應的類庫,對用戶的使用來說是透明的,收集的跟蹤數據稱為Span;將數據發送到Zipkin的儀器化應用程序中的組件稱為Reporter,Reporter通過幾種傳輸方式之一將追蹤數據發送到Zipkin收集器(collector),然后將跟蹤數據進行存儲(storage),由API查詢存儲以向UI提供數據。
架構圖如下:

1.Trace
Zipkin使用Trace結構表示對一次請求的跟蹤,一次請求可能由后台的若干服務負責處理,每個服務的處理是一個Span,Span之間有依賴關系,Trace就是樹結構的Span集合;

2.Span
每個服務的處理跟蹤是一個Span,可以理解為一個基本的工作單元,包含了一些描述信息:id,parentId,name,timestamp,duration,annotations等,例如:

{
      "traceId": "bd7a977555f6b982",
      "name": "get-traces",
      "id": "ebf33e1a81dc6f71",
      "parentId": "bd7a977555f6b982",
      "timestamp": 1458702548478000,
      "duration": 354374,
      "annotations": [
        {
          "endpoint": {
            "serviceName": "zipkin-query",
            "ipv4": "192.168.1.2",
            "port": 9411
          },
          "timestamp": 1458702548786000,
          "value": "cs"
        }
      ],
      "binaryAnnotations": [
        {
          "key": "lc",
          "value": "JDBCSpanStore",
          "endpoint": {
            "serviceName": "zipkin-query",
            "ipv4": "192.168.1.2",
            "port": 9411
          }
        }
      ]
}

traceId:標記一次請求的跟蹤,相關的Spans都有相同的traceId;
id:span id;
name:span的名稱,一般是接口方法的名稱;
parentId:可選的id,當前Span的父Span id,通過parentId來保證Span之間的依賴關系,如果沒有parentId,表示當前Span為根Span;
timestamp:Span創建時的時間戳,使用的單位是微秒(而不是毫秒),所有時間戳都有錯誤,包括主機之間的時鍾偏差以及時間服務重新設置時鍾的可能性,出於這個原因,Span應盡可能記錄其duration;
duration:持續時間使用的單位是微秒(而不是毫秒);
annotations:注釋用於及時記錄事件;有一組核心注釋用於定義RPC請求的開始和結束;

cs:Client Send,客戶端發起請求;
sr:Server Receive,服務器接受請求,開始處理;
ss:Server Send,服務器完成處理,給客戶端應答;
cr:Client Receive,客戶端接受應答從服務器;

binaryAnnotations:二進制注釋,旨在提供有關RPC的額外信息。

3.Transport
收集的Spans必須從被追蹤的服務運輸到Zipkin collector,有三個主要的傳輸方式:HTTP, Kafka和Scribe

4.Components
有4個組件組成Zipkin:collector,storage,search,web UI
collector:一旦跟蹤數據到達Zipkin collector守護進程,它將被驗證,存儲和索引,以供Zipkin收集器查找;
storage:Zipkin最初數據存儲在Cassandra上,因為Cassandra是可擴展的,具有靈活的模式,並在Twitter中大量使用;但是這個組件可插入,除了Cassandra之外,還支持ElasticSearch和MySQL;  存儲,zipkin默認的存儲方式為in-memory,即不會進行持久化操作。如果想進行收集數據的持久化,可以存儲數據在Cassandra,因為Cassandra是可擴展的,有一個靈活的模式,並且在Twitter中被大量使用,我們使這個組件可插入。除了Cassandra,我們原生支持ElasticSearch和MySQL。其他后端可能作為第三方擴展提供。
search:一旦數據被存儲和索引,我們需要一種方法來提取它。查詢守護進程提供了一個簡單的JSON API來查找和檢索跟蹤,主要給Web UI使用;
web UI:創建了一個GUI,為查看痕跡提供了一個很好的界面;Web UI提供了一種基於服務,時間和注釋查看跟蹤的方法。

 

Zipkin下載和啟動

有三種安裝方法:

Zipkin的使用比較簡單,官網有說明幾種方式: 
1、容器 
Docker Zipkin項目能夠建立docker鏡像,提供腳本和docker-compose.yml來啟動預構建的圖像。最快的開始是直接運行最新鏡像:

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

2、下載jar 
如果你有java 8或更高版本,上手最快的方法是把新版本作為一個獨立的可執行jar,Zipkin使用springboot來構建的:

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

3、下載源代碼運行 
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

1、使用官網自己打包好的Jar運行,Docker方式或下載源代碼自己打包Jar運行(因為zipkin使用了springboot,內置了服務器,所以可以直接使用jar運行)。zipkin推薦使用docker方式運行,我后面會專門寫一遍關於docker的運行方式,而源碼運行方式好處是有機會體驗到最新的功能特性,但是可能也會帶來一些比較詭異的坑,所以不做講解,下面我直接是使用官網打包的jar運行過程:
官方提供了三種方式來啟動,這里使用第二種方式來啟動;

wget -O zipkin.jar 'https://search.maven.org/remote_content?g=io.zipkin.java&a=zipkin-server&v=LATEST&c=exec'
java -jar zipkin.jar

首先下載zipkin.jar,我下載的是zipkin-server-2.10.2-exec.jar,然后直接使用-jar命令運行,要求jdk8以上版本;

D:\workspace\zipkin>java -jar zipkin-server-2.10.2-exec.jar
                                    ********
                                  **        **
                                 *            *
                                **            **
                                **            **
                                 **          **
                                  **        **
                                    ********
                                      ****
                                      ****
        ****                          ****
     ******                           ****                                 ***
  ****************************************************************************
    *******                           ****                                 ***
        ****                          ****
                                       **
                                       **


             *****      **     *****     ** **       **     **   **
               **       **     **  *     ***         **     **** **
              **        **     *****     ****        **     **  ***
             ******     **     **        **  **      **     **   **

:: Powered by Spring Boot ::         (v2.0.3.RELEASE)
...
2018-07-20 14:59:08.635  INFO 17284 --- [           main] o.xnio                                   : XNIO version 3.3.8.Final
2018-07-20 14:59:08.650  INFO 17284 --- [           main] o.x.nio                                  : XNIO NIO Implementation Version 3.3.8.Final
2018-07-20 14:59:08.727  INFO 17284 --- [           main] o.s.b.w.e.u.UndertowServletWebServer     : Undertow started on port(s) 9411 (http) with context path ''
2018-07-20 14:59:08.729  INFO 17284 --- [           main] z.s.ZipkinServer                         : Started ZipkinServer in 4.513 seconds (JVM running for 5.756)
2018-07-20 14:59:36.546  INFO 17284 --- [  XNIO-1 task-1] i.u.servlet                              : Initializing Spring FrameworkServlet 'dispatcherServlet'
2018-07-20 14:59:36.547  INFO 17284 --- [  XNIO-1 task-1] o.s.w.s.DispatcherServlet                : FrameworkServlet 'dispatcherServlet': initialization started
2018-07-20 14:59:36.563  INFO 17284 --- [  XNIO-1 task-1] o.s.w.s.DispatcherServlet                : FrameworkServlet 'dispatcherServlet': initialization completed in 15ms

(3)    查看運行效果

        通過上圖,我們發現zipkin使用springboot,並且啟動的端口為9411,然后我們通過瀏覽器訪問,效果如下:

詳細參考:https://zipkin.io/pages/quick...

 

六、入門示例

追蹤服務包含下面幾個服務:

1、注冊中心 Eureka Server(可選的,只用於服務生產者和調用者注冊)

2、Zipkin服務器

3、服務的生產者及服務的調用者:

1)服務的生產者、調用者是相對的,兩者之間可以互相調用,即可以同時作為生產者和調用者,兩者都是Eureka Client;

2)兩者都要注冊到注冊中心上,這樣才可以相互可見,才能通過服務名來調用指定服務,才能使用Feign或RestTemplate+Ribbon來達到負載均衡

3)兩者都要注冊到Zipkin服務器上,這樣Zipkin才能追蹤服務的調用鏈路

構建工程

基本知識講解完畢,下面我們來實戰,本文的案例主要有三個工程組成:一個server-zipkin,它的主要作用使用ZipkinServer 的功能,收集調用數據,並展示;一個service-hi,對外暴露hi接口;一個service-miya,對外暴露miya接口;這兩個service可以相互調用;並且只有調用了,server-zipkin才會收集數據的,這就是為什么叫服務追蹤了。

一、Zipkin服務器

代碼地址(碼雲):https://gitee.com/wudiyong/ZipkinServer.git

1、新建一個普通的Spring Boot項目,工程取名為server-zipkin,在其pom引入依賴:

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>

    <groupId>com.dxz.serverzipkin</groupId>
    <artifactId>serverzipkin</artifactId>
    <version>0.0.1-SNAPSHOT</version>
    <packaging>jar</packaging>

    <name>server-zipkin</name>
    <description>Demo project for Spring Boot</description>

    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>1.3.5.RELEASE</version>   <!--配合spring cloud版本 -->
        <relativePath /> <!-- lookup parent from repository -->
    </parent>
    <properties>
        <!--設置字符編碼及java版本 -->
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
        <java.version>1.8</java.version>
    </properties>
    <dependencies>
        <!--增加zipkin的依賴 -->
        <dependency>
            <groupId>io.zipkin.java</groupId>
            <artifactId>zipkin-server</artifactId>
        </dependency>

        <dependency>
            <groupId>io.zipkin.java</groupId>
            <artifactId>zipkin-autoconfigure-ui</artifactId>
        </dependency>
        <!--用於測試的,本例可省略 -->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>
    </dependencies>

    <!--依賴管理,用於管理spring-cloud的依賴 -->
    <dependencyManagement>
        <dependencies>
            <dependency>
                <groupId>org.springframework.cloud</groupId>
                <artifactId>spring-cloud-starter-parent</artifactId>
                <version>Brixton.SR3</version>   <!--官網為Angel.SR4版本,但是我使用的時候總是報錯 -->
                <type>pom</type>
                <scope>import</scope>
            </dependency>
        </dependencies>
    </dependencyManagement>

    <build>
        <plugins>
            <!--使用該插件打包 -->
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
            </plugin>
        </plugins>
    </build>
</project>

2在其程序入口類, 加上注解@EnableZipkinServer,開啟ZipkinServer的功能:

package com.dxz.serverzipkin;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

import zipkin.server.EnableZipkinServer;

@EnableZipkinServer
@SpringBootApplication
public class ServerZipkinApplication {

    public static void main(String[] args) {
        SpringApplication.run(ServerZipkinApplication.class, args);
    }
}

3在配置文件application.yml指定,配置Zipkin服務端口、名稱等:

server.port=9411
spring.application.name=my-zipkin-server
啟動后打開http://localhost:9411/可以看到
如下圖,什么內容都沒有,因為還沒有任何服務注冊到Zipkin,一旦有服務注冊到Zipkin便在Service Name下拉列表中可以看到服務名字,當有服務被調用,則可以在Span Name中看到被調用的接口名字.

這里為了測試方便,我們可以將數據保存到內存中,但是生產環境還是需要將數據持久化的,原生支持了很多產品,例如ES、數據庫等。

二服務生成者調用者

這兩者配置是一樣的此處簡化,直接修改compute-server和feign-consumer兩個服務,修改有兩點:

1、pom增加

        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-zipkin</artifactId>
        </dependency>

2、在其配置文件application.yml指定zipkin server的地址,頭通過配置“spring.zipkin.base-url”指定:

spring.zipkin.base-url=http://localhost:9411

至此,可以開始測試Zipkin追蹤服務了

 

三啟動工程,演示追蹤

啟動順序:注冊中心(可選)->配置中心(可選)->Zipkin服務器->服務生產者及調用者

我們可以嘗試調用生產者或調用者的接口,然后刷新Zipkin服務器頁面,可以看到如下結果:

依次啟動上面的三個工程,打開瀏覽器訪問:http://localhost:9411/,會出現以下界面:

再打開http://localhost:9411/的界面,點擊Dependencies,可以發現服務的依賴關系:

Paste_Image.png

點擊find traces,可以看到具體服務相互調用的數據:

可以看到,調用消費者(ribbon-consumer)耗時83ms,其中消費者調用生產者占了5ms,占比6%。

在測試的過程中我們會發現,有時候,程序剛剛啟動后,刷新幾次,並不能看到任何數據,原因就是我們的spring-cloud-sleuth收集信息是有一定的比率的,默認的采樣率是0.1,配置此值的方式在配置文件中增加spring.sleuth.sampler.percentage參數配置(如果不配置默認0.1),如果我們調大此值為1,可以看到信息收集就更及時。但是當這樣調整后,我們會發現我們的rest接口調用速度比0.1的情況下慢了很多,即使在0.1的采樣率下,我們多次刷新consumer的接口,會發現對同一個請求兩次耗時信息相差非常大,如果取消spring-cloud-sleuth后我們再測試,會發現並沒有這種情況,可以看到這種方式追蹤服務調用鏈路會給我們業務程序性能帶來一定的影響。

#sleuth采樣率,默認為0.1,值越大收集越及時,但性能影響也越大

spring.sleuth.sampler.percentage=1

 

其實,我們仔細想想也可以總結出這種方式的幾種缺陷:
缺陷1:zipkin客戶端向zipkin-server程序發送數據使用的是http的方式通信,每次發送的時候涉及到連接和發送過程。
缺陷2:當我們的zipkin-server程序關閉或者重啟過程中,因為客戶端收集信息的發送采用http的方式會被丟失。

針對以上兩個明顯的缺陷,改進的辦法是:
1、通信采用socket或者其他效率更高的通信方式。
2、客戶端數據的發送盡量減少業務線程的時間消耗,采用異步等方式發送收集信息。
3、客戶端與zipkin-server之間增加緩存類的中間件,例如redis、MQ等,在zipkin-server程序掛掉或重啟過程中,客戶端依舊可以正常的發送自己收集的信息。

相信采用以上三種方式會很大的提高我們的效率和可靠性。其實spring-cloud已經為我們提供采用MQ或redis等其他的采用socket方式通信,利用消息中間件或數據庫緩存的實現方式。

spring-cloud-sleuth-zipkin-stream方式的實現請看下面內容!

將HTTP通信改成MQ異步方式通信

springcloud官方按照傳輸方式分成了三種啟動服務端的方式:

  1. Sleuth with Zipkin via HTTP,
  2. Sleuth with Zipkin via Spring Cloud Stream,
  3. Spring Cloud Sleuth Stream Zipkin Collector。

只需要添加相應的依賴,之后配置相應的注解,如@EnableZipkinStreamServer即可。具體配置參考官方文檔:
http://cloud.spring.io/spring-cloud-static/spring-cloud-sleuth/1.2.1.RELEASE/#_adding_to_the_project

1、加入依賴

要將http方式改為通過MQ通信,我們要將依賴的原來依賴的io.zipkin.java:zipkin-server換成spring-cloud-sleuth-zipkin-stream和spring-cloud-starter-stream-rabbit

<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-sleuth-zipkin-stream</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-stream-rabbit</artifactId>
</dependency>

 

2、在啟動類中開啟Stream通信功能

package com.zipkinServer.ZipkinServer;
 
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.sleuth.zipkin.stream.EnableZipkinStreamServer;
import zipkin.server.EnableZipkinServer;
 
/*
* @EnableZipkinServer、@EnableZipkinStreamServer兩者二選一
* 通過源碼可看到,@EnableZipkinStreamServer包含了@EnableZipkinServer,同時
* 還創建了一個rabbit-mq的消息隊列監聽器,所以也支持原來的HTTP通信方式 
*/

//@EnableZipkinServer//默認采用HTTP通信方式啟動ZipkinServer
@EnableZipkinStreamServer//采用Stream通信方式啟動ZipkinServer,也支持HTTP通信方式
 
@SpringBootApplication
public class ZipkinServerApplication {
 
public static void main(String[] args) {
 
SpringApplication.run(ZipkinServerApplication.class, args);
 
}
 
}

 

3、配置消息中間件rabbit mq地址等信息

#連接rabbitmq服務器配置
spring.rabbitmq.host=localhost
spring.rabbitmq.port=5672
spring.rabbitmq.username=guest
spring.rabbitmq.password=guest

 

至此,ZipkinServer配置完成,下面是Zipkin客戶端的配置

1、將原來的spring-cloud-starter-zipkin替換為如下依賴即可

<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-sleuth-zipkin-stream</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-stream-rabbit</artifactId>
</dependency>
 
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-sleuth</artifactId>
</dependency>

 

2、此外,在配置文件中也加上連接MQ的配置

#連接rabbitmq服務器配置
spring.rabbitmq.host=localhost
spring.rabbitmq.port=5672
spring.rabbitmq.username=guest
spring.rabbitmq.password=guest

至此全部配置完成,可以開始測試。

另外,由於要連接到rabbitmq服務器,所以,還要安裝及啟動rabbitmq服務器!

加了MQ之后,通信過程如下圖所示:

可以看到如下效果:

1)請求的耗時時間不會出現突然耗時特長的情況

2)當ZipkinServer不可用時(比如關閉、網絡不通等),追蹤信息不會丟失,因為這些信息會保存在Rabbitmq服務器上,直到Zipkin服務器可用時,再從Rabbitmq中取出這段時間的信息

持久化到數據庫

Zipkin目前只支持mysql數據庫,ZipkinServer服務做如下修改,其它服務不需做任何修改

1、加入數據庫依賴

<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-jdbc</artifactId>
</dependency>

 

2、在application.properties中配置數據庫屬性

#zipkin數據保存到數據庫中需要進行如下配置
#表示當前程序不使用sleuth
spring.sleuth.enabled=false
#表示zipkin數據存儲方式是mysql
zipkin.storage.type=mysql
 
#數據庫腳本創建地址,當有多個時可使用[x]表示集合第幾個元素,腳本可到官網下載,需要先手動到數據庫執行
spring.datasource.schema[0]=classpath:/zipkin.sql
 
#spring boot數據源配置
spring.datasource.url=jdbc:mysql://localhost:3306/zipkin?autoReconnect=true&useUnicode=true&characterEncoding=UTF-8&zeroDateTimeBehavior=convertToNull&useSSL=false
spring.datasource.username=root
spring.datasource.password=123456
spring.datasource.driver-class-name=com.mysql.jdbc.Driver
spring.datasource.initialize=true
spring.datasource.continue-on-error=true

 

3、zipkin.sql

數據庫腳本文件放到resources目錄下,且需要先手動到數據庫執行一次,內容如下:

CREATE TABLE IF NOT EXISTS zipkin_spans (
 
`trace_id_high` BIGINT NOT NULL DEFAULT 0 COMMENT 'If non zero, this means the trace uses 128 bit traceIds instead of 64 bit',
 
`trace_id` BIGINT NOT NULL,
 
`id` BIGINT NOT NULL,
 
`name` VARCHAR(255) NOT NULL,
 
`parent_id` BIGINT,
 
`debug` BIT(1),
 
`start_ts` BIGINT COMMENT 'Span.timestamp(): epoch micros used for endTs query and to implement TTL',
 
`duration` BIGINT COMMENT 'Span.duration(): micros used for minDuration and maxDuration query'
 
) ENGINE=InnoDB ROW_FORMAT=COMPRESSED CHARACTER SET=utf8 COLLATE utf8_general_ci;
 
 
 
ALTER TABLE zipkin_spans ADD UNIQUE KEY(`trace_id_high`, `trace_id`, `id`) COMMENT 'ignore insert on duplicate';
 
ALTER TABLE zipkin_spans ADD INDEX(`trace_id_high`, `trace_id`, `id`) COMMENT 'for joining with zipkin_annotations';
 
ALTER TABLE zipkin_spans ADD INDEX(`trace_id_high`, `trace_id`) COMMENT 'for getTracesByIds';
 
ALTER TABLE zipkin_spans ADD INDEX(`name`) COMMENT 'for getTraces and getSpanNames';
 
ALTER TABLE zipkin_spans ADD INDEX(`start_ts`) COMMENT 'for getTraces ordering and range';
 
 
 
CREATE TABLE IF NOT EXISTS zipkin_annotations (
 
`trace_id_high` BIGINT NOT NULL DEFAULT 0 COMMENT 'If non zero, this means the trace uses 128 bit traceIds instead of 64 bit',
 
`trace_id` BIGINT NOT NULL COMMENT 'coincides with zipkin_spans.trace_id',
 
`span_id` BIGINT NOT NULL COMMENT 'coincides with zipkin_spans.id',
 
`a_key` VARCHAR(255) NOT NULL COMMENT 'BinaryAnnotation.key or Annotation.value if type == -1',
 
`a_value` BLOB COMMENT 'BinaryAnnotation.value(), which must be smaller than 64KB',
 
`a_type` INT NOT NULL COMMENT 'BinaryAnnotation.type() or -1 if Annotation',
 
`a_timestamp` BIGINT COMMENT 'Used to implement TTL; Annotation.timestamp or zipkin_spans.timestamp',
 
`endpoint_ipv4` INT COMMENT 'Null when Binary/Annotation.endpoint is null',
 
`endpoint_ipv6` BINARY(16) COMMENT 'Null when Binary/Annotation.endpoint is null, or no IPv6 address',
 
`endpoint_port` SMALLINT COMMENT 'Null when Binary/Annotation.endpoint is null',
 
`endpoint_service_name` VARCHAR(255) COMMENT 'Null when Binary/Annotation.endpoint is null'
 
) ENGINE=InnoDB ROW_FORMAT=COMPRESSED CHARACTER SET=utf8 COLLATE utf8_general_ci;
 
 
 
ALTER TABLE zipkin_annotations ADD UNIQUE KEY(`trace_id_high`, `trace_id`, `span_id`, `a_key`, `a_timestamp`) COMMENT 'Ignore insert on duplicate';
 
ALTER TABLE zipkin_annotations ADD INDEX(`trace_id_high`, `trace_id`, `span_id`) COMMENT 'for joining with zipkin_spans';
 
ALTER TABLE zipkin_annotations ADD INDEX(`trace_id_high`, `trace_id`) COMMENT 'for getTraces/ByIds';
 
ALTER TABLE zipkin_annotations ADD INDEX(`endpoint_service_name`) COMMENT 'for getTraces and getServiceNames';
 
ALTER TABLE zipkin_annotations ADD INDEX(`a_type`) COMMENT 'for getTraces';
 
ALTER TABLE zipkin_annotations ADD INDEX(`a_key`) COMMENT 'for getTraces';
 
ALTER TABLE zipkin_annotations ADD INDEX(`trace_id`, `span_id`, `a_key`) COMMENT 'for dependencies job';
 
 
 
CREATE TABLE IF NOT EXISTS zipkin_dependencies (
 
`day` DATE NOT NULL,
 
`parent` VARCHAR(255) NOT NULL,
 
`child` VARCHAR(255) NOT NULL,
 
`call_count` BIGINT
 
) ENGINE=InnoDB ROW_FORMAT=COMPRESSED CHARACTER SET=utf8 COLLATE utf8_general_ci;
 
 
 
ALTER TABLE zipkin_dependencies ADD UNIQUE KEY(`day`, `parent`, `child`);

 

至此,ZipkinServer采用數據庫存儲配置完成。

測試時發現,要用MQ異步方式通信的pom.xml配置及@EnableZipkinStreamServer注解才可以(@EnableZipkinServer貌似只能保存到內存),否則啟動報錯,不明白為什么。

elasticsearch存儲

前面講了利用mq的方式發送數據,存儲在mysql,實際生產過程中調用數據量非常的大,mysql存儲並不是很好的選擇,這時我們可以采用elasticsearch進行存儲

配置過程也很簡單

1、mysql依賴改成elasticsearch依賴

<!-- 添加 spring-data-elasticsearch的依賴 -->
<dependency>
<groupId>io.zipkin.java</groupId>
<artifactId>zipkin-autoconfigure-storage-elasticsearch-http</artifactId>
<version>1.24.0</version>
<optional>true</optional>
</dependency>

 

2、數據庫配置改成elasticsearch配置

#表示當前程序不使用sleuth
spring.sleuth.enabled=false
#表示zipkin數據存儲方式是elasticsearch
zipkin.storage.StorageComponent = elasticsearch
zipkin.storage.type=elasticsearch
zipkin.storage.elasticsearch.cluster=elasticsearch-zipkin-cluster
zipkin.storage.elasticsearch.hosts=127.0.0.1:9300
# zipkin.storage.elasticsearch.pipeline=
zipkin.storage.elasticsearch.max-requests=64
zipkin.storage.elasticsearch.index=zipkin
zipkin.storage.elasticsearch.index-shards=5
zipkin.storage.elasticsearch.index-replicas=1

3、安裝elasticsearch

其它代碼完全不變

具體見:

http://www.cnblogs.com/shunyang/p/7011306.html

http://www.cnblogs.com/shunyang/p/7298005.html

https://segmentfault.com/a/1190000012342007

https://blog.csdn.net/meiliangdeng1990/article/details/54131384


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM