1. 程式人生 > >Spring Cloud Sleuth(分散式服務跟蹤)(1)

Spring Cloud Sleuth(分散式服務跟蹤)(1)

首先準備工作如下:

1.服務註冊中心:eureka-server。

2.微服務應用:trace-1,實現REST介面,並呼叫trace-2應用的介面。

其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.example</groupId>
    <artifactId>trace-1</artifactId>
    <version>0.0.1-SNAPSHOT</version>
    <packaging>jar</packaging>

    <name>trace-1</name>
    <description>Demo project for Spring Boot</description>

    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>2.0.5.RELEASE</version>
        <relativePath/> <!-- lookup parent from repository -->
    </parent>

    <properties>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
        <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
        <java.version>1.8</java.version>
        <spring-cloud.version>Finchley.SR1</spring-cloud.version>
    </properties>

    <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-client</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-netflix-ribbon</artifactId>
        </dependency>

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

    <dependencyManagement>
        <dependencies>
            <dependency>
                <groupId>org.springframework.cloud</groupId>
                <artifactId>spring-cloud-dependencies</artifactId>
                <version>${spring-cloud.version}</version>
                <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>

應用主類如下:

@RestController
@EnableDiscoveryClient
@SpringBootApplication
public class Trace1Application {

    private final Logger logger=Logger.getLogger(getClass());

    @Bean
    @LoadBalanced
    RestTemplate restTemplate(){
        return new RestTemplate();
    }

    @GetMapping("/trace-1")
    public String trace(){
        logger.info("---------------------------    trace1   ----------------------------------");
        return restTemplate().getForEntity("http://trace-2/trace-2",String.class).getBody();
    }

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

application.properties檔案如下:

spring.application.name=trace-1
server.port=9101
eureka.client.serviceUrl.defaultZone=http://localhost:1111/eureka/

3.建立第二個微服務應用:trace-2,實現REST介面/trace-2。

其pom.xml跟trace-1的pom檔案相同。

應用主類如下:

@RestController
@EnableDiscoveryClient
@SpringBootApplication
public class Trace2Application {

    private final Logger logger=Logger.getLogger(getClass());

    @GetMapping("/trace-2")
    public String trace(){
        logger.info("---------------------------    trace2   ----------------------------------");
        return "Trace2";
    }

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

application.properties檔案如下:

spring.application.name=trace-2
server.port=9102
eureka.client.serviceUrl.defaultZone=http://localhost:1111/eureka/

啟動三個服務後呼叫http://localhost:9101/trace-1

實現跟蹤

首先在trace-1和trace-2的pom.xml檔案中加入spring-cloud-starter-sleuth依賴:

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

然後訪問http://localhost:9101/trace-1

第一個值:trace-1,它記錄了應用的名稱(application.properties中的spring.application.name引數配置的屬性。)

第二個值:13a031b537091ddc,Spring Cloud Sleuth生成的一個ID,稱為Trace ID,它用來標示一條請求鏈路。一條請求鏈路中包含一個Trace ID,多個Span ID。

第三個值:13a031b537091ddc,Spring Cloud Sleuth生成的另外一個ID,稱為Span ID,他表示一個基本的工作單元,比如傳送一個HTTP請求。

第四個值:false,表示是否要將該資訊輸出到Zipkin等服務中來收集和展示。

上面的Trace ID和Span ID是Spring Cloud Sleuth實現分散式服務跟蹤的核心。在一次服務請求鏈路的呼叫中,會保持傳遞同一個Trace ID,從而將整個分佈於不同微服務程序中的請求跟蹤資訊串聯在一起。

跟蹤原理

分散式系統中的服務跟蹤在理論上實現不復雜,它主要有兩個關鍵點:

1.為了實現請求跟蹤,當請求傳送到分散式系統的入口端點時,只需要服務跟蹤框架為該請求建立一個唯一的跟蹤標識,同時在分散式系統內部流轉的時候,框架始終保持傳遞該唯一標識,直到返回給請求方為止,這個唯一標識就是前文中提到的 TraceID。通過 TraceID的記錄,我們就能將所有請求過程日誌關聯起來。

2.為了統計各處理單元的時間延遲,當請求達到各個服務元件時,或是處理邏輯到達某個狀態時,也通過一個唯一標識來標記它的開始、具體過程以及結束,該標識就是我們前文中提到的Span ID,對於每個Span來說,它必須有開始和結束兩個節點,通過記錄開始Span和結束Span的時間戳,就能統計出該Span的時間延遲,除了時間戳記錄之外,它還可以包含一些其他元資料,比如:事件名稱、請求資訊等。

在Spring Boot應用中,通過在工程中引入spring-cloud-starter-sleuth依賴之後,它會自動為當前應用構建起各通訊通道的跟蹤機制,如:

1.通過RabbitMQ,Kafka傳遞請求

2.通過Zuul代理傳遞的請求

3.通過RestTemplate發起的請求

檢視原始碼可以獲取:

  • X-B3-TraceId:一條請求鏈路(Trace)的唯一標識,必須值

  • X-B3-SpanId:一個工作單元(Span)的唯一標識,必須值

  • X-B3-ParentSpanId::標識當前工作單元所屬的上一個工作單元,Root Span(請求鏈路的第一個工作單元)的該值為空

  • X-B3-Sampled:是否被抽樣輸出的標誌,1表示需要被輸出,0表示不需要被輸出

  • X-Span-Name:工作單元的名稱

修改trace-2的實現:

@RestController
@EnableDiscoveryClient
@SpringBootApplication
public class Trace2Application {

    private final Logger logger = Logger.getLogger(getClass());

    @GetMapping("/trace-2")
    public String trace(HttpServletRequest request) {
        logger.info("---------------------------    trace2   ----------------------------------");
        logger.infof(" \n  TraceId={} \n  SpanId={} \n  ParentSpanId={} \n  Sampled={} \n  Name={}"
                , request.getHeader("X-B3-TraceId")
                , request.getHeader("X-B3-SpanId")
                , request.getHeader("X-B3-ParentSpanId")
                , request.getHeader("X-B3-Sampled")
                , request.getHeader("X-Span-Name"));
        return "Trace2";
    }

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

為了更直觀的觀察跟蹤資訊,可以在application.properties增加如下配置:

logging.level.org.springframework.web.servlet.DispatcherServlet=DEBUG

 

參考《Spring Cloud微服務實戰》