第九篇 : 服務鏈路追蹤(Spring Cloud Sleuth)

第九篇 : 服務鏈路追蹤(Spring Cloud Sleuth)

一、簡介

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

二、服務追蹤分析

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


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


三、術語

  • 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註解的過程圖形化:

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

四、構建工程

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

1. 構建server-zipkin

在spring Cloud爲F版本的時候,已經不需要自己構建Zipkin Server了,只需要下載jar即可,下載地址:
https://dl.bintray.com/openzipkin/maven/io/zipkin/java/zipkin-server/

下載完成jar 包之後,需要運行jar,如下: 

java -jar zipkin-server-2.9.4-exec.jar

訪問瀏覽器localhost:9411,顯示如下界面: 

2. 創建 eureka-server

1. pom.xml

<?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.gf</groupId>
    <artifactId>eureka-server</artifactId>
    <version>0.0.1-SNAPSHOT</version>
    <packaging>jar</packaging>

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

    <parent>
        <groupId>com.gf</groupId>
        <artifactId>chapter09</artifactId>
        <version>0.0.1-SNAPSHOT</version>
    </parent>

    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-netflix-eureka-server</artifactId>
        </dependency>

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>
    </dependencies>

</project>

2. application.yml

# eureka.client.registerWithEureka :表示是否將自己註冊到Eureka Server,默認爲true。由於當前這個應用就是Eureka Server,故而設爲false
# eureka.client.fetchRegistry :表示是否從Eureka Server獲取註冊信息,默認爲true。因爲這是一個單點的Eureka Server,不需要同步其他的Eureka Server節點的數據,故而設爲false。
# eureka.client.serviceUrl.defaultZone :設置與Eureka Server交互的地址,查詢服務和註冊服務都需要依賴這個地址。默認是http://localhost:8761/eureka ;多個地址可使用 , 分隔。

server:
  port: 8761
eureka:
  instance:
    hostname: localhost
  client:
    register-with-eureka: false
    fetch-registry: false
    service-url:
      defaultZone: http://${eureka.instance.hostname}:/${server.port}/eureka/
spring:
  application:
    name: eureka-server

3. 主啓動類 EurekaServerApplication

package com.gf;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.netflix.eureka.server.EnableEurekaServer;

@EnableEurekaServer
@SpringBootApplication
public class EurekaServerApplication {    public static void main(String[] args) {        SpringApplication.run(EurekaServerApplication.class, args);    }

}

3. 創建 service-hi

1. pom.xml

在其pom引入起步依賴spring-cloud-starter-zipkin,代碼如下:

<?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.gf</groupId>
    <artifactId>service-hi</artifactId>
    <version>0.0.1-SNAPSHOT</version>
    <packaging>jar</packaging>

    <name>service-hi</name>
    <description>Demo project for Spring Boot</description>

    <parent>
        <groupId>com.gf</groupId>
        <artifactId>chapter09</artifactId>
        <version>0.0.1-SNAPSHOT</version>
    </parent>

    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-netflix-eureka-server</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-ribbon</artifactId>
        </dependency>

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

    </dependencies>

</project>

2. application.yml

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

server:
  port: 8988

spring:
  application:
    name: service-hi
  zipkin:
    base-url: http://localhost:9411
eureka:
  client:
    service-url:
      defaultZone: http://localhost:8761/eureka/

3. 主啓動類 ServiceHiApplication

package com.gf;

import brave.sampler.Sampler;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.client.loadbalancer.LoadBalanced;
import org.springframework.cloud.netflix.eureka.EnableEurekaClient;
import org.springframework.context.annotation.Bean;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.client.RestTemplate;

@EnableEurekaClient
@SpringBootApplication
@RestController
public class ServiceHiApplication {    private static final Logger logger = LoggerFactory.getLogger( ServiceHiApplication.class );    @Autowired    private RestTemplate restTemplate;    public static void main(String[] args) {        SpringApplication.run(ServiceHiApplication.class, args);    }    @GetMapping("/hi")    public String hi() {        logger.info( "calling trace service-hi" );        return restTemplate.getForObject( "http://SERVICE-MIYA/miya" , String.class );    }    @GetMapping("/info")    public String info() {        logger.info( "calling trace service-hi " );        return "i'm service-hi";    }    @LoadBalanced    @Bean    RestTemplate restTemplate(){        return new RestTemplate();    }    /**     * 採樣率:         在生成環境中,由於業務量比較大,所產生的跟蹤數據可能會非常大,如果全部採集一是對業務有一定影響,二是對存儲壓力也會比較大,所以採樣變的很重要。         一般來說,我們也不需要把每一個發生的動作都進行記錄。         Spring Cloud Sleuth有一個Sampler策略,可以通過這個實現類來控制採樣算法。         採樣器不會阻礙span相關id的產生,但是會對導出以及附加事件標籤的相關操作造成影響。         Sleuth默認採樣算法的實現是Reservoir sampling,具體的實現類是PercentageBasedSampler,默認的採樣比例爲: 0.1(即10%)。         不過我們可以通過spring.sleuth.sampler.percentage來設置,所設置的值介於0.0到1.0之間,1.0則表示全部採集。         也可以通過實現bean的方式來設置採樣爲全部採樣(AlwaysSampler)或者不採樣(NeverSampler):如     */    @Bean    public Sampler defaultSampler() {        return Sampler.ALWAYS_SAMPLE;    }

}

4. 創建service-miya

1. pom.xml

<?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.gf</groupId>
    <artifactId>service-miya</artifactId>
    <version>0.0.1-SNAPSHOT</version>
    <packaging>jar</packaging>

    <name>service-miya</name>
    <description>Demo project for Spring Boot</description>

    <parent>
        <groupId>com.gf</groupId>
        <artifactId>chapter09</artifactId>
        <version>0.0.1-SNAPSHOT</version>
    </parent>

    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-netflix-eureka-server</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-zipkin</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-ribbon</artifactId>
        </dependency>

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>
    </dependencies>

</project>

2. application.yml

server:
  port: 8989

spring:
  application:
    name: service-miya
  zipkin:
    base-url: http://localhost:9411
eureka:
  client:
    service-url:
      defaultZone: http://localhost:8761/eureka/

3. 主啓動類 ServiceMiyaApplication

package com.gf;

import brave.sampler.Sampler;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.client.loadbalancer.LoadBalanced;
import org.springframework.cloud.netflix.eureka.EnableEurekaClient;
import org.springframework.context.annotation.Bean;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.client.RestTemplate;

@SpringBootApplication
@EnableEurekaClient
@RestController
public class ServiceMiyaApplication {    private static final Logger logger = LoggerFactory.getLogger( ServiceMiyaApplication.class );    @Autowired    private RestTemplate restTemplate;    public static void main(String[] args) {        SpringApplication.run(ServiceMiyaApplication.class, args);    }    @GetMapping("/hi")    public String hi() {        logger.info( "hi is being called" );        return "hi i'm miya";    }    @GetMapping("/miya")    public String info() {        logger.info( "info is being called" );        return restTemplate.getForObject( "http://SERVICE-HI/info" , String.class );    }    @LoadBalanced    @Bean    RestTemplate restTemplate(){        return new RestTemplate();    }    @Bean    public Sampler defaultSampler() {        return Sampler.ALWAYS_SAMPLE;    }

}

五、啓動工程,演示追蹤

啓動上面的工程,訪問:http://localhost:8989/miya,瀏覽器出現: 

i'm service-hi

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

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

可以看到,Zipkin已經獲取到幾次服務的調用跟蹤信息了。我們可以點擊其中的一個請求,可以看到如下界面:

在該界面中我們可以看到之前所講的各個時間跟蹤信息。

源碼下載:https://github.com/gf-huanchupk/SpringCloudLearning


本文分享自微信公衆號 - 程序員果果(huanchuguofupk_gz)。
如有侵權,請聯繫 [email protected] 刪除。
本文參與“OSC源創計劃”,歡迎正在閱讀的你也加入,一起分享。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章