⑦SpringCloud 實戰:引入Sleuth元件,完善服務鏈路跟蹤

Admol發表於2020-12-21

這是SpringCloud實戰系列中第7篇文章,瞭解前面第兩篇文章更有助於更好理解本文內容:
①SpringCloud 實戰:引入Eureka元件,完善服務治理
②SpringCloud 實戰:引入Feign元件,發起服務間呼叫
③SpringCloud 實戰:使用 Ribbon 客戶端負載均衡
④SpringCloud 實戰:引入Hystrix元件,分散式系統容錯
⑤SpringCloud 實戰:引入Zuul元件,開啟閘道器路由
⑥SpringCloud 實戰:引入gateway元件,開啟閘道器路由功能

背景

近年來,隨著微服務架構的流行,很多公司都走上了微服務拆分之路。從而使系統變得越來越複雜,原本單體的系統被拆成很多個服務,每個服務之間通過輕量級的 HTTP 協議進行互動。

單體架構時,一個請求的呼叫鏈路非常清晰,一般由負載均衡器,比如 Nginx。將呼叫方的請求轉發到後端服務,後端服務進行業務處理後返回給呼叫方。而當架構變成微服務架構時,可能帶來一系列的問題,比如下面三個問題:

  1. 介面響應慢,怎麼排查?
  2. 服務間的依賴關係如何檢視?
  3. 請求貫穿多個微服務,如何將每個請求的日誌串起來?

分散式鏈路跟蹤

分散式鏈路跟蹤原理在於如何能將請求經過的服務節點都關聯起來。當一個請求從客戶端到達閘道器後,相當於是第一個入口,這時就需要生成一個唯一的請求 ID,作為這次請求的標識。從閘道器到達服務 A 後,肯定是需要將請求 ID 傳遞到服務 A 中的,這樣才能將閘道器到服務 A 的請求關聯起來,依次類推,後面會經過多層服務,都需要將資訊一層層傳遞。當然在每一層都需要將資料進行上報、統一儲存、展示等操作。

從我們對這個需求的理解來看,鏈路跟蹤並不是很複雜,而複雜的點在於如何實現這一套跟蹤框架,就拿請求資訊傳遞這件事來說,服務之間互動,有的用的是 Feign 呼叫介面,有的用的是 RestTemplate 呼叫介面,要想將資訊傳遞到下游服務,那麼必須得擴充套件這些呼叫的框架才可以。

1

核心概念

  • Span

    基本工作單元,例如,傳送 RPC 請求是一個新的 Span,傳送 HTTP 請求是一個新的 Span,內部方法呼叫也是一個新的 Span。

  • Trace

    一次分散式呼叫的鏈路資訊,每次呼叫鏈路資訊都會在請求入口處生成一個 TraceId

  • Annotation

    用於記錄事件的資訊。在 Annotation 中會有 CS、SR、SS、CR 這些資訊,前面的C表示客戶端,S表示伺服器端; 後面的S表示sent,也就是發起請求時的動作,R表示Received,也就是接受到請求時的動作;下面分別介紹下這些資訊的作用。

    • CS
      也就是 Client Sent,客戶端發起一個請求,這個 Annotation 表示 Span 的開始。
    • CR
      也就是 Client Received,表示 Span 的結束,客戶端已成功從伺服器端收到響應,用 CR 的時間戳減去 CS 的時間戳就可以知道客戶端從伺服器接收響應所需的全部時間。
    • SS
      也就是 Server Sent,在請求處理完成時將響應傳送回客戶端,用 SS 的間戳減去 SR 的時間戳會顯示伺服器端處理請求所需的時間。
    • SR
      也就是 Server Received,伺服器端獲得請求並開始處理它,用 SR 的時間戳減去 CS 的時間戳會顯示網路延遲時間。

請求追蹤過程分解

2

  1. 首先當一個請求訪問 SERVICE1 時,這時是沒有 Trace 和 Span 的,然後會生成 Trace 和 Span,如圖所示生成的 Trace ID 是 X,Span ID 是 A。
  2. 接著 SERVICE1 請求 SERVICE2,這是一次遠端請求,會生成一個新的 Span,Span ID 為 B,Trace ID 不變還是 X。Span B 處於 CS 狀態。
  3. 當請求到達 SERVICE2 後,Trade ID 和 Span ID 就被傳遞過來了,這時,SERVICE2 有內部操作,又生成了一個新的 Span,Span ID 為 C,Trace ID 不變還是 X。
  4. SERVICE2 處理完後向 SERVICE3 發起請求,同時產生新的 Span,Span ID 為 D,Span D 處於 CS 狀態,SERVICE3 接收到請求後,Span D 處於 SR 狀態,同時 SERVICE3 內部操作也會產生新的 Span,Span ID 為 E。
  5. 當 SERVICE3 處理完後,需要將結果響應給呼叫方,這時 Span D 就處於 SS 的狀態,當 SERVICE2 收到響應後,Span ID 為 D 的 Span 就是 CR 狀態,表示 Span 已經結束了。

Zipkin 介紹

ZipkinTwitter 的一個開源專案,是一個致力於收集所有服務監控資料的分散式跟蹤系統,它提供了收集資料和查詢資料兩大介面服務。有了 Zipkin 我們就可以很直觀地檢視呼叫鏈,並且可能很方便看出服務之間的呼叫關係,以及呼叫耗費的時間。

Zipkin還提供了可插拔資料儲存方式:In-Memory、MySql、Cassandra以及Elasticsearch。測試方便可直接採用In-Memory方式進行儲存,生產推薦使用Elasticsearch。

安裝 Zipkin

如果使用了 Java 8 或者更高的版本,可以獲取最新的可執行 jar 包來進行啟動。

  1. 下載jar包:

    curl -sSL https://zipkin.io/quickstart.sh | bash -s
    

    如果下載太慢,可以直接訪問Maven地址進行下載最新的jar。

    其他方式安裝,可以檢視官網的quickstart

  2. 啟動服務

    java -jar zipkin.jar
    
  3. 訪問Zipkin

    成功啟動服務後,訪問http://127.0.0.1:9411/zipkin/即可。

    3

Sleuth 介紹

Spring Cloud Sleuth 是一種分散式的服務鏈路跟蹤解決方案,通過使用 Spring Cloud Sleuth 可以讓我們快速定位某個服務的問題,以及釐清服務間的依賴關係。

Sleuth 可以新增鏈路資訊到日誌中,這樣的好處是可以統一將日誌進行收集展示,並且可以根據鏈路的資訊將日誌進行串聯。

Sleuth 中的鏈路資料可直接上報給 Zipkin,在 Zipkin 中就可以直接檢視呼叫關係和每個服務的耗時情況.

Sleuth 中內建了很多框架的埋點,比如:Zuul、Feign、Hystrix、RestTemplate 等。正因為有了這些框架的埋點,鏈路資訊才能一直往下傳遞。

通過 Http 結合Zipkin

  1. 在我們的微服務專案中新增Zipkin依賴

    <dependency> 
        <groupId>org.springframework.cloud</groupId>
        <artifactId>spring-cloud-starter-zipkin</artifactId>
    </dependency>
    
  2. 配置Zipkin地址

    spring.zipkin.base-url=http://127.0.0.1:9411/
    
  3. 配置取樣比例
    實際使用中可能呼叫了 10 次介面,但是 Zipkin 中只有一條資料,這是因為收集資訊是有一定比例的,Zipkin 中的資料條數與呼叫介面次數預設比例是 10:1,通過下面的配置來改變這個比例值:

    spring.sleuth.sampler.probability=1.0
    
  4. 驗證
    啟動我們的微服務,訪問 http://localhost:9000/eureka-client/sayHello 介面,介面由閘道器路由到eureka-client 服務,eureka-client 服務再呼叫eureka-provider服務,介面返回eureka-provider服務的埠等資訊。
    然後訪問 http://127.0.0.1:9411/zipkin ,點選查詢,即可檢視到相關訪問記錄
    4

    點選選單上面的依賴,可以檢視專案的依賴關係

    5

使用 RabbitMQ or Kafka 代替 HTTP 傳送呼叫鏈資料

資料的傳送如果採用 HTTP 對效能還是有影響的。如果Zipkin 的服務端在重啟或者掛掉時,那麼將丟失部分採集資料。為了解決這些問題,我們可以整合 RabbitMQ 或者Kafka 來傳送採集資料,利用訊息佇列來提高傳送效能,保證資料不丟失;

  1. 如果要使用RabbitMQ或Kafka而不是HTTP,需要引入spring-rabbit or spring-kafka 相關依賴。

    <dependency> 
        <groupId>org.springframework.amqp</groupId>
        <artifactId>spring-rabbit</artifactId>
    </dependency>
    
  2. 然後在配置檔案修改相關配置:

    # WEB、KAFKA、RABBIT、ACTIVEMQ
    spring.zipkin.sender.type=kafka
    
  3. 刪除之前配置的 spring.zipkin.base-url

  4. 配置kafka、rabbit

自定義 Zipkin 配置

每個跟蹤系統都需要具有Reporter <Span>Sender,如果要覆蓋提供的bean,則需要給它們指定一個特定的名稱 ZipkinAutoConfiguration.REPORTER_BEAN_NAME and ZipkinAutoConfiguration.SENDER_BEAN_NAME

下面是示例:

@Configuration
protected static class MyConfig {

    @Bean(ZipkinAutoConfiguration.REPORTER_BEAN_NAME)
    Reporter<zipkin2.Span> myReporter() {
        return AsyncReporter.create(mySender());
    }

    @Bean(ZipkinAutoConfiguration.SENDER_BEAN_NAME)
    MySender mySender() {
        return new MySender();
    }

    static class MySender extends Sender {

        private boolean spanSent = false;

        boolean isSpanSent() {
            return this.spanSent;
        }

        @Override
        public Encoding encoding() {
            return Encoding.JSON;
        }

        @Override
        public int messageMaxBytes() {
            return Integer.MAX_VALUE;
        }

        @Override
        public int messageSizeInBytes(List<byte[]> encodedSpans) {
            return encoding().listSizeInBytes(encodedSpans);
        }

        @Override
        public Call<Void> sendSpans(List<byte[]> encodedSpans) {
            this.spanSent = true;
            return Call.create(null);
        }

    }

}

相關文章