Spring Cloud 微服務架構全鏈路實踐

Spring Cloud 微服務架構全鏈路實踐

目前公司使用的 Spring Cloud 整個技術組件,基本包含了上面圖中所包含的,不得不說,Spring Cloud 整個生態真的很強大,使用起來也很方便有效。

後面有時間再針對每個組件進行使用解讀,這篇文章主要說下 Spring Cloud 架構的鏈路圖,順便把自己的思路整理下來,以備查閱。

1. 網關請求流程

Spring Cloud 微服務架構全鏈路實踐

在 Spring Cloud 整個組件庫中,Spring Cloud Zuul 是最容易被忽視,但也是最重要的,Spring Cloud Zuul 可以和 Eureka 註冊中心集成,我們目前使用 Spring Cloud Zuul 的功能如下:

  • Filter 過濾器
  • Router 路由
  • Ribbon 負載均衡
  • Hystrix 熔斷
  • Retry 重試

有些功能是 Spring Cloud Zuul 自帶的,比如 Filter 和 Router,有些是結合 Spring Cloud 其他組件,比如 Ribbon 和 Hystrix。

這裡重點介紹下 Filter 過濾器,分為四個過濾類型:

  • pre:Zuul 轉發請求之前執行,我們目前的實現是AccessTokenFilter,用於 oAuth2.0 JWT 的授權驗證。
  • route
    :Zuul 路由時執行,目前項目沒用到。
  • post:Zuul 路由轉發後執行,也就是已經請求成功了後端服務,我們目前的實現是CustomResponseFilter,用於統一請求格式的封裝,比如 code/msg/data 等。
  • error:以上過濾器發生錯誤時執行,我們目前的實現是CustomErrorFilter,用於攔截過濾器執行的出現的錯誤,然後統一格式封裝返回,另外,error 過濾器好像並不能捕獲後端服務執行出現的錯誤。

另外,關於 oAuth2.0 JWT 的授權驗證,實現的方式有兩種:

  • 授權的配置在後端服務中(每個服務都需要當作 Resource Server 進行配置,需要配置公鑰,接口的授權具體配置在註解中),Zuul 只做轉發,並不進行授權的驗證。
  • 授權的配置在 Zuul 中,也就是把 Zuul 當作 Resource Server,後端服務不需要進行任何處理,Zuul 中具體的實現就是AccessTokenFilter,裡面的邏輯是手動解析 JWT,然後判斷是否正確,以及解析出用戶信息/Scope/Role,然後根據當前的請求 API,對授權 Map 中的配置進行匹配,如果匹配錯誤,直接拋出 401 授權錯誤。

我們目前採用的是第二種方式,這兩種方式都有利有弊,關鍵在於自己的取捨,為什麼採用第二種方式?目的就是發揮 Zuul 的作用,對外網關進行統一授權驗證。

關於授權 Map,裡面存儲了所有服務接口的配置,示例配置:

private static final Map ROUTE_MAPS;static{ ROUTE_MAPS = new HashMap(); ROUTE_MAPS.put("eureka-client/home", "read:ROLE_ADMIN"); ROUTE_MAPS.put("eureka-client/user", "read:ROLE_ADMIN"); ROUTE_MAPS.put("eureka-client/error", "read:ROLE_ADMIN");}

這是我們目前的配置,是一個靜態的 Map,後面會存儲在 Spring Cloud Config 配置中心,Zuul 啟動時進行加載,利用 Spring Cloud Bus 動態刷新。

關於 Zuul 網關,其實還有很多需要說的,後面有機會再進行針對說明。

2. Eureka 服務治理

Spring Cloud 微服務架構全鏈路實踐

Eureka 遵循的是 AP 原則(服務可用性和分區容錯性),是服務治理最理想的遵循 CAP 分佈式原則。

Eureka 集群中的節點是彼此平級,不像 Consul 有 master/worker 之分,集群中的 Eureka 節點彼此兩兩註冊,所以,Eureka 集群最好部署三個節點,這也是我們目前的部署方式。

另外,Eureka 的自我保護機制,可以參考這篇文章。

服務之間的相互調用,負載有兩種使用方式:

  • Feign:基於聲明式,顧名思義,就是需要定義接口,就像我們平常使用對象調用一樣。
  • Ribbon:軟負載,通過往 RestTemplate 中注入負載 Handler,然後通過負載算法選取調用(通過 Eureka 獲取服務註冊信息)。

我們目前打算使用 Ribbon 負載方式,為什麼?看下面代碼就知道了:

restTemplate.getForObject("http://eureka-client/hello", String.class);

3. Config 配置中心

我們目前配置中心使用的是 Spring Cloud Config,當然你也可以使用功能更強大的 Polly(攜程開源),但 Config 目前也能滿足我們的需求,存儲倉庫我們現在使用的是 Git。

Config 配置中心提供了數據加密功能,你可以使用 RSA 的加密方式,這樣存儲在 Git 中的配置都是密文形式,Config Client 獲取加密配置的時候,Config Server 會自動進行解密返回。

配置中心的使用場景,我們目前主要是兩個地方:

  • 項目啟動的配置信息,比如數據庫的連接字符串等。
  • 業務服務的配置信息,也就是業務相關的配置。

另外,需要說明的是,默認情況下,如果 Git 中的配置更新了,Config Client 不會進行更新配置,我們目前的解決方式是,使用 Spring Cloud Bus 進行動態刷新配置(Config Server 中配置),具體的流程:

  • Git 中添加 WebHooks 腳本,比如curl -X POST http://manager1:8180/bus/refresh,當 Git 倉庫中的配置更新後,自動執行。
  • Config Server 中配置 Spring Cloud Bus,接受 Git 的配置刷新請求,然後利用 RabbitMQ 廣播通知所有的 Config Client 訂閱方,刷新配置信息。

4. Hystrix 監控

Spring Cloud 微服務架構全鏈路實踐

Hystrix 主要是用於服務熔斷/降級/隔離處理,Hystrix 配置在調用方,當被調用方服務不可用時,觸發 Hystrix 熔斷,會執行指定的 Fallback 方法,進行特殊處理。

我之前以為,Hystrix 熔斷的觸發條件是服務不可用,也就是服務請求超時(比如服務掛掉了),但我自己測試了下,服務出現 500 錯誤,也會觸發 Hystrix 熔斷,而且會自動忽略 Hystrix 的超時時間設置。

我們目前使用 Hystrix,主要有兩個地方:

  • 內部服務調用:可以對某個 API 接口進行熔斷處理。
  • Zuul 網關使用:就是當 Zuul 路由轉發調用時,但有個侷限性,就是隻能對服務進行熔斷,並不能針對某個 API 接口熔斷。

上面圖中,主要畫的是 Hystrix 的監控流程,我們目前主要使用 RabbitMQ 進行採集傳輸,turbine-server 進行數據流的聚合,hystri

Spring Cloud 微服務架構全鏈路實踐

ashboard 進行圖形化的展示。

5. 服務調用鏈路

Spring Cloud 微服務架構全鏈路實踐

服務調用鏈路的概念,就是當服務請求發起時,記錄整個請求鏈路的數據,以備查詢。

目前市面上,幾乎所有服務調用鏈路的實現,理論基礎都是基於 Google Dapper 的那篇論文,其中最重要的概念就是 traceId 和 spanId。

  • traceId 記錄整個服務鏈路的 ID,由首次請求方創建,服務鏈路中唯一。
  • spanId 記錄當前服務塊的 ID,由當前服務方創建。
  • parentId 記錄上一個請求服務的 spanId。

下面我描述下,我們目前的服務調用鏈路過程:

  • H5 發起請求,到 Zuul 網關,Zuul 創建全局的 traceId 和自己的 spanId,然後攜帶這些數據到業務服務 A,並利用 Spring Cloud Sluth 傳輸到 RabbitMQ。
  • 業務服務 A,接收到 Zuul 傳輸的 traceId 和 spanId,然後把 Zuul 的 spanId 設置成 parentId,並生成自己的 spanId,然後攜帶這些數據到業務服務 B,並利用 Spring Cloud Sluth 傳輸到 RabbitMQ。
  • ....

上面圖中,詳細說明了整個服務調用鏈路的過程,這邊再說下使用的技術棧:

  • Spring Cloud Sluth:和 SkyWalking 的探針概念比較類似,每個服務都進行配置,收集當然服務的請求數據(traceId 和 spanId),然後利用stream-sluth和binder-rabbit組件,將請求數據傳輸到 RabbitMQ。
  • Spring Cloud Zipkin:主要用於請求鏈路的 UI 展示,Zipkin 會從 RabbitMQ 讀取請求數據,然後存儲到 ElasticSearch 中,然後下次顯示直接從 ElasticSearch 中讀取。
  • Kibana:Kibana 也可以顯示 ElasticSearch 中的請求數據,只不過不是圖形化的,需要索引配置創建。

6. ELK 日誌鏈路

Spring Cloud 微服務架構全鏈路實踐

ELK 可以參考下之前的幾篇文章:

  • ELK 架構之 Elasticsearch 和 Kibana 安裝配置
  • ELK 架構之 Logstash 和 Filebeat 安裝配置
  • ELK 架構之 Logstash 和 Filebeat 配置使用(採集過濾)
  • ELK 架構之 Elasticsearch、Kibana、Logstash 和 Filebeat 安裝配置彙總(6.2.4 版本)

上面圖中已經很詳細介紹了下 ELK 的流程,ELK 默認技術棧裡是沒有 Filebeat 的,Logstash 用作日誌收集的時候,CPU 和內存會佔用資源比較大,所以我們使用輕量化的 Filebeat 進行日誌的收集,Filebeat 部署在每個業務服務所在的服務器,然後將收集到的日誌數據傳輸到 Logstash,Logstash 可以部署兩到三臺服務器上,用作日誌的過濾和分析工作,然後再將處理後的日誌數據,傳輸到 ElasticSearch 存儲。

7. 統一格式返回

Spring Cloud 微服務架構全鏈路實踐

關於統一格式返回,圖中已經詳細的說明了,如果你有更好的方案,歡迎探討。


分享到:


相關文章: