Spring Cloud 參考文檔(Spring Cloud Sleuth介紹)

Spring Cloud Sleuth介紹

Spring Cloud Sleuth爲Spring Cloud實現了分佈式追蹤解決方案。css

術語

Spring Cloud Sleuth借用了Dapper的術語。html

Span:基本工做單元,例如,發送RPC是一個新的span,就像向RPC發送響應同樣,Span由span的惟一64位ID標識,另外一個64位ID標識其所屬的Trace。Span還有其餘數據,例如描述、帶時間戳的事件、鍵值annotations(標籤),致使它們的span的ID以及進程ID(一般是IP地址)。git

span能夠啓動和中止,它們能夠追蹤本身的時間信息,建立span後,必須在未來的某個時刻中止它。github

啓動Trace的初始span稱爲 root span,該span的ID值等於trace ID。

Trace:一組span造成的樹狀結構,例如,若是運行分佈式大數據存儲,則可能由PUT請求造成trace。web

Annotation:用於及時記錄事件的存在,使用Brave工具,再也不須要爲Zipkin設置特殊的事件來了解客戶端和服務器是誰、請求在哪裏開始以及在哪裏結束,然而,出於學習目的,標記這些事件以突出發生了什麼類型的操做。spring

  • cs:Client Sent,客戶端發起了一個請求,這個annotation表示span的開始。
  • sr:Server Received,服務器端得到了請求並開始處理它,今後時間戳中減去cs時間戳會顯示網絡延遲。
  • ss:Server Sent,在請求處理完成時註釋(當響應被髮送回客戶端時),今後時間戳中減去sr時間戳會顯示服務器端處理請求所需的時間。
  • cr:Client Received,表示span的結束,客戶端已成功從服務器端收到響應,今後時間戳中減去cs時間戳會顯示客戶端從服務器接收響應所需的所有時間。

下圖顯示了系統中的SpanTrace,以及Zipkin annotations:json

trace-id.png

標記的每種顏色表示一個span(有七個span — 從AG),請考慮如下標記:bootstrap

Trace Id = X
Span Id = D
Client Sent

此標記表示當前span的Trace Id設置爲XSpan Id設置爲D,此外,還發生了Client Sent事件。服務器

下圖顯示了span的父—子關係:網絡

parents.png

用途

如下部分參考上圖中顯示的示例。

使用Zipkin進行分佈式追蹤

這個例子有七個span,若是你進入Zipkin中的trace,你能夠在第二個trace中看到這個數字,以下圖所示:

zipkin-traces.png

可是,若是選擇特定trace,則能夠看到四個span,以下圖所示:

zipkin-ui.png

選擇特定trace時,你會看到合併的span,這意味着,若是經過Server Received和Server Sent或Client Received和Client Sent annotations向Zipkin發送了兩個span,則它們將顯示爲單個span。

在這種狀況下,爲何七個和四個span之間存在差別?

  • 一個span來自http:/start span,它具備Server Received(sr)和Server Sent(ss)annotations。
  • 兩個span來自從service1service2http:/foo端點的RPC調用,Client Sent(cs)和Client Received(cr)事件發生在service1端,Server Received(sr)和Server Sent(ss)事件發生在service2端,這兩個span造成一個與RPC調用相關的邏輯span。
  • 兩個span來自從service2service3http:/bar端點的RPC調用,Client Sent(cs)和Client Received(cr)事件發生在service2端,Server Received(sr)和Server Sent(ss)事件發生在service3端,這兩個span造成一個與RPC調用相關的邏輯span。
  • 兩個span來自從service2service4http:/baz端點的RPC調用,Client Sent(cs)和Client Received(cr)事件發生在service2端,Server Received(sr)和Server Sent(ss)事件發生在service4端,這兩個span造成一個與RPC調用相關的邏輯span。

所以,若是咱們計算物理span,咱們有一個來自http:/start,兩個來自service1調用service2,兩個來自service2調用service3,兩個來自service2調用service4,總之,咱們總共有七個span。

從邏輯上講,咱們看到四個總Span的信息,由於咱們有一個與傳入請求到service1相關的span和三個與RPC調用相關的span。

可視化錯誤

Zipkin容許你可視化trace中的錯誤,當拋出異常而沒有被捕獲時,在span上設置了適當的標籤,而後Zipkin能夠正確地着色,你能夠在trace列表中看到一條紅色的trace,出現這種狀況是由於拋出了異常。

若是單擊該trace,則會看到相似的圖片,以下所示:

zipkin-error-traces.png

若是你隨後單擊其中一個span,則會看到如下內容:

zipkin-error-trace-screenshot.png

span顯示錯誤的緣由以及與之相關的整個堆棧追蹤。

Brave的分佈式追蹤

從版本2.0.0開始,Spring Cloud Sleuth使用Brave做爲追蹤庫,所以,Sleuth再也不負責存儲上下文,而是將該工做委託給Brave。

因爲Sleuth與Brave有不一樣的命名和標記約定,Spring決定從如今開始遵循Brave的約定,可是,若是要使用遺留的Sleuth方法,能夠將spring.sleuth.http.legacy.enabled屬性設置爲true

實例

pws.png

點擊這裏查看實例!

Zipkin中的依賴關係圖應相似於如下圖像:

dependencies.png

日誌相關

當使用grep經過掃描等於(例如)2485ec27856c56f4的trace ID來讀取這四個應用程序的日誌時,你將得到相似於如下內容的輸出:

service1.log:2016-02-26 11:15:47.561  INFO [service1,2485ec27856c56f4,2485ec27856c56f4,true] 68058 --- [nio-8081-exec-1] i.s.c.sleuth.docs.service1.Application   : Hello from service1. Calling service2
service2.log:2016-02-26 11:15:47.710  INFO [service2,2485ec27856c56f4,9aa10ee6fbde75fa,true] 68059 --- [nio-8082-exec-1] i.s.c.sleuth.docs.service2.Application   : Hello from service2. Calling service3 and then service4
service3.log:2016-02-26 11:15:47.895  INFO [service3,2485ec27856c56f4,1210be13194bfe5,true] 68060 --- [nio-8083-exec-1] i.s.c.sleuth.docs.service3.Application   : Hello from service3
service2.log:2016-02-26 11:15:47.924  INFO [service2,2485ec27856c56f4,9aa10ee6fbde75fa,true] 68059 --- [nio-8082-exec-1] i.s.c.sleuth.docs.service2.Application   : Got response from service3 [Hello from service3]
service4.log:2016-02-26 11:15:48.134  INFO [service4,2485ec27856c56f4,1b1845262ffba49d,true] 68061 --- [nio-8084-exec-1] i.s.c.sleuth.docs.service4.Application   : Hello from service4
service2.log:2016-02-26 11:15:48.156  INFO [service2,2485ec27856c56f4,9aa10ee6fbde75fa,true] 68059 --- [nio-8082-exec-1] i.s.c.sleuth.docs.service2.Application   : Got response from service4 [Hello from service4]
service1.log:2016-02-26 11:15:48.182  INFO [service1,2485ec27856c56f4,2485ec27856c56f4,true] 68058 --- [nio-8081-exec-1] i.s.c.sleuth.docs.service1.Application   : Got response from service2 [Hello from service2, response from service3 [Hello from service3] and from service4 [Hello from service4]]

若是你使用日誌聚合工具(例如KibanaSplunk等),你能夠排序發生的事件,Kibana的一個例子相似於下圖:

kibana.png

若是要使用Logstash,如下列表顯示Logstash的Grok模式:

filter {
       # pattern matching logback pattern
       grok {
              match => { "message" => "%{TIMESTAMP_ISO8601:timestamp}\s+%{LOGLEVEL:severity}\s+\[%{DATA:service},%{DATA:trace},%{DATA:span},%{DATA:exportable}\]\s+%{DATA:pid}\s+---\s+\[%{DATA:thread}\]\s+%{DATA:class}\s+:\s+%{GREEDYDATA:rest}" }
       }
}
若是要將Grok與Cloud Foundry中的日誌一塊兒使用,則必須使用如下模式:
filter {
       # pattern matching logback pattern
       grok {
              match => { "message" => "(?m)OUT\s+%{TIMESTAMP_ISO8601:timestamp}\s+%{LOGLEVEL:severity}\s+\[%{DATA:service},%{DATA:trace},%{DATA:span},%{DATA:exportable}\]\s+%{DATA:pid}\s+---\s+\[%{DATA:thread}\]\s+%{DATA:class}\s+:\s+%{GREEDYDATA:rest}" }
       }
}

JSON化Logback與Logstash一塊兒使用

一般,你不但願將日誌存儲在文本文件中,而是存儲在Logstash能夠當即選擇的JSON文件中,爲此,你必須執行如下操做(爲了便於閱讀,在groupId:artifactId:version notation中傳遞依賴項)。

依賴關係設置

  1. 確保Logback位於類路徑上(ch.qos.logback:logback-core)。
  2. 添加Logstash Logback encode,例如,要使用版本4.6,請添加net.logstash.logback:logstash-logback-encoder:4.6

Logback設置

請考慮如下Logback配置文件示例(名爲logback-spring.xml)。

<?xml version="1.0" encoding="UTF-8"?>
<configuration>
    <include resource="org/springframework/boot/logging/logback/defaults.xml"/>
    ​
    <springProperty scope="context" name="springAppName" source="spring.application.name"/>
    <!-- Example for logging into the build folder of your project -->
    <property name="LOG_FILE" value="${BUILD_FOLDER:-build}/${springAppName}"/>​

    <!-- You can override this to have a custom pattern -->
    <property name="CONSOLE_LOG_PATTERN"
              value="%clr(%d{yyyy-MM-dd HH:mm:ss.SSS}){faint} %clr(${LOG_LEVEL_PATTERN:-%5p}) %clr(${PID:- }){magenta} %clr(---){faint} %clr([%15.15t]){faint} %clr(%-40.40logger{39}){cyan} %clr(:){faint} %m%n${LOG_EXCEPTION_CONVERSION_WORD:-%wEx}"/>

    <!-- Appender to log to console -->
    <appender name="console" class="ch.qos.logback.core.ConsoleAppender">
        <filter class="ch.qos.logback.classic.filter.ThresholdFilter">
            <!-- Minimum logging level to be presented in the console logs-->
            <level>DEBUG</level>
        </filter>
        <encoder>
            <pattern>${CONSOLE_LOG_PATTERN}</pattern>
            <charset>utf8</charset>
        </encoder>
    </appender>

    <!-- Appender to log to file -->​
    <appender name="flatfile" class="ch.qos.logback.core.rolling.RollingFileAppender">
        <file>${LOG_FILE}</file>
        <rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
            <fileNamePattern>${LOG_FILE}.%d{yyyy-MM-dd}.gz</fileNamePattern>
            <maxHistory>7</maxHistory>
        </rollingPolicy>
        <encoder>
            <pattern>${CONSOLE_LOG_PATTERN}</pattern>
            <charset>utf8</charset>
        </encoder>
    </appender>
    ​
    <!-- Appender to log to file in a JSON format -->
    <appender name="logstash" class="ch.qos.logback.core.rolling.RollingFileAppender">
        <file>${LOG_FILE}.json</file>
        <rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
            <fileNamePattern>${LOG_FILE}.json.%d{yyyy-MM-dd}.gz</fileNamePattern>
            <maxHistory>7</maxHistory>
        </rollingPolicy>
        <encoder class="net.logstash.logback.encoder.LoggingEventCompositeJsonEncoder">
            <providers>
                <timestamp>
                    <timeZone>UTC</timeZone>
                </timestamp>
                <pattern>
                    <pattern>
                        {
                        "severity": "%level",
                        "service": "${springAppName:-}",
                        "trace": "%X{X-B3-TraceId:-}",
                        "span": "%X{X-B3-SpanId:-}",
                        "parent": "%X{X-B3-ParentSpanId:-}",
                        "exportable": "%X{X-Span-Export:-}",
                        "pid": "${PID:-}",
                        "thread": "%thread",
                        "class": "%logger{40}",
                        "rest": "%message"
                        }
                    </pattern>
                </pattern>
            </providers>
        </encoder>
    </appender>
    ​
    <root level="INFO">
        <appender-ref ref="console"/>
        <!-- uncomment this to have also JSON logs -->
        <!--<appender-ref ref="logstash"/>-->
        <!--<appender-ref ref="flatfile"/>-->
    </root>
</configuration>

那個Logback配置文件:

  • 未來自應用程序的信息以JSON格式記錄到build/${spring.application.name}.json文件中。
  • 已註釋掉兩個額外的appender:控制檯和標準日誌文件。
  • 具備與上一節中介紹的相同的日誌記錄模式。
若是使用自定義 logback-spring.xml,則必須在 bootstrap而不是 application屬性文件中傳遞 spring.application.name,不然,你的自定義logback文件沒法正確讀取該屬性。

傳播span上下文

span上下文是必須傳播到跨進程邊界的任何子span的狀態,span上下文的一部分是Baggage,trace和span ID是span上下文的必需部分,Baggage是可選部分。

Baggage是一組存儲在span上下文中的鍵:值對,Baggage隨着trace一塊兒移動,並附在每個span上,Spring Cloud Sleuth瞭解若是HTTP header以baggage-爲前綴,則標題與行李相關,而且對於消息,它以baggage_開頭。

目前對baggage條目的數量或大小沒有限制,可是,請記住,太多可能會下降系統吞吐量或增長RPC延遲,在極端狀況下,因爲超出傳輸級別的消息或header容量,過多的baggage可能會使應用程序崩潰。

如下示例顯示了在span上設置baggage:

Span initialSpan = this.tracer.nextSpan().name("span").start();
ExtraFieldPropagation.set(initialSpan.context(), "foo", "bar");
ExtraFieldPropagation.set(initialSpan.context(), "UPPER_CASE", "someValue");

Baggage與Span標籤

Baggage隨trace而行(每一個子span包含其父級的baggage),Zipkin不知道baggage,也不接收這些信息。

從Sleuth 2.0.0開始,你必須在項目配置中明確傳遞baggage鍵名稱。

標籤附加到特定span,換句話說,它們僅針對特定span呈現,可是,你能夠按標記搜索以查找trace,假設存在具備搜索標記值的span。

若是你但願可以根據baggage查找span,則應在根span中添加相應的條目做爲標記。

span必須在scope內。

如下清單顯示了使用baggage的集成測試:

設置

spring.sleuth:
  baggage-keys:
    - baz
    - bizarrecase
  propagation-keys:
    - foo
    - upper_case

代碼

initialSpan.tag("foo",
        ExtraFieldPropagation.get(initialSpan.context(), "foo"));
initialSpan.tag("UPPER_CASE",
        ExtraFieldPropagation.get(initialSpan.context(), "UPPER_CASE"));

添加Sleuth到項目

本節介紹如何使用Maven或Gradle將Sleuth添加到項目中。

要確保你的應用程序名稱在Zipkin中正確顯示,請在 bootstrap.yml中設置 spring.application.name屬性。

只有Sleuth(log相關)

若是你想在沒有Zipkin集成的狀況下僅使用Spring Cloud Sleuth,請將spring-cloud-starter-sleuth模塊添加到你的項目中。

如下示例顯示如何使用Maven添加Sleuth:

Maven

<dependencyManagement>
      <dependencies>
          <dependency>
              <groupId>org.springframework.cloud</groupId>
              <artifactId>spring-cloud-dependencies</artifactId>
              <version>${release.train.version}</version>
              <type>pom</type>
              <scope>import</scope>
          </dependency>
      </dependencies>
</dependencyManagement>

<dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-starter-sleuth</artifactId>
</dependency>
  1. 建議你經過Spring BOM添加依賴關係管理,這樣你就無需自行管理版本。
  2. 添加依賴spring-cloud-starter-sleuth

如下示例顯示如何使用Gradle添加Sleuth:

Gradle

dependencyManagement {
    imports {
        mavenBom "org.springframework.cloud:spring-cloud-dependencies:${releaseTrainVersion}"
    }
}

dependencies {
    compile "org.springframework.cloud:spring-cloud-starter-sleuth"
}
  1. 建議你經過Spring BOM添加依賴關係管理,這樣你就無需自行管理版本。
  2. 添加依賴spring-cloud-starter-sleuth

經過HTTP Sleuth與Zipkin一塊兒使用

若是你想要Sleuth和Zipkin,請添加spring-cloud-starter-zipkin依賴項。

如下示例顯示瞭如何爲Maven執行此操做:

Maven

<dependencyManagement>
      <dependencies>
          <dependency>
              <groupId>org.springframework.cloud</groupId>
              <artifactId>spring-cloud-dependencies</artifactId>
              <version>${release.train.version}</version>
              <type>pom</type>
              <scope>import</scope>
          </dependency>
      </dependencies>
</dependencyManagement>

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

如下示例顯示瞭如何爲Gradle執行此操做:

Gradle

dependencyManagement {
    imports {
        mavenBom "org.springframework.cloud:spring-cloud-dependencies:${releaseTrainVersion}"
    }
}

dependencies {
    compile "org.springframework.cloud:spring-cloud-starter-zipkin"
}

在RabbitMQ或Kafka上Sleuth與Zipkin一塊兒使用

若是你想使用RabbitMQ或Kafka而不是HTTP,添加spring-rabbitspring-kafka依賴項,默認目標名稱是zipkin

若是使用Kafka,則必須相應地設置屬性spring.zipkin.sender.type屬性:

spring.zipkin.sender.type: kafka
spring-cloud-sleuth-stream已棄用且與這些目標不兼容。

若是你想在RabbitMQ上使用Sleuth,請添加spring-cloud-starter-zipkinspring-rabbit依賴項。

如下示例顯示瞭如何爲Maven執行此操做:

Maven

<dependencyManagement>
      <dependencies>
          <dependency>
              <groupId>org.springframework.cloud</groupId>
              <artifactId>spring-cloud-dependencies</artifactId>
              <version>${release.train.version}</version>
              <type>pom</type>
              <scope>import</scope>
          </dependency>
      </dependencies>
</dependencyManagement>

<dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-starter-zipkin</artifactId>
</dependency>
<dependency>
    <groupId>org.springframework.amqp</groupId>
    <artifactId>spring-rabbit</artifactId>
</dependency>
  1. 建議你經過Spring BOM添加依賴關係管理,這樣你就無需自行管理版本。
  2. 添加依賴spring-cloud-starter-zipkin,這樣,全部嵌套的依賴項都會被下載。
  3. 要自動配置RabbitMQ,請添加spring-rabbit依賴項。

Gradle

dependencyManagement {
    imports {
        mavenBom "org.springframework.cloud:spring-cloud-dependencies:${releaseTrainVersion}"
    }
}

dependencies {
    compile "org.springframework.cloud:spring-cloud-starter-zipkin" 2
    compile "org.springframework.amqp:spring-rabbit" 3
}

覆蓋Zipkin的自動配置

Spring Cloud Sleuth從2.1.0版開始支持向多個追蹤系統發送trace,爲了使其工做,每一個追蹤系統都須要有一個Reporter<Span>Sender,若是要覆蓋提供的bean,則須要爲它們指定一個特定的名稱,爲此,你能夠分別使用ZipkinAutoConfiguration.REPORTER_BEAN_NAMEZipkinAutoConfiguration.SENDER_BEAN_NAME

@Configuration
protected static class MyConfig {

    @Bean(ZipkinAutoConfiguration.REPORTER_BEAN_NAME)
    Reporter<zipkin2.Span> myReporter() {
        return AsyncReporter.create(mySender());
    }

    @Bean(ZipkinAutoConfiguration.SENDER_BEAN_NAME)
    MySender mySender() {
        return new MySender();
    }

    static class MySender extends Sender {

        private boolean spanSent = false;

        boolean isSpanSent() {
            return this.spanSent;
        }

        @Override
        public Encoding encoding() {
            return Encoding.JSON;
        }

        @Override
        public int messageMaxBytes() {
            return Integer.MAX_VALUE;
        }

        @Override
        public int messageSizeInBytes(List<byte[]> encodedSpans) {
            return encoding().listSizeInBytes(encodedSpans);
        }

        @Override
        public Call<Void> sendSpans(List<byte[]> encodedSpans) {
            this.spanSent = true;
            return Call.create(null);
        }

    }

}

額外的資源

你能夠點擊這裏觀看Reshmi KrishnaMarcin Grzejszczak關於Spring Cloud Sleuth和Zipkin的視頻。

你能夠在openzipkin/sleuth-webmvc-example存儲庫中檢查Sleuth和Brave的不一樣設置。

相關文章
相關標籤/搜索