sleuth主要功能是在分布式系統中提供追蹤解決方案,並且兼容支持了zipkin(提供了鏈路追蹤的可視化功能)
zipkin原理:在服務調用的請求和響應中加入ID,表明上下游請求的關系。
利用這些信息,可以可視化地分析服務調用鏈路和服務間的依賴關系。
sleuth是對zipkin的封裝,對應Span,Trace等信息的生成、接入http request,以及向Zipkin server發送采集信息等全部自動化完成。
目前主流的鏈路追蹤組件有:google的Dapper,Twitter的zipkin和阿里的Eagleeye(鷹眼)。
1. 搭建zipkin服務器
1.1 創建springboot項目,引入相應的jar依賴(其他依賴參考前面的教材)
<!-- 引入zipkin-server --> <dependency> <groupId>io.zipkin.java</groupId> <artifactId>zipkin-server</artifactId> </dependency>
<!-- 引入zipkin-server 圖形化界面 --> <dependency> <groupId>io.zipkin.java</groupId> <artifactId>zipkin-autoconfigure-ui</artifactId> </dependency>
1.2 添加配置文件
server:
port: 8769
spring:
application:
name: zipkin-server
eureka:
client:
serviceUrl:
defaultZone: http://localhost:8761/eureka/ #注冊服務器地址
management:
security:
enabled: false #關閉驗證
info: #/info請求的顯示信息
app:
name: ${spring.application.name}
version: 1.0.0
build:
artifactId: @project.artifactId@
version: @project.version@
1.3 在啟動類中聲明為zipkin服務器
@SpringBootApplication @EnableDiscoveryClient @EnableZipkinServer //zipkin服務器 默認使用http進行通信 public class ZipkinServerApp { public static void main(String[] args) { SpringApplication.run(ZipkinServerApp.class, args); } }
1.4 啟動查看效果

2 分別在cloud-consumer-ribbon和cloud-consumer-feign中引入zipkin
2.1 引入zipkin依賴
<!-- 配置服務鏈路追蹤 --> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-zipkin</artifactId> </dependency>
或
<!-- 配置服務鏈路追蹤 --> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-sleuth-zipkin</artifactId> </dependency> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-sleuth</artifactId> </dependency>
2.2 在配置文件中聲明zipkin服務器的地址
spring:
zipkin:
base-url: http://localhost:8769
2.3 在項目中調用需要的服務
@RequestMapping("hello")
public String helloConsumer() {
//使用restTemplate調用消費服務提供者的SERVICE-HI的info服務
//String response=restTemplate.getForObject("http://cloud-consumer-feign/hi", String.class);
String response=hiService.sayHello()+" ribbon";
logger.info(response);
return response;
}
2.4 啟動項目查看



有時候可能在zipkin服務器中看不到數據,那是因為默認sleuth收集信息的比率是0.1 ,針對於這個問題有兩種解決方法:
a 在配置文件中配置 spring.sleuth.sampler.percentage=1
b 在代碼中聲明
//100%的來采集日志,和在配置文件中配置spring.sleuth.sampler.percentage=1是一樣的 @Bean public AlwaysSampler defaultSampler(){ return new AlwaysSampler(); }
但這樣每個請求都會向zipkin server發送http請求,通信效率低,造成網絡延遲。
而且所用的追蹤信息都在內存中保存,重啟zipkin server后信息丟失
針對以上的問題的解決方法:
a 采用socket或高效率的通信方式
b 采用異步方式發送信息數據
c 在客戶端和zipkin之間增加緩存類的中間件,如redis,mq等,即時zipkin server重啟過程中,客戶端依然可以將數據發送成功
3 將http通信改為mq異步通信方式
3.1 修改zipkin server
3.1.1 將原來的依賴io.zipkin.java:zipkin-server換成spring-cloud-sleuth-zipkin-stream和spring-cloud-starter-stream-rabbit
<!-- 引入zipkin-server --> <!-- <dependency> <groupId>io.zipkin.java</groupId> <artifactId>zipkin-server</artifactId> </dependency> --> <!-- 將http請求修改為mq請求 --> <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> </dependency>
3.1.2 在配置文件中配置ribbitmq地址
spring:
application:
name: zipkin-server
rabbitmq: #配置mq消息隊列
host: localhost
port: 5672
username: guest
password: guest
3.1.3 在啟動類中使用@EnableZipkinStreamServer替換@EnableZipkinServer
@SpringBootApplication @EnableDiscoveryClient //@EnableZipkinServer //zipkin服務器 默認使用http進行通信 @EnableZipkinStreamServer //采用stream方式啟動zipkin server ,也支持http通信 包含了@EnableZipkinServer,同時創建了rabbit-mq消息隊列監聽器 public class ZipkinServerApp { public static void main(String[] args) { SpringApplication.run(ZipkinServerApp.class, args); } }
3.2 配置客戶端(這里只配置一個客戶端,其他的客戶端配置一樣配置即可)
3.2.1 將原來的spring-cloud-starter-zipkin依賴,使用以下依賴進行替換
<!-- 配置服務鏈路追蹤 --> <!-- <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-zipkin</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> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-sleuth</artifactId> </dependency>
3.2.2 在配置文件中加入ribbitmq的配置
spring:
rabbitmq: #配置mq消息隊列
host: localhost
port: 5672
username: guest
password: guest
3.3 現在啟動其他項目,不啟動zipkin server,進行項目訪問,會把追蹤信息放入到ribbitmq中,當zipkin server啟動后會直接沖zipkin server中獲取信息


4 將追蹤信息保存到數據庫(只需修改zipkin server即可)
4.1 引入mysql數據庫依賴
<!-- 配置mysql --> <dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-jdbc</artifactId> </dependency>
4.2 在配置文件msql連接
spring: sleuth: enabled: false #表示當前程序不使用sleuth datasource: #配置msyql 連接 schema[0]: classpath:/zipkin.sql #數據庫創建腳本,可以到官網下載 url: jdbc:mysql://localhost:3306/zipkin?autoReconnect=true&useUnicode=true&characterEncoding=UTF-8&zeroDateTimeBehavior=convertToNull&useSSL=false username: root password: root driver-class-name: com.mysql.jdbc.Driver initialize: true continue-on-error: true zipkin: storage: type: mysql #mysql存儲zipkin追蹤信息
4.3 創建數據庫和表
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 ) ENGINE=InnoDB ROW_FORMAT=COMPRESSED CHARACTER SET=utf8 COLLATE utf8_general_ci; ALTER TABLE zipkin_dependencies ADD UNIQUE KEY(`day`, `parent`, `child`);
