微服務(wù)監(jiān)控之分布式鏈路追蹤技術(shù) Sleuth + Zipkin
1.問(wèn)題場(chǎng)景
為了?撐?益增?的龐?業(yè)務(wù)量卦睹,我們會(huì)使?微服務(wù)架構(gòu)設(shè)計(jì)我們的系統(tǒng),使得我們的系統(tǒng)不僅能
夠通過(guò)集群部署抵擋流量的沖擊,?能根據(jù)業(yè)務(wù)進(jìn)?靈活的擴(kuò)展。
那么泣港,在微服務(wù)架構(gòu)下,?次請(qǐng)求少則經(jīng)過(guò)三四次服務(wù)調(diào)?完成价匠,多則跨越??個(gè)甚?是上百個(gè)服
務(wù)節(jié)點(diǎn)当纱。那么問(wèn)題接踵?來(lái):
1)如何動(dòng)態(tài)展示服務(wù)的調(diào)?鏈路?(?如A服務(wù)調(diào)?了哪些其他的服務(wù)---依賴關(guān)系)
2)如何分析服務(wù)調(diào)?鏈路中的瓶頸節(jié)點(diǎn)并對(duì)其進(jìn)?調(diào)優(yōu)踩窖?(?如A—>B—>C坡氯,C服務(wù)處理時(shí)間特別
?)
3)如何快速進(jìn)?服務(wù)鏈路的故障發(fā)現(xiàn)?
分布式鏈路追蹤技術(shù)
如果我們?cè)?個(gè)請(qǐng)求的調(diào)?處理過(guò)程中,在各個(gè)鏈路節(jié)點(diǎn)都能夠記錄下?志箫柳,并最終將?志進(jìn)?集
中可視化展示手形,那么我們想監(jiān)控調(diào)?鏈路中的?些指標(biāo)就有希望了~~~?如,請(qǐng)求到達(dá)哪個(gè)服務(wù)實(shí)
例悯恍?請(qǐng)求被處理的狀態(tài)怎樣库糠?處理耗時(shí)怎樣?這些都能夠分析出來(lái)了...
分布式環(huán)境下基于這種想法實(shí)現(xiàn)的監(jiān)控技術(shù)就是就是分布式鏈路追蹤(全鏈路追蹤)坪稽。
市場(chǎng)上的分布式鏈路追蹤?案
分布式鏈路追蹤技術(shù)已然成熟曼玩,產(chǎn)品也不少鳞骤,國(guó)內(nèi)外都有窒百,?如
Spring Cloud Sleuth + Twitter Zipkin
阿?巴巴的“鷹眼”
?眾點(diǎn)評(píng)的“CAT”
美團(tuán)的“Mtrace”
京東的“Hydra”
新浪的“Watchman”
另外還有最近也被提到很多的Apache Skywalking。
2.分布式鏈路追蹤技術(shù)核?思想
本質(zhì):記錄?志
為了追蹤整個(gè)調(diào)?鏈路豫尽,肯定需要記錄?志篙梢,?志記錄是基礎(chǔ),在此之上肯定有?些理論概念美旧,當(dāng)下主
流的的分布式鏈路追蹤技術(shù)/系統(tǒng)所基于的理念都來(lái)?于Google的?篇論?《Dapper, a Large-Scale
Distributed Systems Tracing Infrastructure》,核?理念如下:
Trace:服務(wù)追蹤的追蹤單元是從客戶發(fā)起請(qǐng)求(request)抵達(dá)被追蹤系統(tǒng)的邊界開(kāi)始渤滞,到被追蹤系統(tǒng)
向客戶返回響應(yīng)(response)為?的過(guò)程
Trace ID:為了實(shí)現(xiàn)請(qǐng)求跟蹤,當(dāng)請(qǐng)求發(fā)送到分布式系統(tǒng)的??端點(diǎn)時(shí)榴嗅,只需要服務(wù)跟蹤框架為該請(qǐng)求
創(chuàng)建?個(gè)唯?的跟蹤標(biāo)識(shí)Trace ID妄呕,同時(shí)在分布式系統(tǒng)內(nèi)部流轉(zhuǎn)的時(shí)候,框架失蹤保持該唯?標(biāo)識(shí)嗽测,直
到返回給請(qǐng)求?
?個(gè)Trace由?個(gè)或者多個(gè)Span組成绪励,每?個(gè)Span都有?個(gè)SpanId,Span中會(huì)記錄TraceId唠粥,同時(shí)還有
?個(gè)叫做ParentId疏魏,指向了另外?個(gè)Span的SpanId,表明??關(guān)系晤愧,其實(shí)本質(zhì)表達(dá)了依賴關(guān)系
Span ID:為了統(tǒng)計(jì)各處理單元的時(shí)間延遲大莫,當(dāng)請(qǐng)求到達(dá)各個(gè)服務(wù)組件時(shí),也是通過(guò)?個(gè)唯?標(biāo)識(shí)Span
ID來(lái)標(biāo)記它的開(kāi)始官份,具體過(guò)程以及結(jié)束只厘。對(duì)每?個(gè)Span來(lái)說(shuō),它必須有開(kāi)始和結(jié)束兩個(gè)節(jié)點(diǎn)舅巷,通過(guò)記錄
開(kāi)始Span和結(jié)束Span的時(shí)間戳羔味,就能統(tǒng)計(jì)出該Span的時(shí)間延遲,除了時(shí)間戳記錄之外悄谐,它還可以包含
?些其他元數(shù)據(jù)介评,?如時(shí)間名稱、請(qǐng)求信息等。
每?個(gè)Span都會(huì)有?個(gè)唯?跟蹤標(biāo)識(shí) Span ID,若?個(gè)有序的 span 就組成了?個(gè) trace们陆。
Span可以認(rèn)為是?個(gè)?志數(shù)據(jù)結(jié)構(gòu)寒瓦,在?些特殊的時(shí)機(jī)點(diǎn)會(huì)記錄了?些?志信息,?如有時(shí)間戳坪仇、
spanId杂腰、TraceId,parentIde等椅文,Span中也抽象出了另外?個(gè)概念喂很,叫做事件,核?事件如下
CS :client send/start 客戶端/消費(fèi)者發(fā)出?個(gè)請(qǐng)求皆刺,描述的是?個(gè)span開(kāi)始
SR: server received/start 服務(wù)端/?產(chǎn)者接收請(qǐng)求 SR-CS屬于請(qǐng)求發(fā)送的?絡(luò)延遲
SS: server send/fifinish 服務(wù)端/?產(chǎn)者發(fā)送應(yīng)答 SS-SR屬于服務(wù)端消耗時(shí)間
CR:client received/fifinished 客戶端/消費(fèi)者接收應(yīng)答 CR-SS表示回復(fù)需要的時(shí)間(響應(yīng)的?絡(luò)延
遲)
Spring Cloud Sleuth (追蹤服務(wù)框架)可以追蹤服務(wù)之間的調(diào)?少辣,Sleuth可以記錄?個(gè)服務(wù)請(qǐng)求經(jīng)過(guò)哪
些服務(wù)、服務(wù)處理時(shí)?等羡蛾,根據(jù)這些漓帅,我們能夠理清各微服務(wù)間的調(diào)?關(guān)系及進(jìn)?問(wèn)題追蹤分析。
耗時(shí)分析:通過(guò) Sleuth 了解采樣請(qǐng)求的耗時(shí)痴怨,分析服務(wù)性能問(wèn)題(哪些服務(wù)調(diào)??較耗時(shí))
鏈路優(yōu)化:發(fā)現(xiàn)頻繁調(diào)?的服務(wù)忙干,針對(duì)性優(yōu)化等
Sleuth就是通過(guò)記錄?志的?式來(lái)記錄蹤跡數(shù)據(jù)的
注意:我們往往把Spring Cloud Sleuth 和 Zipkin ?起使?,把 Sleuth 的數(shù)據(jù)信息發(fā)送給 Zipkin 進(jìn)
?聚合浪藻,利? Zipkin 存儲(chǔ)并展示數(shù)據(jù)捐迫。
3. Sleuth + Zipkin
1)每?個(gè)需要被追蹤蹤跡的微服務(wù)?程都引?依賴坐標(biāo)
<!--鏈路追蹤-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-sleuth</artifactId>
</dependency>
2)每?個(gè)微服務(wù)都修改application.yml配置?件,添加?志級(jí)別
#分布式鏈路追蹤
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是?個(gè)單獨(dú)的服務(wù)施戴,Zipkin Client就是具體的微服務(wù)
-
Zipkin Server 構(gòu)建
<!--zipkin-server的依賴坐標(biāo)--> <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界?依賴坐標(biāo)--> <dependency> <groupId>io.zipkin.java</groupId> <artifactId>zipkin-autoconfigure-ui</artifactId> <version>2.12.3</version> </dependency>
-
添加注解啟動(dòng)
@EnableZipkinServer
-
添加配置
management: metrics: web: server: auto-time-requests: false # 關(guān)閉?動(dòng)檢測(cè)請(qǐng)求
-
Zipkin Client 構(gòu)建
<dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-zipkin</artifactId> </dependency>
-
配置:添加對(duì)zipkin server的引?
spring: zipkin: base-url: http://127.0.0.1:9411 # zipkin server的請(qǐng)求地址 sender: # web 客戶端將蹤跡?志數(shù)據(jù)通過(guò)?絡(luò)請(qǐng)求的?式傳送到服務(wù)端钧惧,另外還有配置 # kafka/rabbit 客戶端將蹤跡?志數(shù)據(jù)傳遞到mq進(jìn)?中轉(zhuǎn) type: web sleuth: sampler: # 采樣率 1 代表100%全部采集 暇韧,默認(rèn)0.1 代表10% 的請(qǐng)求蹤跡數(shù)據(jù)會(huì)被采集 # ?產(chǎn)環(huán)境下,請(qǐng)求量?常?浓瞪,沒(méi)有必要所有請(qǐng)求的蹤跡數(shù)據(jù)都采集分析懈玻,對(duì)于?絡(luò)包括server端壓?都是?較?的,可以配置采樣率采集?定?例的請(qǐng)求的蹤跡數(shù)據(jù)進(jìn)?分析即可 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
-
啟動(dòng)類中注?事務(wù)管理器
@Bean public PlatformTransactionManager txManager(DataSource dataSource) { return new DataSourceTransactionManager(dataSource); }
- 初始數(shù)據(jù)庫(kù)腳本
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;