Chapter eleven《SpringCloud微服務(wù)實(shí)戰(zhàn)》

分布式服務(wù)跟蹤: Spring Cloud Sleuth

什么是Sprng Cloud Sleuth

是對一個請求所經(jīng)過的整個服務(wù)鏈的跟蹤番刊,幫助我們快速發(fā)現(xiàn)錯誤根源以及監(jiān)控分析每條請求鏈路上的性能瓶頸哮针。

隨著業(yè)務(wù)不斷地發(fā)展曾我,系統(tǒng)拆分導(dǎo)致系統(tǒng)調(diào)用鏈路愈發(fā)復(fù)雜一個前端請求可能最終需要調(diào)用很多次后端服務(wù)才能完成察净,當(dāng)整個請求變慢或不可用時疆股,我們是無法得知該請求是由某個或某些后端服務(wù)引起的根资,這時就需要解決如何快讀定位服務(wù)故障點(diǎn)架专,以對癥下藥同窘。于是就有了分布式系統(tǒng)調(diào)用跟蹤的誕生。

Tips:業(yè)界分布式服務(wù)跟蹤的理論基礎(chǔ)主要來自于 Google 的一篇論文《Dapper, a Large-Scale Distributed Systems Tracing Infrastructure》部脚,使用最為廣泛的開源實(shí)現(xiàn)是 Twitter 的 Zipkin想邦,為了實(shí)現(xiàn)平臺無關(guān)、廠商無關(guān)的分布式服務(wù)跟蹤委刘,CNCF 發(fā)布了布式服務(wù)跟蹤標(biāo)準(zhǔn) Open Tracing丧没。國內(nèi),淘寶的“鷹眼”锡移、京東的“Hydra”呕童、大眾點(diǎn)評的“CAT”、新浪的“Watchman”淆珊、唯品會的“Microscope”夺饲、窩窩網(wǎng)的“Tracing”都是這樣的系統(tǒng)。

一般的施符,一個分布式服務(wù)跟蹤系統(tǒng)往声,主要有三部分:

數(shù)據(jù)收集、數(shù)據(jù)存儲和數(shù)據(jù)展示戳吝。

根據(jù)系統(tǒng)大小不同烁挟,每一部分的結(jié)構(gòu)又有一定變化。譬如骨坑,對于大規(guī)模分布式系統(tǒng)撼嗓,數(shù)據(jù)存儲可分為實(shí)時數(shù)據(jù)和全量數(shù)據(jù)兩部分,實(shí)時數(shù)據(jù)用于故障排查(troubleshooting)欢唾,全量數(shù)據(jù)用于系統(tǒng)優(yōu)化且警;數(shù)據(jù)收集除了支持平臺無關(guān)和開發(fā)語言無關(guān)系統(tǒng)的數(shù)據(jù)收集,還包括異步數(shù)據(jù)收集(需要跟蹤隊(duì)列中的消息礁遣,保證調(diào)用的連貫性)斑芜,以及確保更小的侵入性;數(shù)據(jù)展示又涉及到數(shù)據(jù)挖掘和分析祟霍。雖然每一部分都可能變得很復(fù)雜杏头,但基本原理都類似。

image

服務(wù)追蹤的追蹤單元是從客戶發(fā)起請求(request)抵達(dá)被追蹤系統(tǒng)的邊界開始沸呐,到被追蹤系統(tǒng)向客戶返回響應(yīng)(response)為止的過程醇王,稱為一個“trace”。

每個 trace 中會調(diào)用若干個服務(wù)崭添,為了記錄調(diào)用了哪些服務(wù)寓娩,以及每次調(diào)用的消耗時間等信息,在每次調(diào)用服務(wù)時,埋入一個調(diào)用記錄,稱為一個“span”。這樣大脉,若干個有序的 span 就組成了一個 trace。在系統(tǒng)向外界提供服務(wù)的過程中仁连,會不斷地有請求和響應(yīng)發(fā)生,也就會不斷生成 trace阱穗,把這些帶有span 的 trace 記錄下來怖糊,就可以描繪出一幅系統(tǒng)的服務(wù)拓?fù)鋱D。附帶上 span 中的響應(yīng)時間颇象,以及請求成功與否等信息伍伤,就可以在發(fā)生問題的時候,找到異常的服務(wù)遣钳;根據(jù)歷史數(shù)據(jù)扰魂,還可以從系統(tǒng)整體層面分析出哪里性能差,定位性能優(yōu)化的目標(biāo)蕴茴。

Spring Cloud Sleuth為服務(wù)之間調(diào)用提供鏈路追蹤劝评。通過Sleuth可以很清楚的了解到一個服務(wù)請求經(jīng)過了哪些服務(wù),每個服務(wù)處理花費(fèi)了多長倦淀。從而讓我們可以很方便的理清各微服務(wù)間的調(diào)用關(guān)系蒋畜。此外Sleuth可以幫助我們:

  • 耗時分析: 通過Sleuth可以很方便的了解到每個采樣請求的耗時,從而分析出哪些服務(wù)調(diào)用比較耗時;
  • 可視化錯誤: 對于程序未捕捉的異常撞叽,可以通過集成Zipkin服務(wù)界面上看到;
  • 鏈路優(yōu)化: 對于調(diào)用比較頻繁的服務(wù)姻成,可以針對這些服務(wù)實(shí)施一些優(yōu)化措施。

spring cloud sleuth可以結(jié)合zipkin愿棋,將信息發(fā)送到zipkin科展,利用zipkin的存儲來存儲信息,利用zipkin ui來展示數(shù)據(jù)糠雨。

Sleuth術(shù)語

因?yàn)镾leuth是根據(jù)Google的Dapper’s論文而來的才睹,所以在術(shù)語上也借鑒了Dapper。

  • Span: 最基本的工作單元甘邀。例如: 發(fā)送一個RPC就是一個新的span琅攘,同樣一次RPC的應(yīng)答也是。Span通過一個唯一的松邪,長度為64位的ID來作為標(biāo)識坞琴,另外,再使用一個64位ID用于服務(wù)調(diào)用跟蹤测摔。Span也可以帶有其他數(shù)據(jù)置济,eg:描述,時間戳锋八,鍵值對標(biāo)簽浙于,起始Span的ID,以及處理ID(通常使用IP地址)等等挟纱。 Span有起始和結(jié)束羞酗,它們用于跟蹤時間信息。Span應(yīng)該都是成對出現(xiàn)的紊服,有始必有終檀轨,所以一旦創(chuàng)建了一個span,那就必須在未來某個時間點(diǎn)結(jié)束它欺嗤。

提示: 起始的Span通常被稱為:root span参萄。它的id通常也被作為一個跟蹤記錄的id。

  • Trace: 一個樹結(jié)構(gòu)的Span集合煎饼。例如:在分布式大數(shù)據(jù)存儲中讹挎,可能每一次請求都是一次跟蹤記錄。
  • Annotation: 用于記錄一個事件的時間信息吆玖。一些基礎(chǔ)核心的Annotation用于記錄請求的起始和結(jié)束時間筒溃,例如:
    • cs: 客戶端發(fā)送(Client Sent的縮寫)。這個annotation表示一個span的起始;
    • sr: 服務(wù)端接收(Server Received的縮寫)沾乘。表示服務(wù)端接收到請求怜奖,并開始處理。如果減去cs的時間戳翅阵,則可以計算出網(wǎng)絡(luò)傳輸耗時歪玲。
    • ss: 服務(wù)端完成請求處理,應(yīng)答信息被發(fā)回客戶端(Server Sent的縮寫)掷匠。如果減去sr的時間戳读慎,則可以計算出服務(wù)端處理請求的耗時。
    • cr: 客戶端接收(Client Received的縮寫)槐雾。標(biāo)志著Span的結(jié)束夭委。客戶端成功的接收到服務(wù)端的應(yīng)答信息募强。如果減去cs的時間戳株灸,則可以計算出請求的響應(yīng)耗時。

下圖擎值,通過可視化的方式描述了Span和Trace的概念:

image

ZipKin

Zipkin是Twitter的一個開源項(xiàng)目慌烧,它基于 Google Dapper 實(shí)現(xiàn)。我們可以使用它來收集各個服務(wù)器上請求鏈路的跟蹤數(shù)據(jù)鸠儿,并通過它提供的REST API接口來輔助查詢跟蹤數(shù)據(jù)以實(shí)現(xiàn)對分布式系統(tǒng)的監(jiān)控程序屹蚊,從而及時發(fā)現(xiàn)系統(tǒng)中出現(xiàn)的延遲升高問題并找出系統(tǒng)性能瓶頸的根源厕氨。除了面向開發(fā)的API接口之外,它還提供了方便的UI組件來幫助我們直觀地搜索跟蹤信息和分析請求鏈路明細(xì)汹粤,比如可以查詢某段時間內(nèi)各用戶請求的處理時間等命斧。

它主要由4個核心組件構(gòu)成。

  • Collector:收集器組件嘱兼,它主要處理從外部系統(tǒng)發(fā)送過來的跟蹤信息国葬,將這些信息轉(zhuǎn)換為Zipkin內(nèi)部處理的Span格式,以支待后續(xù)的存儲芹壕、分析汇四、展示等功能。
  • Storage:存儲組件踢涌,它主要處理收集器接收到的跟蹤信息通孽,默認(rèn)會將這些信息存儲在內(nèi)存中。我們也可以修改此存儲策略睁壁, 通過使用其他存儲組件將跟蹤信息存儲到數(shù)據(jù)庫中利虫。
  • RESTful API: API組件,它主要用來提供外部訪問接口堡僻。比如給客戶端展示跟蹤信息糠惫,或是外接系統(tǒng)訪問以實(shí)現(xiàn)監(jiān)控等。
  • Web UI: UI組件钉疫,基于API組件實(shí)現(xiàn)的上層應(yīng)用硼讽。通過UI組件,用戶可以方便而又直觀地查詢和分析跟蹤信息牲阁。

快速入門

創(chuàng)建 trace-1 9015

pom 依賴

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"     xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
     xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>

<groupId>com.zk.springcloud</groupId>
<artifactId>springcloud-trace-1</artifactId>
<version>0.0.1-SNAPSHOT</version>
<packaging>jar</packaging>

<name>springcloud-trace-1</name>
<description>Demo project for Spring Boot</description>

<parent>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-parent</artifactId>
    <version>2.0.6.RELEASE</version>
    <relativePath/> <!-- lookup parent from repository -->
</parent>

<properties>
    <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
    <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
    <java.version>1.8</java.version>
    <spring-cloud.version>Finchley.SR2</spring-cloud.version>
</properties>

<dependencies>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-web</artifactId>
    </dependency>
    <dependency>
        <groupId>org.springframework.cloud</groupId>
        <artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
    </dependency>
    <dependency>
        <groupId>org.springframework.cloud</groupId>
        <artifactId>spring-cloud-starter-netflix-ribbon</artifactId>
    </dependency>

    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-test</artifactId>
        <scope>test</scope>
    </dependency>
</dependencies>

<dependencyManagement>
    <dependencies>
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-dependencies</artifactId>
            <version>${spring-cloud.version}</version>
            <type>pom</type>
            <scope>import</scope>
        </dependency>
    </dependencies>
</dependencyManagement>

<build>
    <plugins>
        <plugin>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-maven-plugin</artifactId>
        </plugin>
    </plugins>
</build>

</project>

啟動類

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.client.discovery.EnableDiscoveryClient;
import org.springframework.cloud.client.loadbalancer.LoadBalanced;
import org.springframework.context.annotation.Bean;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.client.RestTemplate;

@SpringBootApplication
@RestController
@EnableDiscoveryClient
public class SpringcloudTrace1Application {

    @Bean
    @LoadBalanced
    RestTemplate restTemplate(){
        return  new RestTemplate();
    }

    @GetMapping(value = "/trace-1")
    public String trace(){
        System.out.println("===call trace-1===");
        return restTemplate().getForEntity("http://trace-2/trace-2",String.class).getBody();
    }

    public static void main(String[] args) {
        SpringApplication.run(SpringcloudTrace1Application.class, args);
    }
}

配置文件

spring.application.name=trace-1
server.port=9015
eureka.client.service-url.defaultZone=http://localhost:9001/eureka/,http://localhost:9004/eureka/

創(chuàng)建 trace-2 9016

pom 依賴同 trace-1 9015

啟動類

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.client.discovery.EnableDiscoveryClient;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;

@SpringBootApplication
@RestController
@EnableDiscoveryClient
public class SpringcloudTrace2Application {

    @GetMapping(value = "/trace-2")
    public String trace(){
        System.out.println("====== <call trace-2> ======");
        return "Trace";
    }

    public static void main(String[] args) {
        SpringApplication.run(SpringcloudTrace2Application.class, args);
    }
}

配置文件

spring.application.name=trace-2
server.port=9016
eureka.client.service-url.defaultZone=http://localhost:9001/eureka/,http://localhost:9004/eureka/

將 eureka 9001 和 eureka-1 9004 固阁、 trace-1 、 trace-2 都啟動

訪問 http://localhost:9015/trace-1 返回 Trace

訪問 http://localhost:9016/trace-2 返回 Trace

日志打印

trace-1 

===call trace-1===

trace-2

====== <call trace-2> ======

實(shí)現(xiàn)跟蹤

在 trace-1 9015 城菊、trace-2 9016 添加如下依賴

<dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-starter-sleuth</artifactId>
    <version>1.2.5.RELEASE</version>
</dependency>

重啟后备燃,調(diào)用 訪問 http://localhost:9015/trace-1 返回 Trace

跟蹤原理

對 trace-2 9016 輸出頭信息

@GetMapping(value = "/trace-2")
public String trace(HttpServletRequest request){
    System.out.println("====== <call trace-2> ====== traceid = " + request.getHeader("X-B3-TraceId")
+ " spanid = "+ request.getHeader("X-B3-SpanId"));
    return "Trace";
}

重啟后,調(diào)用 訪問 http://localhost:9015/trace-1 返回 Trace

為應(yīng)用引入和配置 Zipkin 服務(wù)

trace-1 9015 凌唬、 trace-2 9016 添加 zipkin 依賴 和 配置

    <!-- 分布式鏈路追蹤 -->
    <dependency>
        <groupId>org.springframework.cloud</groupId>
        <artifactId>spring-cloud-starter-zipkin</artifactId>
        <version>1.2.5.RELEASE</version>
    </dependency>

配置

# zipkin 服務(wù)地址
spring.zipkin.base-url=http://localhost:9411
# #樣本采集量并齐,默認(rèn)為0.1,為了測試這里修改為1客税,正式環(huán)境一般使用默認(rèn)值况褪。
spring.sleuth.sampler.probability=1

調(diào)用 http://localhost:9015/trace-1 可在 http://localhost:9411/zipkin 看到請求鏈路

消息中間件收集

修改客戶端 trace-1 和 trace-2 添加 pom 依賴

<!-- 消息中間件  -->
    <dependency>
        <groupId>org.springframework.cloud</groupId>
        <artifactId>spring-cloud-sleuth-stream</artifactId>
        <version>2.0.2.RELEASE</version>
    </dependency>
    <dependency>
        <groupId>org.springframework.cloud</groupId>
        <artifactId>spring-cloud-starter-stream-rabbit</artifactId>
        <version>2.0.1.RELEASE</version>
    </dependency>

配置文件添加 rabbitmq 并將 zipkin 配置注釋

# zipkin 服務(wù)地址
#spring.zipkin.base-url=http://localhost:9411
spring.rabbitmq.host=localhost
spring.rabbitmq.port=5672
spring.rabbitmq.username=springcloud
spring.rabbitmq.password=123456

Less is more.

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市更耻,隨后出現(xiàn)的幾起案子测垛,更是在濱河造成了極大的恐慌,老刑警劉巖秧均,帶你破解...
    沈念sama閱讀 217,406評論 6 503
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件食侮,死亡現(xiàn)場離奇詭異号涯,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī)锯七,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,732評論 3 393
  • 文/潘曉璐 我一進(jìn)店門链快,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人起胰,你說我怎么就攤上這事久又∥籽樱” “怎么了效五?”我有些...
    開封第一講書人閱讀 163,711評論 0 353
  • 文/不壞的土叔 我叫張陵,是天一觀的道長炉峰。 經(jīng)常有香客問我畏妖,道長,這世上最難降的妖魔是什么疼阔? 我笑而不...
    開封第一講書人閱讀 58,380評論 1 293
  • 正文 為了忘掉前任戒劫,我火速辦了婚禮,結(jié)果婚禮上婆廊,老公的妹妹穿的比我還像新娘迅细。我一直安慰自己,他們只是感情好淘邻,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,432評論 6 392
  • 文/花漫 我一把揭開白布茵典。 她就那樣靜靜地躺著,像睡著了一般宾舅。 火紅的嫁衣襯著肌膚如雪统阿。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 51,301評論 1 301
  • 那天筹我,我揣著相機(jī)與錄音扶平,去河邊找鬼。 笑死蔬蕊,一個胖子當(dāng)著我的面吹牛结澄,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播岸夯,決...
    沈念sama閱讀 40,145評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼概而,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了囱修?” 一聲冷哼從身側(cè)響起赎瑰,我...
    開封第一講書人閱讀 39,008評論 0 276
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎破镰,沒想到半個月后餐曼,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體压储,經(jīng)...
    沈念sama閱讀 45,443評論 1 314
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,649評論 3 334
  • 正文 我和宋清朗相戀三年源譬,在試婚紗的時候發(fā)現(xiàn)自己被綠了集惋。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 39,795評論 1 347
  • 序言:一個原本活蹦亂跳的男人離奇死亡踩娘,死狀恐怖刮刑,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情养渴,我是刑警寧澤雷绢,帶...
    沈念sama閱讀 35,501評論 5 345
  • 正文 年R本政府宣布,位于F島的核電站理卑,受9級特大地震影響翘紊,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜藐唠,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,119評論 3 328
  • 文/蒙蒙 一帆疟、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧宇立,春花似錦踪宠、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,731評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至蟋滴,卻和暖如春染厅,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背津函。 一陣腳步聲響...
    開封第一講書人閱讀 32,865評論 1 269
  • 我被黑心中介騙來泰國打工肖粮, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人尔苦。 一個月前我還...
    沈念sama閱讀 47,899評論 2 370
  • 正文 我出身青樓涩馆,卻偏偏與公主長得像,于是被迫代替她去往敵國和親允坚。 傳聞我的和親對象是個殘疾皇子魂那,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,724評論 2 354

推薦閱讀更多精彩內(nèi)容