1. 程式人生 > >Spring Cloud(七):鏈路追蹤Sleuth-Zipkin與Mysql資料的持久化

Spring Cloud(七):鏈路追蹤Sleuth-Zipkin與Mysql資料的持久化

一:回顧

上節我們瞭解到Spring Cloud Sleuth可以實現對服務的追蹤,以及和Zipkin結合,可以視覺化服務追蹤介面和依賴等相關資訊。遺憾的是資料不能持久化。

二:專案的構建

構建三個專案sleuth-stream-server,sleuth-stream-provider,sleuth-stream-consumer
前期準備:
下載rabbitMq,http://www.rabbitmq.com/
啟動rabbit服務,
這裡寫圖片描述

2.1:sleuth-stream-server構建
2.1.1:pom.xml

<parent>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-parent</artifactId>
    <version>1.5.6.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>Dalston.SR2</spring-cloud.version>
</properties>

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

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

    <!--此依賴會自動引入spring-cloud-sleuth-stream並且引入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>io.zipkin.java</groupId>
        <artifactId>zipkin-autoconfigure-ui</artifactId>
        <scope>runtime</scope>
    </dependency>

    <!--儲存到資料庫需要如下依賴-->
    <dependency>
        <groupId>mysql</groupId>
        <artifactId>mysql-connector-java</artifactId>
    </dependency>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-jdbc</artifactId>
    </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>

2.1.2:application.properties

server.port=9110
spring.application.name=zipkin-stream-server
#zipkin資料儲存到資料庫中需要進行如下配置
#表示當前程式不使用sleuth
spring.sleuth.enabled=false
#表示zipkin資料儲存方式是mysql
zipkin.storage.type=mysql
#資料庫指令碼建立地址,當有多個是可使用[x]表示集合第幾個元素
spring.datasource.schema[0]=classpath:/mysql.sql
#spring boot資料來源配置
    spring.datasource.url=jdbc:mysql://localhost:3306/sleuth_log?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


#rabbitmq配置
spring.rabbitmq.host=localhost
spring.rabbitmq.port=5672
spring.rabbitmq.username=guest
spring.rabbitmq.password=guest

2.1.3:在classPath目錄下新建mysql.sql

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`);

2.1.4:入口程式

@SpringBootApplication
@EnableZipkinStreamServer
public class SleuthStreamServerApplication {

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

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

3.1:sleuth-stream-provider
3.1.1:pom.xml

<parent>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-parent</artifactId>
    <version>1.5.6.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>Dalston.SR2</spring-cloud.version>
</properties>

<dependencies>
    <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>

    <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>
</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>

3.1.2:application.yml

server:
  port: 9111
spring:
  application:
    name: sleuth-stream-provider

 #rabbitmq配置
  rabbitmq:
    host: localhost
    username: guest
    password: guest

3.1.3: 程式入口

@SpringBootApplication
public class SleuthStreamProviderApplication {

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

3.1.4:提供一個服務

@RestController
public class SleuthProviderController {

private static final Logger LOGGER = LoggerFactory.getLogger(SleuthProviderController.class);

@GetMapping({"","/"})
public String index(){
    return "this is a spring-cloud sleuth provider!";
}

/**
 * 用來測試鏈路
 * @return
 */
@GetMapping("/index")
public String providerIndex(){
    LOGGER.info("<<<<<<<<<<<<<<<<trace sleuth demo provider is being called! >>>>>>>>>>>>>>>");
    return "Hello dy_bom ,this is a spring-cloud sleuth provider!";
        }
}

4.1:sleuth-stream-consumer
4.1.1:pom.xml

 <parent>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-parent</artifactId>
    <version>1.5.6.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>Dalston.SR2</spring-cloud.version>
</properties>

<dependencies>
    <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>

    <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>
</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>

4.1.2:application.yml

server:
  port: 9112
spring:
  application:
    name: sleuth-stream-consumer

#rabbitmq配置
  rabbitmq:
    host: localhost
    username: guest
    password: guest

4.1.3:程式入口

@SpringBootApplication
public class SleuthStreamConsumerApplication {

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

4.1.4:服務消費

@RestController
public class SleuthConsumerController {

private static final Logger LOGGER = LoggerFactory.getLogger(SleuthConsumerController.class);
@Autowired
private RestTemplate restTemplate;

@Bean
public RestTemplate getRestTemplate(){
    return new RestTemplate();
}

/**
 * 專案說明
 * @return
 */
@GetMapping({"/",""})
public String index(){
    return "this is spring-cloud-sleuth-consumer";
}

/**
 * 遠端服務
 * @return
 */
@GetMapping( "/index")
public Object sleuthProviderIndex() {

    LOGGER.info("<<<<<<<<<<<<<<<<calling trace demo provider>>>>>>>>>>>>>>>");
    String str =  restTemplate.getForObject("http://localhost:9111/index", String.class);
    return str;
    }
 }

說明,從上面的配置可以看出,我們客戶端無須加入配置Server的地址。因為我們不是通過Http的Zipkin來收集資料。而是通過資料流Stream傳輸的。而且在這裡我們沒有配置它的手機採集率,即是預設的0.1。

三:專案的啟動

依次啟動專案Server端,服務提供端,服務消費端。
多次訪問:http://localhost:9112/index
這裡寫圖片描述
看日誌。解釋一下:
紅色部分分別表示服務名稱,traceId,spanId,boolean(代表是否進入了ZipKin),由於我們沒設定採集率所以,你會發現很多false。
檢視資料庫是否入庫了:
這裡寫圖片描述
檢視zipKin管理頁面,檢視追蹤資訊
這裡寫圖片描述

可以看出描述的很詳細。

四:重啟Server端
重啟Server,檢視zipKin管理頁面,檢視追蹤資訊是否有歷史資料