天天看點

Spring Cloud Sleuth--服務鍊路追蹤(二)

在鍊路中添加自定義資料

現在需要實作這樣一個功能:在鍊路資料中加上請求的操作人。被案例在geteway-service服務中實作。在gateway-service工程中建立一個ZuulFilter過濾器,它的類型為post類型,order為900,開啟攔截。在過濾器的攔截邏輯方法裡,通過Tracer 的addTag 方法加上自定義的資料,在本案例中加上了鍊路的操作人。另外也可以在這個過濾器中擷取目前鍊路的traceld資訊,traceld 作為鍊路資料的唯一辨別, 可以存儲在log日志中,友善後續查找,本案例隻是将traceld資訊簡單地列印在控制台上。代碼如下:

@Component
public class LoggerFilter extends ZuulFilter {
    
    @Autowired
    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;
    }
}
           
Spring Cloud Sleuth--服務鍊路追蹤(二)

使用RabbitMQ傳輸鍊路資料

在上述案例中,最終gateway-service收集的資料是通過Http上傳給zipkin-server的。在SpringCloudSleuth中支援消息元件來傳遞鍊路資料。

首先改造sipkin-server工程,在其pom檔案中将zipkin-server的依賴去掉,加上spring-cloud-sleuth-zipkin-stream和spring-cloud-starter-stream-rabbit的依賴:

<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.cloud</groupId>
            <artifactId>spring-cloud-sleuth-zipkin-stream</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-stream-rabbit</artifactId>
        </dependency>
    </dependencies>
           

配置中加上rabbitmq的配置:

server:
  port: 9411

spring:
  application:
    name: zipkin-server
  rabbitmq:
    host: localhost
    port: 5672
    username: guest
    password: guest

eureka:
  client:
    service-url:
      defaultZone: http://localhost:8761/eureka/
           

啟動類上加上@EnableZipkinStreamServer注解:

@SpringBootApplication
@EnableEurekaClient
// @EnableZipkinServer
@EnableZipkinStreamServer
public class ZipkinServerApplication {
    public static void main(String[] args) {
        SpringApplication.run(ZipkinServerApplication.class, args);
    }
}
           

現在來改造Zipkin Client (包括gateway-service工程和user-service工程),在它們的pom檔案中将spring coud-tartrzipki依賴改為spring cloud-sleth-zipkin-strearmn和spring-cloud-tarterstream-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>
           

同時在配置檔案applicayion.yml加上RabbitMQ的配置,同zipkin-server工程。這樣,就将鍊路的上傳資料從Http改為用消息代元件RabbitMQ。

Spring Cloud Sleuth--服務鍊路追蹤(二)
Spring Cloud Sleuth--服務鍊路追蹤(二)

在MySQL資料庫中存儲鍊路資料

在上面的例子中,Zipkin Server将資料存儲在記憶體中,一旦程式重新開機, 之前的鍊路資料全部丢失,那麼怎麼将鍊路資料存儲起來呢? Zipkin 支援将鍊路資料存儲在MySQL、Elasticsearch和Cassandra資料庫中。

使用Http傳輸鍊路資料,并存儲在MySQL資料庫中

隻需要改造zipkin-server 工程。在zipkin-server工程的pom檔案加上Zipkin Server 的依賴zipkin-server、Zipkin 的MySQL存儲依賴zipkin-storage-mysql (這兩個依賴的版本都為1.19.0)、 Zipkin Server 的UI界面依賴zipkin-autoconfigure-ui、MySQL的連接配接器依賴mysql-connector-java 和JDBC的起步依賴spring-boot- starter-jdbc。代碼如下:

<dependencies>
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-eureka</artifactId>
        </dependency>
        <!--zipkin起步依賴 -->
        <dependency>
            <groupId>io.zipkin.java</groupId>
            <artifactId>zipkin-server</artifactId>
        </dependency>
        <!-- 界面 -->
        <dependency>
            <groupId>io.zipkin.java</groupId>
            <artifactId>zipkin-autoconfigure-ui</artifactId>
        </dependency>
        <!-- mysql存儲依賴 -->
        <dependency>
            <groupId>io.zipkin.java</groupId>
            <artifactId>zipkin-storage-mysql</artifactId>
            <version>1.19.0</version>
        </dependency>
        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-jdbc</artifactId>
        </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>-->
    </dependencies>
           

在zipkin-server工程的配置檔案application.yml中加上資料源的配置,包括資料庫的Url、使用者名、密碼和連接配接驅動,并且需要配置zipkin.storage.type為mysql,代碼如下:

server:
  port: 9411

spring:
  application:
    name: zipkin-server
#  rabbitmq:
#    host: localhost
#    port: 5672
#    username: guest
#    password: guest
  datasource:
    url: jdbc:mysql://localhost:3306/zipkin?userUnicode=true&characterEncoding=utf8&useSSL=false
    username: root
    password: 123456
    driver-class-name: com.mysql.jdbc.Driver

zipkin:
  storage:
    type: mysql

eureka:
  client:
    service-url:
      defaultZone: http://localhost:8761/eureka/
           

另外需要在MySQL資料庫中初始化資料庫腳本,資料庫腳本位址為

https://github.com/openzipkin/zipkin/blob/master/zipkin-storage/mysql-v1/src/main/resources/mysql.sql

--
-- Copyright 2015-2019 The OpenZipkin Authors
--
-- Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except
-- in compliance with the License. You may obtain a copy of the License at
--
-- http://www.apache.org/licenses/LICENSE-2.0
--
-- Unless required by applicable law or agreed to in writing, software distributed under the License
-- is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
-- or implied. See the License for the specific language governing permissions and limitations under
-- the License.
--

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,
  `remote_service_name` VARCHAR(255),
  `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',
  PRIMARY KEY (`trace_id_high`, `trace_id`, `id`)
) ENGINE=InnoDB ROW_FORMAT=COMPRESSED CHARACTER SET=utf8 COLLATE utf8_general_ci;

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(`remote_service_name`) COMMENT 'for getTraces and getRemoteServiceNames';
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 and autocomplete values';
ALTER TABLE zipkin_annotations ADD INDEX(`a_key`) COMMENT 'for getTraces and autocomplete values';
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,
  PRIMARY KEY (`day`, `parent`, `child`)
) ENGINE=InnoDB ROW_FORMAT=COMPRESSED CHARACTER SET=utf8 COLLATE utf8_general_ci;
           

最後需要在程式的啟動類ZipkinServerApplication中注入MySQLStorage的Bean,代碼如下:

@SpringBootApplication
@EnableEurekaClient
@EnableZipkinServer
// @EnableZipkinStreamServer
public class ZipkinServerApplication {
    public static void main(String[] args) {
        SpringApplication.run(ZipkinServerApplication.class, args);
    }

    @Bean
    public MySQLStorage mySQLStorage(DataSource dataSource) {
        return MySQLStorage.builder()
                .datasource(dataSource)
                .executor(Runnable::run)
                .build();
    }
}
           
Spring Cloud Sleuth--服務鍊路追蹤(二)
Spring Cloud Sleuth--服務鍊路追蹤(二)

繼續閱讀