Eclipse配置運行SpringCloud(Hoxton + 2.2.4)微服務框架 + 分佈式鏈路跟蹤 Sleuth+Zipkin(RabbitMQ+MySQL)

簡介

Spring Cloud Sleuth

隨着業務發展,系統拆分導致系統調用鏈路愈發複雜一個前端請求可能最終需要調用很多次後端服務才能完成,當整個請求變慢或不可用時,我們是無法得知該請求是由某個或某些後端服務引起的,這時就需要解決如何快讀定位服務故障點,以對症下藥。於是就有了分佈式系統調用跟蹤的誕生。

一般的,一個分佈式服務跟蹤系統主要由三部分構成:

  • 數據收集
    數據收集除了支持平臺無關和開發語言無關係統的數據收集,還包括異步數據收集(需要跟蹤隊列中的消息,保證調用的連貫性),以及確保更小的侵入性。

  • 數據存儲
    對於大規模分佈式系統,數據存儲可分爲實時數據和全量數據兩部分,實時數據用於故障排查(Trouble Shooting),全量數據用於系統優化。

  • 數據展示
    數據展示涉及到數據挖掘和分析。

在這裏插入圖片描述
服務追蹤的追蹤單元是從客戶發起請求(request)抵達被追蹤系統的邊界開始,到被追蹤系統向客戶返回響應(response)爲止的過程,稱爲一個 trace。每個 trace 中會調用若干個服務,爲了記錄調用了哪些服務,以及每次調用的消耗時間等信息,在每次調用服務時,埋入一個調用記錄,稱爲一個 span。這樣,若干個有序的 span 就組成了一個 trace。在系統向外界提供服務的過程中,會不斷地有請求和響應發生,也就會不斷生成 trace,把這些帶有 span 的 trace 記錄下來,就可以描繪出一幅系統的服務拓撲圖。附帶上 span 中的響應時間,以及請求成功與否等信息,就可以在發生問題的時候,找到異常的服務;根據歷史數據,還可以從系統整體層面分析出哪裏性能差,定位性能優化的目標。
在這裏插入圖片描述

  • 耗時分析:通過 Sleuth 可以很方便的瞭解到每個採樣請求的耗時,從而分析出哪些服務調用比較耗時;
  • 可視化錯誤:對於程序未捕捉的異常,可以通過集成 Zipkin 服務界面上看到;
  • 鏈路優化:對於調用比較頻繁的服務,可以針對這些服務實施一些優化措施。

Zipkin

Zipkin 是基於Dapper 實現(Google 分佈式跟蹤系統的論文),它致力於收集服務的定時數據,以解決微服務架構中的延遲問題,包括數據的追蹤收集、存儲、查找和展現。Zipkin可以收集各個服務器上請求鏈路的跟蹤數據,並通過它提供的 REST API 接口來輔助用戶查詢跟蹤數據以實現對分佈式系統的監控程序,從而及時地發現系統中出現的延遲升高問題並找出系統性能瓶頸的根源。除了面向開發的 API 接口之外,Zipkin也提供了方便的 UI 組件來幫助用戶直觀的搜索跟蹤信息和分析請求鏈路明細,比如:可以查詢某段時間內各用戶請求的處理時間等。

Zipkin 提供了可插拔數據存儲方式:In-Memory、MySql、Cassandra 以及 Elasticsearch。
在這裏插入圖片描述
Zipkin 的基礎架構主要由 4 個核心組件構成:

  • Collector:收集器組件,它主要用於處理從外部系統發送過來的跟蹤信息,將這些信息轉換爲 Zipkin 內部處理的 Span 格式,以支持後續的存儲、分析、展示等功能。
  • Storage:存儲組件,它主要對處理收集器接收到的跟蹤信息,默認會將這些信息存儲在內存中,我們也可以修改此存儲策略,通過使用其他存儲組件將跟蹤信息存儲到數據庫中。
  • RESTful API:API 組件,它主要用來提供外部訪問接口。比如給客戶端展示跟蹤信息,或是外接系統訪問以實現監控等。
  • Web UI:UI 組件,基於 API 組件實現的上層應用。通過 UI 組件用戶可以方便而有直觀地查詢和分析跟蹤信息。

Zipkin 服務端

Docker方式

在 Windows系統上以docker方式運行Zipkin服務端

啓動Docker Quickstart Terminal,等待虛擬機啓動成功,並取得IP地址:在這裏插入圖片描述
啓動Kitematic,配置端口號:
在這裏插入圖片描述
修改C:\Windows\System32\drivers\etc\hosts

192.168.99.100 kitematic-zipkin.com

啓動後,訪問http://kitematic-zipkin.com:9411/zipkin/就能看到如下界面
在這裏插入圖片描述

Eclipse啓動方式(未成功)

在這裏插入圖片描述
在這裏插入圖片描述

添加依賴包文件POM.xml

<?xml version="1.0"?>
<project xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd" xmlns="http://maven.apache.org/POM/4.0.0"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
  <modelVersion>4.0.0</modelVersion>
  <parent>
    <groupId>com.springcloud</groupId>
    <artifactId>springcloud-root</artifactId>
    <version>0.0.1-SNAPSHOT</version>
  </parent>
  <artifactId>springcloud-zipkin</artifactId>
  <name>springcloud-zipkin</name>
  <url>http://maven.apache.org</url>
  <properties>
    <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
  </properties>
  <dependencies>
    <dependency>
      <groupId>org.springframework.boot</groupId>
      <artifactId>spring-boot-starter</artifactId>
      <exclusions>
        <exclusion>
          <groupId>org.springframework.boot</groupId>
          <artifactId>spring-boot-starter-logging</artifactId>
        </exclusion>
      </exclusions>
    </dependency>
    <dependency>
      <groupId>org.springframework.cloud</groupId>
      <artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
    </dependency>
    <dependency>
      <groupId>io.zipkin.java</groupId>
      <artifactId>zipkin-server</artifactId>
      <version>2.12.9</version>
    </dependency>
    <dependency>
      <groupId>io.zipkin.java</groupId>
      <artifactId>zipkin-autoconfigure-ui</artifactId>
      <version>2.12.9</version>
    </dependency>
    <dependency>
      <groupId>org.springframework.boot</groupId>
      <artifactId>spring-boot-starter-web</artifactId>
    </dependency>
    <dependency>
      <groupId>junit</groupId>
      <artifactId>junit</artifactId>
      <version>3.8.1</version>
      <scope>test</scope>
    </dependency>
  </dependencies>
</project>

log4j-slf4j-impl cannot be present with log4j-to-slf4j錯誤

SLF4J: Class path contains multiple SLF4J bindings.
SLF4J: Found binding in [jar:file:/E:/SpringBoot/Maven-Repository/org/apache/logging/log4j/log4j-slf4j-impl/2.12.1/log4j-slf4j-impl-2.12.1.jar!/org/slf4j/impl/StaticLoggerBinder.class]
SLF4J: Found binding in [jar:file:/E:/SpringBoot/Maven-Repository/ch/qos/logback/logback-classic/1.2.3/logback-classic-1.2.3.jar!/org/slf4j/impl/StaticLoggerBinder.class]
SLF4J: See http://www.slf4j.org/codes.html#multiple_bindings for an explanation.
SLF4J: Actual binding is of type [org.apache.logging.slf4j.Log4jLoggerFactory]
Exception in thread "main" java.lang.ExceptionInInitializerError
	at org.springcloud.zipkin.ZipkinServerApplication.main(ZipkinServerApplication.java:12)
Caused by: org.apache.logging.log4j.LoggingException: log4j-slf4j-impl cannot be present with log4j-to-slf4j
	at org.apache.logging.slf4j.Log4jLoggerFactory.validateContext(Log4jLoggerFactory.java:49)
	at org.apache.logging.slf4j.Log4jLoggerFactory.newLogger(Log4jLoggerFactory.java:39)
	at org.apache.logging.slf4j.Log4jLoggerFactory.newLogger(Log4jLoggerFactory.java:30)
	at org.apache.logging.log4j.spi.AbstractLoggerAdapter.getLogger(AbstractLoggerAdapter.java:54)
	at org.apache.logging.slf4j.Log4jLoggerFactory.getLogger(Log4jLoggerFactory.java:30)
	at org.slf4j.LoggerFactory.getLogger(LoggerFactory.java:363)
	at org.apache.commons.logging.LogAdapter$Slf4jAdapter.createLocationAwareLog(LogAdapter.java:130)
	at org.apache.commons.logging.LogAdapter.createLog(LogAdapter.java:91)
	at org.apache.commons.logging.LogFactory.getLog(LogFactory.java:67)
	at org.apache.commons.logging.LogFactory.getLog(LogFactory.java:59)
	at org.springframework.boot.SpringApplication.<clinit>(SpringApplication.java:196)
	... 1 more

POM增加配置

<dependency>
  <groupId>org.springframework.boot</groupId>
  <artifactId>spring-boot-starter</artifactId>
  <exclusions>
    <exclusion>
      <groupId>org.springframework.boot</groupId>
      <artifactId>spring-boot-starter-logging</artifactId>
    </exclusion>
  </exclusions>
</dependency>

配置application.yml文件

spring:
  application:
    name: springcloud-zipkin-server
  freemarker:
    prefer-file-system-access: false
  security:
    user:
      name: admin
      password: 123456

server:
  port: 9411

eureka:
  instance:
    hostname: zipkin-server.com
    instance-id: zipkin-server
  client:
    service-url:
      defaultZone: http://${spring.security.user.name}:${spring.security.user.password}@eureka-peer1.com:8897/eureka/,http://${spring.security.user.name}:${spring.security.user.password}@eureka-peer2.com:8898/eureka/,http://${spring.security.user.name}:${spring.security.user.password}@eureka-peer3.com:8899/eureka/

management:
  metrics:
    web:
      server:
        request:
          autotime:
            enabled: false

添加ZipkinServerApplication啓動類

package org.springcloud.zipkin;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

import zipkin2.server.internal.EnableZipkinServer;

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

Error creating bean with name ‘webMvcMetricsFilter’ defined in class path resource錯誤

2020-02-26 18:15:14.627 ERROR 16740 --- [           main] o.s.b.w.e.t.TomcatStarter                : Error starting Tomcat context. Exception: org.springframework.beans.factory.UnsatisfiedDependencyException. Message: Error creating bean with name 'webMvcMetricsFilter' defined in class path resource [org/springframework/boot/actuate/autoconfigure/metrics/web/servlet/WebMvcMetricsAutoConfiguration.class]: Unsatisfied dependency expressed through method 'webMvcMetricsFilter' parameter 0; nested exception is org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'prometheusMeterRegistry' defined in class path resource [org/springframework/boot/actuate/autoconfigure/metrics/export/prometheus/PrometheusMetricsExportAutoConfiguration.class]: Initialization of bean failed; nested exception is org.springframework.beans.factory.UnsatisfiedDependencyException: Error creating bean with name 'zipkin2.server.internal.ZipkinServerConfiguration': Unsatisfied dependency expressed through field 'httpQuery'; nested exception is org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'zipkin2.server.internal.ZipkinQueryApiV2': Bean instantiation via constructor failed; nested exception is java.lang.NoClassDefFoundError: zipkin2/internal/Buffer$Writer

目前不知道如何解決。

收集客戶端信息到Zipkin服務端

目前的服務組件有

  • Eureka註冊中心:
    springcloud-eureka-cluster-peer1
    springcloud-eureka-cluster-peer2
    springcloud-eureka-cluster-peer3

  • 服務提供者:
    springcloud-eureka-provider1
    springcloud-eureka-provider2
    springcloud-eureka-provider3

  • 服務消費者:
    springcloud-feign

爲每個組件都引入sleuth和對Zipkin支持的依賴

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

如下依賴包含以上兩個依賴

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

服務提供者和服務消費者的application.yml中配置:

spring:
  sleuth:
    web:
      client:
        enabled: true
    sampler:
      probability: 1.0
  zipkin:
    base-url: http://kitematic-zipkin.com:9411/
    enabled: true
  • spring.zipkin.base-url: zipkin服務端連接地址url
  • spring.zipkin.enabled: 允許開啓zipkin
  • spring.sleuth.sampler.probability:表示採樣率,1.0表示日誌全量傳遞到服務端,默認是 0.1

依次啓動項目

springcloud-eureka-cluster-peer1
springcloud-eureka-cluster-peer2
springcloud-eureka-cluster-peer3
springcloud-config-server
springcloud-eureka-provider1
springcloud-eureka-provider2
springcloud-eureka-provider3
springcloud-feign

瀏覽器訪問http://eureka-feign.com:8101/config?name=zhaojq後,
點擊查找會看到有一條記錄
在這裏插入圖片描述
點擊記錄,可以看到每一個服務所耗費的時間和順序
在這裏插入圖片描述
點擊應用名稱,還可以看到更多的信息:
在這裏插入圖片描述

整合RabbitMQ和MySQL實現持久化環境

RabbitMQ

在默認情況下,Zipkin客戶端和Server之間是使用HTTP請求的方式進行通信(即同步的請求方式),在網絡波動,Server端異常等情況下可能存在信息收集不及時的問題。Zipkin支持與RabbitMQ整合完成異步消息傳輸。

在這裏插入圖片描述

配置Zipkin服務端環境變量

在這裏插入圖片描述
可配置的環境變量如下表所示:

屬性 環境變量 描述
zipkin.collector.rabbitmq.concurrency RABBIT_CONCURRENCY 併發消費者數量,默認爲 1
zipkin.collector.rabbitmq.connection-timeout RABBIT_CONNECTION_TIMEOUT 建立連接時的超時時間,默認爲 60000 毫秒,即 1 分鐘
zipkin.collector.rabbitmq.queue RABBIT_QUEUE 從中獲取 span 信息的隊列,默認爲 zipkin
zipkin.collector.rabbitmq.uri RABBIT_URI 符合 RabbitMQ URI 規範 的 URI,例如 amqp://user:pass@host:10000/vhost

如果設置了 URI,則以下屬性將被忽略。

屬性 環境變量 描述
zipkin.collector.rabbitmq.addresses RABBIT_ADDRESSES 用逗號分隔的 RabbitMQ 地址列表,例如 localhost:5672,localhost:5673
zipkin.collector.rabbitmq.password RABBIT_PASSWORD 連接到 RabbitMQ 時使用的密碼,默認爲 guest
zipkin.collector.rabbitmq.username RABBIT_USER 連接到 RabbitMQ 時使用的用戶名,默認爲 guest
zipkin.collector.rabbitmq.virtual-host RABBIT_VIRTUAL_HOST 使用的 RabbitMQ virtual host,默認爲 /
zipkin.collector.rabbitmq.use-ssl RABBIT_USE_SSL 設置爲 true 則用 SSL 的方式與 RabbitMQ 建立鏈接

重啓Zipkin

查看 RabbitMQ Admin,多了一個名爲 zipkin 的 Queue
在這裏插入圖片描述
在這裏插入圖片描述
當 ZipkinServer不可用時(比如關閉、網絡不通等),追蹤信息不會丟失,因爲這些信息會保存在RabbitMQ服務器上,直到Zipkin服務器可用時,再從RabbitMQ中取出這段時間的信息

<dependency>
  <groupId>org.springframework.cloud</groupId>
  <artifactId>spring-cloud-stream-binder-rabbit</artifactId>
</dependency>
  rabbitmq:
    host: kitematic-rabbitmq.com
    port: 5672
    username: zhaojq
    password: 123456
    listener:
      direct:
        retry:
          enabled: true
      simple:
        retry:
          enabled: true

MySQL

Zipkin對數據的存儲默認是在內存中的,在企業生產環境中,一旦Service關閉重啓或者服務崩潰,就會導致歷史數據消失,需要持久化這些數據。Zipkin支持將追蹤數據持久化到mysql數據庫或者存儲到elasticsearch中。

在這裏插入圖片描述

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;

導入表結構:
在這裏插入圖片描述

配置Zipkin服務端環境變量

在這裏插入圖片描述

  • STORAGE_TYPE=mysql
  • MYSQL_DB=zipkin
  • MYSQL_USER=root
  • MYSQL_PASS=123456
  • MYSQL_HOST= 192.168.99.100
  • MYSQL_TCP_PORT=3306

依次啓動項目

springcloud-eureka-cluster-peer1
springcloud-eureka-cluster-peer2
springcloud-eureka-cluster-peer3
springcloud-eureka-provider1
springcloud-eureka-provider2
springcloud-eureka-provider3
springcloud-feign
springcloud-zuul

瀏覽器訪問http://eureka-zuul.com:8120/v1/hiapi/hi?name=zhaojq&token=cc&password=123456後,

點擊查找會看到有多條記錄
在這裏插入圖片描述
數據庫中也有記錄:
在這裏插入圖片描述

此時,關掉Zipkin服務端,並重新啓動;會發現,上面的信息還會有,並沒有被清除。Zipkin整合RabbitMQ+MySQL實現持久化環境搭建完畢。

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