??Spring Cloud Sleuth是Spring Cloud的一個組件俯抖,它的主要功能是在分布式系統(tǒng)中提供服務(wù)鏈路追蹤的解決方案。
一凑阶、Spring Cloud Sleuth介紹
??微服務(wù)架構(gòu)是一個分布式架構(gòu)猿规,微服務(wù)系統(tǒng)按業(yè)務(wù)劃分服務(wù)單元,一個微服務(wù)系統(tǒng)往往有很多個服務(wù)單元晌砾。由于服務(wù)單元數(shù)量眾多坎拐,業(yè)務(wù)的復(fù)雜性較高烦磁,如果出現(xiàn)了錯誤和異常养匈,很難去定位哼勇。主要體現(xiàn)在一個請求可能需要調(diào)用很多個服務(wù),而內(nèi)部服務(wù)的調(diào)用復(fù)雜性決定了問題難以定位呕乎。所以在微服務(wù)架構(gòu)中积担,必須實現(xiàn)分布式鏈路追蹤,去跟進一個請求到底有哪些服務(wù)參與猬仁,參與的順序又是怎樣的帝璧,從而達到每個請求的步驟清晰可見,出了問題能夠快速定位的目的湿刽。
??目前的烁,常見的鏈路追蹤組件有Google的Dapper、Twitter的Zipkin诈闺,以及阿里的Eagleeye (鷹眼)等渴庆,它們都是非常優(yōu)秀的鏈路追蹤開源組件。
??這里主要講述如何在Spring Cloud Sleuth中集成Zipkin雅镊,在Spring Cloud Sleuth中集成Zipkin非常簡單襟雷,只需要引入相應(yīng)的依賴并做相關(guān)的配置即可。
Zipkin介紹
??Zipkin是一個分布式跟蹤系統(tǒng):它可以幫助收集時間數(shù)據(jù)仁烹,解決在微服務(wù)架構(gòu)下的延遲問題耸弄;它管理這些數(shù)據(jù)的收集和查找;Zipkin的設(shè)計是基于谷歌的Google Dapper論文卓缰。
??每個應(yīng)用程序向Zipkin報告定時數(shù)據(jù)计呈,Zipkin UI呈現(xiàn)了一個依賴圖表來展示多少跟蹤請求經(jīng)過了每個應(yīng)用程序;如果想解決延遲問題征唬,可以過濾或者排序所有的跟蹤請求震叮,并且可以查看每個跟蹤請求占總跟蹤時間的百分比。
二鳍鸵、基本術(shù)語
Spring Cloud Sleuth 采用了Google的開源項目Dapper的專業(yè)術(shù)語苇瓣。
(1) Span: 基本工作單元,發(fā)送一個遠程調(diào)度任務(wù)就會產(chǎn)生一個Span偿乖,Span是用一個64位ID唯一標(biāo)識的击罪,Trace是用另一個64位ID唯一標(biāo)識的。 Span還包含了其他的信息贪薪,例如摘要媳禁、時間戳事件、Span的ID以及進程ID画切。
(2) Trace:由一系列Span組成的竣稽,呈樹狀結(jié)構(gòu)。請求一個微服務(wù)系統(tǒng)的API接口,這個API接口需要調(diào)用多個微服務(wù)單元毫别,調(diào)用每個微服務(wù)單元都會產(chǎn)生一個新的Span娃弓, 所有由這個請求產(chǎn)生的Span組成了這個Trace。
(3) Annotation:用于記錄一個事件岛宦,一些核心注解用于定義一個請求的開始和結(jié)束台丛,這些注解如下。
- cs-Client Sent: 客戶端發(fā)送一個請求砾肺,這個注解描述了Span的開始挽霉。
- sr-Server Received:服務(wù)端獲得請求并準備開始處理它,如果將其sr減去cs時間戳变汪,
便可得到網(wǎng)絡(luò)傳輸?shù)臅r間侠坎。 - ss-Server Sent:服務(wù)端發(fā)送響應(yīng),該注解表明請求處理的完成(當(dāng)請求返回客戶端) 裙盾,
用ss的時間戳減去sr時間戳硅蹦,便可以得到服務(wù)器請求的時間。 - er-Client Received: 客戶端接收響應(yīng)闷煤, 此時Span結(jié)束童芹,如果er的時間戳減去cs時間
戳,便可以得到整個請求所消耗的時間鲤拿。
三假褪、代碼實現(xiàn)
這里繼續(xù)在之前的工程項目中實現(xiàn),使用eureka-server Module作為服務(wù)注冊中心,新建zipkin-server Module作為鏈路追蹤服務(wù)中心近顷,負責(zé)存儲鏈路數(shù)據(jù)生音。gateway-service Module作為服務(wù)網(wǎng)關(guān)工程,負責(zé)請求的轉(zhuǎn)發(fā)窒升,同時它也作為鏈路追蹤客戶端缀遍,負責(zé)產(chǎn)生鏈路數(shù)據(jù),并上傳給 zipkin-server饱须。 eureka-producer Module是一個服務(wù)提供者域醇,對外暴露API接口,同時它也作為鏈路追蹤客戶端蓉媳,負責(zé)產(chǎn)生鏈路數(shù)據(jù)譬挚。
3.1 構(gòu)建Zipkin-Server
新建一個 Module工程,取名為 zipkin-server,工程的pom文件繼承了主Maven工程的pom文件酪呻,并引入Eureka的起步依賴spring-cloud-starter-eureka减宣、Zipkin Server的依賴zipkin-server,以及Zipkin Server的UI界面依賴zipkin-autoconfigure-ui玩荠。后兩個依賴提供了Zipkin的 功能和Zipkin界面展示的功能 漆腌,代碼如下 :
<parent>
<groupId>com.hand</groupId>
<artifactId>macro-service</artifactId>
<version>1.0-SNAPSHOT</version>
<relativePath/>
</parent>
<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>
</dependencies>
在程序的啟動類ZipkinServiceApplication加上@EnableZipkinServer 注解贼邓,開啟ZipkinServer 的功能,加上@EnableEurekaClient注解闷尿, 啟動EurekaClient塑径,代碼如下:
@SpringBootApplication
@EnableEurekaClient
@EnableZipkinServer
public class ZipkinServerApplication {
public static void main(String[] args) {
SpringApplication.run(ZipkinServerApplication.class, args);
}
}
在程序的配置文件application.yml文件做程序的相關(guān)配置,指定程序名為zipkin-server, 端口號為9411悠砚, 服務(wù)注冊地址為http://localhost:8671/eureka/,代碼如下:
spring:
application:
name: zipkin-server
eureka:
client:
service-url:
defaultZone: http://localhost:8671/eureka/
server:
port: 9411
3.2 構(gòu)建Eureka-Producer
在主Maven工程下建一個Module工程 堂飞,取名為eureka-producer灌旧,作為服務(wù)提供者,對外暴露API接口绰筛。eureka-producer工程的pom文件繼承了主Maven工程的pom文件枢泰,并引入了Eureka的起步依賴spring-cloud-starter-eureka、Web起步依賴 spring-boot-starter-web铝噩,以及 Zipkin 的起步依賴 spring-cloud-starter-zipkin衡蚂,代碼如下:
<parent>
<groupId>com.hand</groupId>
<artifactId>macro-service</artifactId>
<version>1.0-SNAPSHOT</version>
<relativePath/>
</parent>
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-eureka</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-zipkin</artifactId>
<version>RELEASE</version>
</dependency>
</dependencies>
在程序的配置文applicatiom.yml中,指定程序名為eureka-producer骏庸,端口號為8672毛甲,服務(wù)
注冊地址http://localhost:8671/eureka/, Zipkin Server地址為 http://localhost:9411。spring.sleuth.sampler.percentage為 1.0具被,即以100%的概率將鏈路的數(shù)據(jù)上傳給Zipkin Server玻募,在默認情況下,該值為 0.1一姿。配置文件代碼如下:
client:
service-url:
defaultZone: http://localhost:8671/eureka/
spring:
application:
name: eureka-producer
zipkin:
base-url: http://localhost:9411
sleuth:
sampler:
percentage: 1.0
server:
port: 8672
在 UserController類建一個“/hi”的 API接口七咧,對外提供服務(wù), 代碼如下:
@RestController
public class UserController {
@GetMapping("/hi")
public String SayHi(){
return "hi, i'm ben";
}
}
最后作為Eureka Client叮叹, 需要在程序的啟動類EurekaProducerApplication加上@EnableEurekaClient注解艾栋,開啟Eureka Client的功能 。
3.3 構(gòu)建Gateway-Service
新建一個名為gateway-service的工程蛉顽,這個工程作為服務(wù)網(wǎng)關(guān)蝗砾,將請求轉(zhuǎn)發(fā)到eureka-producer。 作為Zipkin客戶端 携冤, 需要將鏈路數(shù)據(jù)上傳給Zipkin Server遥诉,同時它也作為Eureka Client。在工程的pom文件中除了需要繼承主Maven工程的pom文件噪叙,還需引入如下的依賴矮锈,代碼如下:
<parent>
<groupId>com.hand</groupId>
<artifactId>macro-service</artifactId>
<version>1.0-SNAPSHOT</version>
<relativePath/>
</parent>
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-eureka</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-zuul</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-zipkin</artifactId>
<version>RELEASE</version>
</dependency>
</dependencies>
在工程的配置文件application.yml中,配置程序名為gateway-service睁蕾,端口號為5000苞笨,服務(wù)注 冊地址為http://localhost:8671/eureka/, Zipkin Server地址為http://localhost:9411 债朵,以 “/user-api/**"開頭的Url請求轉(zhuǎn)發(fā)到服務(wù)名為eureka-producer的服務(wù),配置代碼如下:
spring:
application:
name: gateway-service
sleuth:
sampler:
percentage: 1.0
zipkin:
base-url: http://localhost:9411
server:
port: 5000
eureka:
client:
service-url:
defaultZone: http://localhost:8671/eureka/
zuul:
routes:
user-api:
path: "/user-api/**"
serviceId: eureka-producer
在程序的啟動類 GatewayServiceApplication 上加@EnableEurekaClient 注解 瀑凝, Client的功能序芦,加上@EnableZuulProxy注解, 開啟 Zuul代理功能粤咪,代碼如下:
@SpringBootApplication
@EnableZuulProxy
@EnableEurekaClient
public class GatewayServiceApplication {
public static void main(String[] args) {
SpringApplication.run(GatewayServiceApplication.class, args);
}
}
3.4 項目演示
完整的項目搭建完畢谚中,依次啟動 eureka-server、zipkin-server寥枝、eureka-producer和gateway-service宪塔。在瀏覽器上訪問http://localhost:5000/user-api/hi, 瀏覽器顯示如下:
訪問http://localhost:9411囊拜,即訪問Zipkin的展示界面某筐, 界面顯示下圖所示。
這個界面用于展示Zipkin Server 收集的鏈路數(shù)據(jù)冠跷,可以根據(jù)服務(wù)名南誊、開始時間、結(jié)束時間蜜托、 請求消耗的時間等條件來查找抄囚。 單擊“FindTraces”按鈕,界面如下圖所示橄务,從圖中可知請 求的調(diào)用情況怠苔,例如請求的調(diào)用時間、消耗時間仪糖,以及請求調(diào)用的鏈路情況柑司。
單擊“ Dependences”按鈕,可以查看服務(wù)的依賴關(guān)系锅劝。在本案例中攒驰,gateway-service 將
請求轉(zhuǎn)發(fā)到了eureka-producer,這兩個服務(wù)的依賴關(guān)系如下圖所示故爵。
四玻粪、在鏈路數(shù)據(jù)中添加自定義數(shù)據(jù)
我們除了可以通過zipkin獲取鏈路數(shù)據(jù)外,還可以項鏈路數(shù)據(jù)中添加自定義數(shù)據(jù)诬垂。本案例在gateway-service服務(wù)中實現(xiàn) 劲室。在gateway-service工程里新建一個 ZuulFilter過濾器,它的類型為 post類型结窘, order為900很洋,開啟攔截。在過濾器的攔截邏輯方法里 隧枫, 通過Tracer的addTag 方法加上自定義的數(shù)據(jù)喉磁,在這里我加上了鏈路的操作人谓苟。另外也可以在這個過濾器中獲取當(dāng)前鏈路的traceld信息, traceld作為鏈路數(shù)據(jù)的唯一標(biāo)識协怒,可以存儲在log日志中涝焙,方便后續(xù)查找,這里只是將traceld信息簡單地打印在控制臺上孕暇。 代碼如下:
@Component
public class LoggerFilter extends ZuulFilter{
public static String PRE_TYPE = "pre";
@Autowired
Tracer tracer;
@Override
public String filterType() {
return PRE_TYPE;
}
@Override
public int filterOrder() {
return 900;
}
@Override
public boolean shouldFilter() {
return true;
}
@Override
public Object run() {
tracer.addTag("operator", "ben");
System.out.println("traceId: " + tracer.getCurrentSpan().getTraceId());
return null;
}
}
重新部署項目仑撞,訪問http://localhost:5000/user-api/hi,然后訪問http://localhost:9411妖滔,可以看到操作人已經(jīng)被添加到鏈路數(shù)據(jù)中了隧哮。如下圖:
后臺也成功輸出traceId
五、使用RabbitMQ傳輸鏈路數(shù)據(jù)
??從上面的程序可知铛楣,最終gateway-service收集的數(shù)據(jù)是通過Http方式上傳給zipkin-server的近迁。但是在一些情況下微服務(wù)可能和Zipkin服務(wù)端網(wǎng)絡(luò)不通(Zipkin服務(wù)端宕機)艺普,使用Http通信收集方式將無法工作簸州。而使用消息組件完成微服務(wù)和Zipkin服務(wù)端的通信,既實現(xiàn)了微服務(wù)和Zipkin服務(wù)端的解耦歧譬,微服務(wù)不需要知道Zipkin服務(wù)端的網(wǎng)絡(luò)地址岸浑,同時也避免因Zipkin服務(wù)器宕機而使收集數(shù)據(jù)的工作無法進行的問題。由于在Spring Cloud Sleuth中支持消息組件來傳輸鏈路數(shù)據(jù)瑰步,本節(jié)使用RabbitMQ來傳輸鏈路數(shù)據(jù)矢洲。
首先改造zipkin-server工程 , 在其pom文件中將zipkin-server的依賴去掉缩焦,加上spring-cloud-sleuth-zipkin-stream和 spring-cloud-starter-stream-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>
在工程的配置文件application.yml加上RabbitMQ的配置,包括host袁滥、端口盖桥、用戶名、密
碼题翻,代碼如下:
spring:
rabbitmq:
host : localhost
port: 5672
username : guest
password : guest
在程序的啟動類ZipkinServerApplication中加上@EnableZipkinStreamServer注解揩徊,開啟 ZipkinStreamServer,代碼如下:
@SpringBootApplication
@EnableEurekaClient
@EnableZipkinStreamServer
public class ZipkinServerApplication {
public static void main(String[] args) {
SpringApplication.run(ZipkinServerApplication.class, args);
}
}
下面改造Zipkin Client (包括 gateway-service工程和eureka-producer工程)嵌赠,在他們的pom文件中將 spring-cloud-starter-zipkin依賴改為 spring-cloud-sleuth-zipkin-stream和spring-cloud-starter--stream-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工程姜挺。這樣齿税,就將鏈路的上傳數(shù)據(jù)得方式從Http改為用消息組件RabbitMQ。
六炊豪、ElasticSearch中存儲鏈路數(shù)據(jù)
??由于Zipkin Server將數(shù)據(jù)存儲在內(nèi)存中偎窘, 一旦程序重啟乌助,之前的鏈路數(shù)據(jù)全部丟失,因此需要將鏈路數(shù)據(jù)持久化存儲陌知。 Zipkin支持將鏈路數(shù)據(jù)存儲在MySQL他托、Elasticsearch 和Cassandra數(shù)據(jù)庫中。這里使用Elasticsearch存儲仆葡,在并發(fā)高赏参、大數(shù)據(jù)的情況下,使用Elasticsearch存儲鏈路數(shù)據(jù)有明顯優(yōu)勢沿盅,并且支持分布式文件實時存儲把篓。需要安裝ElasticSearch和Kibana (下一節(jié)中使用),下載地址為https://www.elastic.co/products/elasticsearch腰涧。安裝完成后啟動韧掩, 其中ElasticSearch的默認端口號為9200, Kibana的默認端口號為5601。
??這里直接在zipkin-server的基礎(chǔ)上進行改造疗锐。首先在pom文件中加上zipkin的依賴和zipkin-autoconfigure-storage-elasticsearch-http的依賴,代碼如下:
<dependency>
<groupId>io.zipkin.java</groupId>
<artifactId>zipkin-server</artifactId>
<version>1.28.0</version>
</dependency>
<dependency>
<groupId>io.zipkin.java</groupId>
<artifactId>zipkin-autoconfigure-storage-elasticsearch-http</artifactId>
<version>1.28.0</version>
</dependency>
??在程序的配置文件application.yml 中加上Zipkin的配置费彼,配置了zipkin的存儲類型(type) elasticsearch滑臊,使用的存儲組件( StorageComponent ) 為elasticsearch,然后需要配置為elasticsearch箍铲,包括hosts (可以配置多個雇卷,用“” 隔開) 和index等。具體配置代碼如下:
zipkin:
storage:
type: elasticsearch
StorageComponent: elasticsearch
elasticsearch:
cluster: elasticsearch
max-requests: 30
index: zipkin
index-shards: 3
index-replicas: 1
hosts: localhost:9200
只需要這些配置颠猴, Zipkin Server的鏈路數(shù)據(jù)就存儲在 ElasticSearch中了关划。
七、用Kibana展示鏈路數(shù)據(jù)
??上一節(jié)講述了如何將鏈路數(shù)據(jù)存儲在ElasticSearch中翘瓮,ElasticSearch可以和Kibana結(jié)合贮折,將鏈路數(shù)據(jù)展示在Kibana上。安裝完成Kibana后啟動春畔,Kibana默認會向本地端口為9200的ElasticSearch讀取數(shù)據(jù)脱货。Kibana默認的端口為5601,訪問Kibana的主頁http://localhost:5601律姨,其界面如下圖所示振峻。
在上圖的界面中择份, 單擊 “Management”按鈕扣孟,然后單擊 “CreateIndexPattern”, 添加一個 index荣赶。這里將在上節(jié)ElasticSearch中寫入鏈路數(shù)據(jù)的index配 置為“zipkin”凤价,那么在界面填寫為“zipkin-*”鸽斟,完成索引創(chuàng)建。創(chuàng)建完成index后利诺,單擊“Discover”富蓄,就可以在界面上展示鏈路數(shù)據(jù)了,展示界面如下圖所示慢逾。
點擊數(shù)據(jù)詳情立倍,可以看到整個鏈路數(shù)據(jù)的詳細數(shù)據(jù),還可以看到自定義添加的鏈路數(shù)據(jù)侣滩。如下圖所示口注。
總結(jié):這一章節(jié)主要學(xué)習(xí)了Sleuth的概念和使用,與zuul結(jié)合進行網(wǎng)關(guān)路由和服務(wù)轉(zhuǎn)發(fā)以實現(xiàn)服務(wù)鏈路君珠,和zipkin結(jié)合進行鏈路數(shù)據(jù)保存和處理寝志,與ElasticSearch和kibana結(jié)合進行鏈路數(shù)據(jù)的持久化處理和可視化操作。
github源碼地址