Spring Cloud實戰系列(七) - 服務鏈路追蹤Spring Cloud Sleuth

零壹技術棧發表於2019-02-04

相關

  1. Spring Cloud實戰系列(一) - 服務註冊與發現Eureka

  2. Spring Cloud實戰系列(二) - 客戶端呼叫Rest + Ribbon

  3. Spring Cloud實戰系列(三) - 宣告式客戶端Feign

  4. Spring Cloud實戰系列(四) - 熔斷器Hystrix

  5. Spring Cloud實戰系列(五) - 服務閘道器Zuul

  6. Spring Cloud實戰系列(六) - 分散式配置中心Spring Cloud Config

  7. Spring Cloud實戰系列(七) - 服務鏈路追蹤Spring Cloud Sleuth

  8. Spring Cloud實戰系列(八) - 微服務監控Spring Boot Admin

  9. Spring Cloud實戰系列(九) - 服務認證授權Spring Cloud OAuth 2.0

  10. Spring Cloud實戰系列(十) - 單點登入JWT與Spring Security OAuth

前言

Spring Cloud Sleuth 的主要功能就是為 分散式系統 提供 追蹤解決方案,並且相容支援了 Zipkin,只需要在 pom.xml 檔案中引入相應的 依賴 即可。本文主要講述 服務追蹤元件 ZipkinSpring Cloud Sleuth 整合了 Zipkin 元件。它主要用於 聚集 來自各個 異構系統實時監控資料,用來追蹤 微服務架構 下的 系統延時問題

正文

1. 相關術語

1.1. Span

Span 是一個基本的 工作單元,用於描述一次 RPC 呼叫,Span 通過一個 64 位的 spanId 作為 唯一標識Zipkin 中的 Span 還有其他資料資訊,比如 摘要時間戳事件關鍵值註釋 (tags) 以及 進度 ID (通常是 IP 地址)。Span 在不斷的啟動和停止,同時記錄了 時間資訊,一個 Span 建立後,必須在未來的某個時刻停止它。

1.2. Trace

一系列 Span 組成的一個 樹狀結構。例如,如果你正在跑一個大型 分散式系統,可能需要建立一個 Trace

1.3. Annotation

表示 基本標註列表,一個 Annotation 可以理解成 Span 生命週期中 重要時刻資料快照,比如一個 Annotation 中一般包含 發生時刻timestamp)、事件型別value)、端點endpoint)等資訊。其中 Annotation事件型別 包含以下四類:

  • cs - Client Sent

客戶端 發起一個請求,這個 Annotion 描述了這個 Span 的開始。

  • sr - Server Received

服務端 獲得請求並 準備開始 處理它,如果將 sr 減去 cs時間戳 便可得到 網路延遲

  • ss - Server Sent

服務端 完成請求處理,如果將 ss 減去 sr時間戳,便可得到 服務端 處理請求消耗的時間。

  • cr - Client Received

客戶端 成功接收到 服務端 的響應,如果將 cr 減去 cs時間戳,便可得到 整個請求 所消耗的 總時間

2. 專案結構

本文案例主要由 四個模組 組成:

  • eureka-server:作為 服務註冊中心

  • zipkin-server:作為 鏈路追蹤服務中心,負責儲存 鏈路資料

  • service-hi:對外暴露一個 測試介面,同時作為 鏈路追蹤服務端,負責 產生鏈路資料

  • service-zuul:作為 路由閘道器,負責 請求轉發,同時作為 鏈路追蹤客戶端,產生 鏈路資料,並上傳至 zipkin-server

8761 開啟 eureka-server 服務註冊中心,參考前面的文章即可,這裡不再演示建立。

3. 構建zipkin-server

新建一個 Spring Boot 應用模組 zipkin-server,它的 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>
    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>1.5.3.RELEASE</version>
        <relativePath/> <!-- lookup parent from repository -->
    </parent>

    <groupId>io.github.ostenant.springcloud</groupId>
    <artifactId>zipkin-server</artifactId>
    <version>0.0.1-SNAPSHOT</version>

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

    <properties>
        <java.version>1.8</java.version>
        <spring-cloud.version>Dalston.RELEASE</spring-cloud.version>
    </properties>

    <dependencies>
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-eureka</artifactId>
        </dependency>
        <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>

    <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>
複製程式碼

在應用的入口類上, 加上註解 @EnableZipkinServer,開啟 Zipkin Server 的功能。

@EnableZipkinServer
@EnableEurekaClient
@SpringBootApplication
public class ZipkinServerApplication {

    public static void main(String[] args) {
        SpringApplication.run(ZipkinServerApplication.class, args);
    }
}
複製程式碼

配置檔案 application.yml 中指定服務埠號為 9411,並向 Eureka 註冊中心進行 服務註冊

eureka:
  client:
    service-url:
      defaultZone: http://localhost:8761/eureka/
server:
  port: 9411
spring:
  application:
    name: zipkin-server
複製程式碼

4. 構建service-hi

新建一個 Spring Boot 應用模組 service-hi,在它的 pom.xml 中引入 引入起步依賴 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>
    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>1.5.3.RELEASE</version>
        <relativePath/> <!-- lookup parent from repository -->
    </parent>
    <groupId>io.github.ostenant.springcloud</groupId>
    <artifactId>service-hi</artifactId>
    <version>0.0.1-SNAPSHOT</version>
    <name>eureka-client</name>
    <description>Demo project for Spring Boot</description>

    <properties>
        <java.version>1.8</java.version>
        <spring-cloud.version>Dalston.SR1</spring-cloud.version>
    </properties>

    <dependencies>
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-eureka</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-zipkin</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</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>
複製程式碼

在它的 配置檔案 application.yml 中通過配置項 spring.zipkin.base-url 指定 zipkin server 的地址。

eureka:
  client:
    serviceUrl:
      defaultZone: http://localhost:8761/eureka/
server:
  port: 8763
spring:
  application:
    name: service-hi
  zipkin:
  # base-url: http://localhost:9411/
  # 若在同一個註冊中心的話可以啟用自動發現,省略base-url
    locator:
      discovery:
        enabled: true #自動發現
  sleuth:
    sampler:
      percentage: 1.0
複製程式碼

到此為止 ZipKin 客戶端 已經整合完畢,最後在 應用啟動類 上對外暴露一個 API 介面 方便測試

@SpringBootApplication
@EnableEurekaClient
@RestController
public class ServiceHiApplication {

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

    @Value("${server.port}")
    private String port;

    @RequestMapping("/hi")
    public String home(@RequestParam String name) {
        return "Hi " + name + ", I am from port: " + port;
    }
}
複製程式碼

5. 構建service-zuul

新建一個 Spring Boot 應用模組 service-zuul,在 pom.xml 中引入依賴 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>

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

    <groupId>io.github.ostenant.springcloud</groupId>
    <artifactId>service-zuul</artifactId>
    <version>0.0.1-SNAPSHOT</version>

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

    <properties>
        <java.version>1.8</java.version>
        <spring-cloud.version>Dalston.SR1</spring-cloud.version>
    </properties>

    <dependencies>
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-eureka</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-zuul</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-zipkin</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>
複製程式碼

在它的 配置檔案 application.yml 中通過配置項 spring.zipkin.base-url 指定 zipkin server 的地址。同時指定 service-hi 基於 zuul服務路徑 匹配字首。

server:
  port: 8769
spring:
  application:
    name: service-zuul
  client:
    service-url:
      defaultZone: http://localhost:8761/eureka/
  sleuth:
    sampler:
      percentage: 1.0
  zipkin:
    # base-url: http://localhost:9411/
    # 若在同一個註冊中心的話可以啟用自動發現,省略base-url
    locator:
      discovery:
        enabled: true #自動發現
zuul:
  routes:
    api-hi:
      path: /api-hi/**
      serviceId: service-hi
複製程式碼

到這裡可以發現,引入 ZipKin 服務只需要 導依賴配置屬性 兩步即可。在應用的 啟動類 上使用 @EnableZuulProxy 註解開啟 路由閘道器

@EnableZuulProxy
@EnableEurekaClient
@SpringBootApplication
public class ServiceZuulApplication {

    public static void main(String[] args) {
        SpringApplication.run(ServiceZuulApplication.class, args);
    }
}
複製程式碼

6. 測試追蹤過程

完整 鏈路追蹤模組 搭建完畢,總結一下:

  1. 搭建一臺 zipkin server 作為 鏈路服務中心

  2. 給各個 服務 引入 zipkin 依賴,配置 zipkin server 地址即可。

下面按順序依次啟動 eureka-serverservice-zipkinservice-hiservice-zuul。訪問服務閘道器,地址為: http://localhost:8769/api-hi/hi?name=vainlgory,服務響應內容如下:

Hi Vainlgory, I am from port: 8763

然後訪問 http://localhost:9411,即訪問 ZipKin 提供的視覺化頁面。

Spring Cloud實戰系列(七) - 服務鏈路追蹤Spring Cloud Sleuth

這個介面用於顯示 ZipKin Server 收集的 鏈路資料,可以根據 服務名開始時間結束時間請求消耗的時間 等條件來查詢。單擊 Find Tracks按鈕,可以檢視請求的 呼叫時間消耗時間,以及請求的 鏈路情況

單擊頂部的 Dependencies 按鈕,可以檢視服務的 依賴關係

Spring Cloud實戰系列(七) - 服務鏈路追蹤Spring Cloud Sleuth

7. 在鏈路資料中新增自定義資料

現在需要實現一個功能:在 鏈路資料 中加上請求的 操作人。本案例在 service-zuul 閘道器服務 中實現。

  1. 新建一個 ZuulFilter 過濾器,它的型別為 post 型別,order900,開啟 攔截功能

  2. 在過濾器的 攔截邏輯方法 run() 裡面,通過 TraceraddTag() 方法加上 自定義 的資料,在本案例中加上了鏈路的 操作人

  3. 也可以在這個 過濾器 中獲取 當前鏈路traceld 資訊,traceld 作為 鏈路資料唯一標識,可以儲存在 log 日誌中,方便後續查詢,本案例只是將 traceld 的資訊簡單地列印在控制檯上。程式碼如下:

@Component
public class LoggerFileter extends ZuulFilter {
    @Autowired
    private Tracer tracer;

    @Override
    public String filterType() {
        return FilterConstants.POST_TYPE;
    }

    @Override
    public int filterOrder() {
        return 900;
    }

    @Override
    public boolean shouldFilter() {
        return true;
    }

    @Override
    public Object run() {
        tracer.addTag("operator","forezp");
        System.out.println(tracer.getCurrentSpan().traceIdString());
        return null;
    }
}
複製程式碼

8. 使用RabbitMQ傳輸鏈路資料

首先改造 zipkin-server 專案模組,在它的 pom.xml 檔案中將 zipkin-server 的依賴去掉,加上 spring-cloud-sleuth-zipkin-streamspring-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>
複製程式碼

zipkin-server 的配置檔案 application.yml 中加上 RabbitMQ 的配置,包括 主機名使用者名稱密碼,程式碼如下:

spring:
  rabbitmq:
    host: localhost
    port: 5672
    username: guest
    password: guest
複製程式碼

在應用的啟動類 ZipkinServerApplication 上把註解 @EnableZipkinServer 替換為註解 @EnableZipkinStreamServer,開啟 ZipkinStreamServer,程式碼如下:

@EnableEurekaClient
@SpringBootApplication
@EnableZipkinStreamServer
public class ZipkinServerApplication {

    public static void main(String[] args) {
        SpringApplication.run(ZipkinServerApplication.class, args);
    }
}
複製程式碼

現在來改造 Zipkin Client(包括 service-zuulservice-hi 兩個模組),分別在它們的 pom.xml 檔案中將 spring-cloud-starter-zipkin 依賴改為 spring-cloud-sleuth-zipkin-streamspring-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>
複製程式碼

和前面的 zipkin-server 模組一樣,同時在配置檔案 applicayion.yml 加上 RabbitMQ 的配置。這樣,就將鏈路的 資料上傳HTTP 改為用 訊息代元件 RabbitMQ 的方式。

9. 在MySQL資料庫中儲存鏈路資料

在上面的例子中,Zipkin Server 將資料儲存在 記憶體 中,一旦應用服務 重啟,之前的 鏈路資料全部丟失,這時候就需要引入 持久化機制Zipkin 支援將 鏈路資料 儲存在 MySQLElasticsearchCassandra 資料庫中。本節講解如何使用 MySQL 儲存。

Zipkin Client 有兩種方式將 鏈路資料 傳輸到 Zipkin Server 中,一種是使用 HTTP,另一種是使用 RabbitMQZipkin Server 通過這兩種方式來 收集鏈路資料,並儲存在 MySQL 中。

9.1. 使用HTTP傳輸鏈路資料

zipkin-server 模組的 pom.xml 檔案加上以下依賴:

  • Zipkin Server 的依賴 zipkin-server

  • ZipkinMySQL 儲存依賴 zipkin-storage-mysql

  • Zipkin ServerUI 介面依賴 zipkin-autoconfigure-ui

  • MySQL 的聯結器依賴 mysql-connector-java

  • JDBC起步依賴 spring-boot-starter-jdbc

程式碼如下:

<dependency>
    <groupId>io.zipkin.java</groupId>
    <artifactId>zipkin-server</artifactId>
    <version>1.19.0</version>
</dependency>
<dependency>
    <groupId>io.zipkin.java</groupId>
    <artifactId>zipkin-storage-mysql</artifactId>
    <version>1.19.0</version>
</dependency>
<dependency>
    <groupId>io.zipkin.java</groupId>
    <artifactId>zipkin-autoconfigure-ui</artifactId>
</dependency>
<dependency>
    <groupId>mysql</groupId>
    <artifactId>mysql-connector-java</artifactId>
</dependency>
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-jdbc</artifactId>
</dependency>
複製程式碼

zipkin-server 模組的 配置檔案 application.yml 中加上 資料來源 的配置,包括 資料庫Url使用者名稱密碼連線驅動,並且需要配置 zipkin.storage.typemysql,程式碼如下:

spring:
  datasource:
    driver-class-name: com.mysql.jdbc.Driver
    url: jdbc:mysql://localhost:3306/spring-cloud-zipkin?useUnicode=true&characterEncoding=utf8&useSSL=false
    username: root
    password: 123456
zipkin:
  storage:
    type: mysql
複製程式碼

另外需要在 MySQL 資料庫中放置資料庫的 初始化指令碼,建立包括 zipkin_spanszipkin_annotationszipkin_dependencies 這幾張表。

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,
  `error_count` BIGINT
) ENGINE=InnoDB ROW_FORMAT=COMPRESSED CHARACTER SET=utf8 COLLATE utf8_general_ci;

ALTER TABLE zipkin_dependencies ADD UNIQUE KEY(`day`, `parent`, `child`);
複製程式碼

最後需要在應用的啟動類 ZipkinServerApplication 中注入 MySQLStorageBean,程式碼如下:

@Bean
public MySQLStorage mySQLStorage(DataSource datasource) {
    return MySQLStorage.builder()
        .datasource(datasource)
        .executor(Runnable::run)
        .build();
}
複製程式碼

只需要上述步驟,即可將使用 HTTP 傳輸的 鏈路資料 儲存在 MySQL 資料庫中。

9.2. 使用RabbitMQ傳輸鏈路資料

本節的案例是在使用 RabbitMQ 傳輸資料 基礎上進行改造的,只需要改造 zipkin-server 的工程。

zipkin-server 工程的 pom.xml 檔案中加上 MySQL聯結器依賴 mysql-connector-javaJDBC起步依賴 spring-boot-starter-jdbc,程式碼如下:

<dependency>
    <groupId>mysql</groupId>
    <artifactId>mysql-connector-java</artifactId>
</dependency>
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-jdbc</artifactId>
</dependency>
複製程式碼

zipkin-server 模組的 配置檔案 application.yml 中加上 資料來源 的配置,包括資料庫的 Uri使用者名稱密碼連線驅動,同樣的配置 zipkin.storage.typemysql,程式碼如下:

spring:
  datasource:
    url: jdbc:mysql://localhost:3306/spring-cloud-zipkin?useUnicode=true&characterEncoding=utf8&useSSL=false
    username: root
    password: 123456
    driver-class-name: com.mysql.jdbc.Driver
zipkin:
  storage:
    type: mysql
複製程式碼

另外需要在 MySQL資料庫中初始化 資料庫指令碼,具體同上一節。

10. 在ElasticSearch中儲存鏈路資料

併發高 的情況下,使用 MySQL 儲存 鏈路資料 顯然不合理,這時可以選擇使用 ElasticSearch 作為儲存。

下載並安裝 ElasticSearchKibana,下載地址為 www.elastic.co/products/el…。安裝完成後啟動,其中 ElasticSearch預設埠號9200Kibana預設埠號5601

本節是在 第八節 的基礎上進行改造。首先在 pom.xml 檔案中加上 zipkin 的依賴和 zipkin-autoconfigure-storage-elasticsearch-http 的依賴,程式碼如下:

<dependency>
    <groupId>io.zipkin.java</groupId>
    <artifactId>zipkin</artifactId>
    <version>1.28.1</version>
</dependency>
<dependency>
    <groupId>io.zipkin.java</groupId>
    <artifactId>zipkin-autoconfigure-storage-elasticsearch-http</artifactId>
    <version>1.28.1</version>
</dependency>
複製程式碼

在應用的配置檔案 application.yml 中加上 Zipkin 的配置,配置了 zipkin儲存型別type) 為 Elasticsearch,使用的 儲存元件StorageComponent)為 Elasticsearch,然後需要配置 Elasticsearch,包括 hosts( 可以配置 多個實例,用 “,” 隔開)。具體配置程式碼如下:

zipkin:
  storage:
    type: elasticsearch
    StorageComponent: elasticsearch
    elasticsearch:
      cluster: elasticsearch
      max-requests: 30
      index: zipkin
      index-shards: 3
      index-replicas: 1
      hosts: localhost:9200
複製程式碼

只需要完成這些配置,Zipkin Server鏈路資料 就可以儲存在 ElasticSearch 裡面。

11. 用Kibana展示鏈路資料

上一節講述瞭如何將 鏈路資料 儲存在 ElasticSearch 中,ElasticSearch 可以和 Kibana 結合,將 鏈路資料 展示在 Kibana 上。安裝完成後啟動 KibanaKibana 預設會向 本地埠9200ElasticSearch 讀取資料。Kibana預設埠5601,訪問 Kibana 的主頁 http://localhost:5601

單擊 Management 按鈕,然後單擊 Add New,新增一個 index。上一節 ElasticSearch 中寫入 鏈路資料index 配置的是 zipkin,在 Kibana 介面上填寫 zipkin-*,單擊 Create 按鈕,建立完成 index 後,單擊 Discover,頁面上出現 鏈路資料

參考

  • 方誌朋《深入理解Spring Cloud與微服務構建》

歡迎關注技術公眾號: 零壹技術棧

零壹技術棧

本帳號將持續分享後端技術乾貨,包括虛擬機器基礎,多執行緒程式設計,高效能框架,非同步、快取和訊息中介軟體,分散式和微服務,架構學習和進階等學習資料和文章。

相關文章