史上最強項目實戰(七)——微服務調用鏈追蹤

現在我們已經完成服務註冊中心、分佈式配置中心、網關服務的搭建,整個工程的完成度變得越來越高了,這是值得欣慰的。廢話不多說,開始今天的主題,今天我們要做得是微服務調用鏈追蹤。

隨着業務的發展,系統被拆分成一個個微服務,服務與服務之間的調用關係變得錯綜複雜,有可能一個請求最終需要調用很多個服務才能完成,當整個請求變慢或者不可用時,我們怎麼快速定位,找到問題所在。這時,分佈式系統調用鏈追蹤的解決方案就很好的解決了我們的困惑。在本篇文章中,我們將詳細介紹如何使用Spring Cloud Sleuth + Zipkin來爲我們的微服務架構增加分佈式服務跟蹤的能力。

1. Spring Cloud Sleuth介紹

服務追蹤的追蹤單元是從客戶發起請求(request)抵達被追蹤系統的邊界開始,到被追蹤系統向客戶返回響應(response)爲止的過程,稱爲一個 trace。每個 trace 中會調用若干個服務,爲了記錄調用了哪些服務,以及每次調用的消耗時間等信息,在每次調用服務時,埋入一個調用記錄,稱爲一個span。這樣,若干個有序的 span 就組成了一個 trace。在系統向外界提供服務的過程中,會不斷地有請求和響應發生,也就會不斷生成 trace,把這些帶有 span 的 trace 記錄下來,就可以描繪出一幅系統的服務拓撲圖。附帶上 span 中的響應時間,以及請求成功與否等信息,就可以在發生問題的時候,找到異常的服務;根據歷史數據,還可以從系統整體層面分析出哪裏性能差,定位性能優化的目標。

Spring Cloud Sleuth 爲服務之間調用提供鏈路追蹤。通過 Sleuth 可以很清楚的瞭解到一個服務請求經過了哪些服務,每個服務處理花費了多長。從而讓我們可以很方便的理清各微服務間的調用關係。此外 Sleuth 可以幫助我們:

  • 耗時分析:通過 Sleuth 可以很方便的瞭解到每個採樣請求的耗時,從而分析出哪些服務調用比較耗時;
  • 可視化錯誤:對於程序未捕捉的異常,可以通過集成 Zipkin 服務界面上看到;
  • 鏈路優化:對於調用比較頻繁的服務,可以針對這些服務實施一些優化措施。

Spring Cloud Sleuth 可以結合 Zipkin,將信息發送到 Zipkin,利用 Zipkin 的存儲來存儲信息,利用 Zipkin UI 來展示數據。

2. Zipkin介紹

Zinkin致力於收集服務的定時數據,以解決微服務架構中的延遲問題,包括數據的收集、存儲、查找和展現。Zipkin的基本架構如下圖(圖片來自網絡,侵刪):

在這裏插入圖片描述

它主要由4個核心組件構成:

  • Collector:收集器組件,它主要用於處理從外部系統發送過來的跟蹤信息,將這些信息轉換爲 Zipkin 內部處理的 Span 格式,以支持後續的存儲、分析、展示等功能。
  • Storage:存儲組件,它主要對處理收集器接收到的跟蹤信息,默認會將這些信息存儲在內存中,我們也可以修改此存儲策略,通過使用其他存儲組件將跟蹤信息存儲到數據庫中。
  • RESTful API:API 組件,它主要用來提供外部訪問接口。比如給客戶端展示跟蹤信息,或是外接系統訪問以實現監控等。
  • Web UI:UI 組件,基於 API 組件實現的上層應用。通過 UI 組件用戶可以方便而有直觀地查詢和分析跟蹤信息。

3. 實戰

Zipkin分爲兩端,一個是Zipkin服務端,一個是Zipkin客戶端,其中客戶端就是我們一個個微服務。

客戶端會配置服務端的 URL 地址,一旦發生服務間的調用的時候,會被配置在微服務裏面的 Sleuth 的監聽器監聽,並生成相應的 Trace 和 Span 信息發送給服務端。

發送的方式主要有兩種,一種是 HTTP 報文的方式,還有一種是消息總線的方式如 RabbitMQ。

今天我們將採用第二種方式,使用RabbitMQ發送。爲什麼?試想一下,假設現在有Zipkin服務端A,然後有客戶端B、C、D……那麼難道我們每個客戶端都要去配置服務端的地址嗎?假設有一天服務端A的地址變了,那麼是不是每個客戶端都要去修改呢?顯然這種方式不夠人性化,而如果採用RabbitMQ消息中間件,客戶端就不需要知道服務端的地址,只要客戶端發送出去的數據能夠被服務端消費到即可,這正是利用了消息中間件實現服務與服務之間的解耦。

默認情況下Zipkin採用內存方式進行存儲,如果服務端重啓,那麼之前所有的數據都將丟失。因此Zipkin提供了可插拔數據存儲方式:In-Memory、MySql、Cassandra 以及 Elasticsearch。今天我們將採用MySQL數據存儲方式。

再說一點,關於 Zipkin 的服務端,在使用 Spring Boot 2.x 版本後,官方就不推薦自行定製編譯了,反而是直接提供了編譯好的 jar 包來給我們使用,所以我們就不用自己搭建Zipkin的服務端了,直接使用編譯好的jar即可。

說了這麼多,真的要開始實戰操練了,請做好準備。

3.1 搭建Zipkin服務端

我這裏是從網上下載了zipkin-server-2.16.2-exec.jar的jar包作爲服務端,然後在運行之前,我們需要到數據庫創建幾張表,sql如下:

--DROP TABLE zipkin_spans;
--DROP TABLE zipkin_annotations;
--DROP TABLE zipkin_dependencies;

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'
) ENGINE=INNODB ROW_FORMAT=COMPRESSED CHARACTER SET=utf8 COLLATE utf8_general_ci;

ALTER TABLE zipkin_spans ADD UNIQUE KEY(`trace_id_high`, `trace_id`, `id`) COMMENT 'ignore INSERT ON duplicate';
ALTER TABLE zipkin_spans ADD INDEX(`trace_id_high`, `trace_id`, `id`) COMMENT 'for joining WITH zipkin_annotations';
ALTER TABLE zipkin_spans ADD INDEX(`trace_id_high`, `trace_id`) COMMENT 'for getTracesByIds';
ALTER TABLE zipkin_spans ADD INDEX(`name`);
ALTER TABLE zipkin_spans ADD INDEX(`remote_service_name`) COMMENT 'for getTraces AND getSpanNames';
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';
ALTER TABLE zipkin_annotations ADD INDEX(`a_key`) COMMENT 'for getTraces';

CREATE TABLE IF NOT EXISTS zipkin_dependencies (
  `day` DATE NOT NULL,
  `parent` VARCHAR(255) NOT NULL,
  `child` VARCHAR(255) NOT NULL,
  `call_count` BIGINT
) ENGINE=INNODB ROW_FORMAT=COMPRESSED CHARACTER SET=utf8 COLLATE utf8_general_ci;

ALTER TABLE zipkin_dependencies ADD UNIQUE KEY(`day`, `parent`, `child`);

3.2 啓動Zipkin服務端

zipkin-server-2.16.2-exec.jar所在的目錄打開終端,執行以下語句即可啓動服務端。

java -jar zipkin-server-2.16.2-exec.jar --zipkin.collector.rabbitmq.addresses=localhost --STORAGE_TYPE=mysql --MYSQL_DB=leyou_zipkin --MYSQL_HOST=localhost --MYSQL_TCP_PORT=3380 --MYSQL_USER=root --MYSQL_PASS=Anbang713

指定RabbitMQ地址、MySQL數據庫信息,看到如下信息,則表示啓動成功,默認端口是:9411。

在這裏插入圖片描述

通過瀏覽器訪問http://localhost:9411則可以看到Zipkin Web UI界面了。
在這裏插入圖片描述

至此Zipkin服務端已完成搭建,接下來看下如何改造客戶端。我們將在之前的網關服務上集成Spring Cloud Sleuth + Zipkin。

3.3 搭建Zipkin客戶端

3.3.1 引入依賴

<dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-starter-sleuth</artifactId>
</dependency>
<dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-starter-zipkin</artifactId>
</dependency>
<dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-stream-binder-rabbit</artifactId>
</dependency>

3.3.2 添加註解

3.3.3 添加配置

spring:
  cloud:
    bus:
      trace:
        enabled: true
      enabled: true
  rabbitmq:
    host: localhost
    port: 5672
    username: guest
    password: guest
    virtual-host: /
  sleuth:
    web:
      client:
        enabled: true
    sampler:
      probability: 1.0 # 將採樣比例設置爲 1.0,也就是全部都需要。默認是 0.1

3.4 啓動Zipkin客戶端

觀察到兩個現象:

(1)集成Sleuth+Zipkin的服務啓動日誌輸出有一點點不一樣了,如下圖:
在這裏插入圖片描述

(2)服務啓動成功後,自動創建了一個名爲zipkin的隊列,如下圖:
在這裏插入圖片描述

3.5 測試

萬事俱備,只欠測試了。我們要做的是微服務調用鏈追蹤,那自然要發起請求,纔有調用過程,調用鏈路。還記得我們在上篇做網關黑白名單攔截以及分佈式配置中心自動刷新測試地址嗎?我們直接調這個地址測試即可。調用完成後,回到Zipkin的Web UI界面可以看到服務名多了個ly-env-gateway-server,這正是我們網關服務的名稱,再點擊查找時,可以看到剛剛調用的鏈路。
在這裏插入圖片描述

點擊調用鏈路,會發現這裏有更多的詳細內容,比如持續時間、經過的服務數量、span總數等等。
在這裏插入圖片描述
在這裏插入圖片描述

——End——
更多詳情,可掃碼關注微信公衆號哦。

在這裏插入圖片描述

發佈了142 篇原創文章 · 獲贊 29 · 訪問量 8萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章