微服務(wù)監(jiān)控之分布式鏈路追蹤技術(shù) Sleuth + Zipkin
1.問題場景
為了?撐?益增?的龐?業(yè)務(wù)量,我們會使?微服務(wù)架構(gòu)設(shè)計我們的系統(tǒng),使得我們的系統(tǒng)不僅能
夠通過集群部署抵擋流量的沖擊,?能根據(jù)業(yè)務(wù)進?靈活的擴展。
那么,在微服務(wù)架構(gòu)下,?次請求少則經(jīng)過三四次服務(wù)調(diào)?完成,多則跨越??個甚?是上百個服
務(wù)節(jié)點。那么問題接踵?來:
1)如何動態(tài)展示服務(wù)的調(diào)?鏈路?(?如A服務(wù)調(diào)?了哪些其他的服務(wù)---依賴關(guān)系)
2)如何分析服務(wù)調(diào)?鏈路中的瓶頸節(jié)點并對其進?調(diào)優(yōu)?(?如A—>B—>C,C服務(wù)處理時間特別
?)
3)如何快速進?服務(wù)鏈路的故障發(fā)現(xiàn)?
分布式鏈路追蹤技術(shù)
如果我們在?個請求的調(diào)?處理過程中,在各個鏈路節(jié)點都能夠記錄下?志,并最終將?志進?集
中可視化展示,那么我們想監(jiān)控調(diào)?鏈路中的?些指標就有希望了~~~?如,請求到達哪個服務(wù)實
例?請求被處理的狀態(tài)怎樣?處理耗時怎樣?這些都能夠分析出來了...
分布式環(huán)境下基于這種想法實現(xiàn)的監(jiān)控技術(shù)就是就是分布式鏈路追蹤(全鏈路追蹤)。
市場上的分布式鏈路追蹤?案
分布式鏈路追蹤技術(shù)已然成熟,產(chǎn)品也不少,國內(nèi)外都有,?如
Spring Cloud Sleuth + Twitter Zipkin
阿?巴巴的“鷹眼”
?眾點評的“CAT”
美團的“Mtrace”
京東的“Hydra”
新浪的“Watchman”
另外還有最近也被提到很多的Apache Skywalking。
2.分布式鏈路追蹤技術(shù)核?思想
本質(zhì):記錄?志
為了追蹤整個調(diào)?鏈路,肯定需要記錄?志,?志記錄是基礎(chǔ),在此之上肯定有?些理論概念,當下主
流的的分布式鏈路追蹤技術(shù)/系統(tǒng)所基于的理念都來?于Google的?篇論?《Dapper, a Large-Scale
Distributed Systems Tracing Infrastructure》,核?理念如下:
Trace:服務(wù)追蹤的追蹤單元是從客戶發(fā)起請求(request)抵達被追蹤系統(tǒng)的邊界開始,到被追蹤系統(tǒng)
向客戶返回響應(yīng)(response)為?的過程
Trace ID:為了實現(xiàn)請求跟蹤,當請求發(fā)送到分布式系統(tǒng)的??端點時,只需要服務(wù)跟蹤框架為該請求
創(chuàng)建?個唯?的跟蹤標識Trace ID,同時在分布式系統(tǒng)內(nèi)部流轉(zhuǎn)的時候,框架失蹤保持該唯?標識,直
到返回給請求?
?個Trace由?個或者多個Span組成,每?個Span都有?個SpanId,Span中會記錄TraceId,同時還有
?個叫做ParentId,指向了另外?個Span的SpanId,表明??關(guān)系,其實本質(zhì)表達了依賴關(guān)系
Span ID:為了統(tǒng)計各處理單元的時間延遲,當請求到達各個服務(wù)組件時,也是通過?個唯?標識Span
ID來標記它的開始,具體過程以及結(jié)束。對每?個Span來說,它必須有開始和結(jié)束兩個節(jié)點,通過記錄
開始Span和結(jié)束Span的時間戳,就能統(tǒng)計出該Span的時間延遲,除了時間戳記錄之外,它還可以包含
?些其他元數(shù)據(jù),?如時間名稱、請求信息等。
每?個Span都會有?個唯?跟蹤標識 Span ID,若?個有序的 span 就組成了?個 trace。
Span可以認為是?個?志數(shù)據(jù)結(jié)構(gòu),在?些特殊的時機點會記錄了?些?志信息,?如有時間戳、
spanId、TraceId,parentIde等,Span中也抽象出了另外?個概念,叫做事件,核?事件如下
CS :client send/start 客戶端/消費者發(fā)出?個請求,描述的是?個span開始
SR: server received/start 服務(wù)端/?產(chǎn)者接收請求 SR-CS屬于請求發(fā)送的?絡(luò)延遲
SS: server send/fifinish 服務(wù)端/?產(chǎn)者發(fā)送應(yīng)答 SS-SR屬于服務(wù)端消耗時間
CR:client received/fifinished 客戶端/消費者接收應(yīng)答 CR-SS表示回復(fù)需要的時間(響應(yīng)的?絡(luò)延
遲)
Spring Cloud Sleuth (追蹤服務(wù)框架)可以追蹤服務(wù)之間的調(diào)?,Sleuth可以記錄?個服務(wù)請求經(jīng)過哪
些服務(wù)、服務(wù)處理時?等,根據(jù)這些,我們能夠理清各微服務(wù)間的調(diào)?關(guān)系及進?問題追蹤分析。
耗時分析:通過 Sleuth 了解采樣請求的耗時,分析服務(wù)性能問題(哪些服務(wù)調(diào)??較耗時)
鏈路優(yōu)化:發(fā)現(xiàn)頻繁調(diào)?的服務(wù),針對性優(yōu)化等
Sleuth就是通過記錄?志的?式來記錄蹤跡數(shù)據(jù)的
注意:我們往往把Spring Cloud Sleuth 和 Zipkin ?起使?,把 Sleuth 的數(shù)據(jù)信息發(fā)送給 Zipkin 進
?聚合,利? Zipkin 存儲并展示數(shù)據(jù)。
3. Sleuth + Zipkin
1)每?個需要被追蹤蹤跡的微服務(wù)?程都引?依賴坐標
<!--鏈路追蹤-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-sleuth</artifactId>
</dependency>
2)每?個微服務(wù)都修改application.yml配置?件,添加?志級別
#分布式鏈路追蹤
logging:
level:
org.springframework.web.servlet.DispatcherServlet: debug
org.springframework.cloud.sleuth: debug
3)結(jié)合 Zipkin 展示追蹤數(shù)據(jù)
Zipkin 包括Zipkin Server和 Zipkin Client兩部分,Zipkin Server是?個單獨的服務(wù),Zipkin Client就是具體的微服務(wù)
-
Zipkin Server 構(gòu)建
<!--zipkin-server的依賴坐標--> <dependency> <groupId>io.zipkin.java</groupId> <artifactId>zipkin-server</artifactId> <version>2.12.3</version> <exclusions> <!--排除掉log4j2的傳遞依賴,避免和springboot依賴的?志組件沖突--> <exclusion> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-log4j2</artifactId> </exclusion> </exclusions> </dependency> <!--zipkin-server ui界?依賴坐標--> <dependency> <groupId>io.zipkin.java</groupId> <artifactId>zipkin-autoconfigure-ui</artifactId> <version>2.12.3</version> </dependency>
-
添加注解啟動
@EnableZipkinServer
-
添加配置
management: metrics: web: server: auto-time-requests: false # 關(guān)閉?動檢測請求
-
Zipkin Client 構(gòu)建
<dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-zipkin</artifactId> </dependency>
-
配置:添加對zipkin server的引?
spring: zipkin: base-url: http://127.0.0.1:9411 # zipkin server的請求地址 sender: # web 客戶端將蹤跡?志數(shù)據(jù)通過?絡(luò)請求的?式傳送到服務(wù)端,另外還有配置 # kafka/rabbit 客戶端將蹤跡?志數(shù)據(jù)傳遞到mq進?中轉(zhuǎn) type: web sleuth: sampler: # 采樣率 1 代表100%全部采集 ,默認0.1 代表10% 的請求蹤跡數(shù)據(jù)會被采集 # ?產(chǎn)環(huán)境下,請求量?常?,沒有必要所有請求的蹤跡數(shù)據(jù)都采集分析,對于?絡(luò)包括server端壓?都是?較?的,可以配置采樣率采集?定?例的請求的蹤跡數(shù)據(jù)進?分析即可 probability: 1
3.1 Zipkin持久化到mysql
-
引入依賴
<dependency> <groupId>io.zipkin.java</groupId> <artifactId>zipkin-autoconfigure-storage?mysql</artifactId> <version>2.12.3</version> </dependency> <dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> </dependency> <dependency> <groupId>com.alibaba</groupId> <artifactId>druid-spring-boot-starter</artifactId> <version>1.1.10</version> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-tx</artifactId> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-jdbc</artifactId> </dependency>
-
配置yaml
# 指定zipkin持久化介質(zhì)為mysql zipkin: storage: type: mysql
-
啟動類中注?事務(wù)管理器
@Bean public PlatformTransactionManager txManager(DataSource dataSource) { return new DataSourceTransactionManager(dataSource); }- 初始數(shù)據(jù)庫腳本
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;