Spring Cloud微服務技術概覽

  • 2020 年 4 月 29 日
  • 筆記

Spring Cloud 是一系列框架的有序集合。它利用 Spring Boot 的開發便利性巧妙地簡化了分佈式系統基礎設施的開發,如服務發現註冊、配置中心、消息總線、負載均衡、斷路器、數據監控等,都可以用 Spring Boot 的開發風格做到一鍵啟動和部署。Spring 並沒有重複製造輪子,它只是將目前各家公司開發的比較成熟、經得起實際考驗的服務框架組合起來,通過 Spring Boot 風格進行再封裝屏蔽掉了複雜的配置和實現原理,最終給開發者留出了一套簡單易懂、易部署和易維護的分佈式系統開發工具包。

本文主要講述我們為什麼選擇 Spring Cloud 和它的技術概覽。

什麼是微服務

要了解 Spring Cloud 就繞不開微服務這個概念。因為 Spring Cloud 是 Spring 為微服務架構思想做的一個一站式實現。從某種程度是可以簡單的理解為,微服務是一個概念、一個項目開發的架構思想。Spring Cloud 是微服務架構的一種 Java 實現。

微服務的概念源於 Martin Fowler 於 2014 年 3 月 25 日寫的一篇文章 Microservices(中文版翻譯點擊查看

微服務架構模式(Microservices Architecture Pattern)的目的是將大型的、複雜的、長期運行的應用程序構建為一組相互配合的服務,每個服務都可以很容易得局部改良。 Micro 這個詞意味着每個服務都應該足夠小,但是,這裡的小不能用代碼量來比較,而應該是從業務邏輯上比較 —— 符合 SRP 原則的才叫微服務。

關於微服務的一個形象表達:

X 軸:水平擴展,即在負載均衡服務器後增加多個運行實例
Z 軸:數據庫的擴展,即分庫分表
Y 軸:功能分解,即將不同職能的模塊分成不同的服務

微服務架構優勢

複雜度可控:在將應用分解的同時,規避了原本複雜度無止境的積累。每一個微服務專註於單一功能,並通過定義良好的接口清晰表述服務邊界。由於體積小、複雜度低,每個微服務可由一個小規模開發團隊完全掌控,易於保持高可維護性和開發效率。
獨立部署:由於微服務具備獨立的運行進程,所以每個微服務也可以獨立部署。當某個微服務發生變更時無需編譯、部署整個應用。由微服務組成的應用相當於具備一系列可並行的發佈流程,使得發佈更加高效,同時降低對生產環境所造成的風險,最終縮短應用交付周期。
技術選型靈活:微服務架構下,技術選型是去中心化的。每個團隊可以根據自身服務的需求和行業發展的現狀,自由選擇最適合的技術棧。由於每個微服務相對簡單,故需要對技術棧進行升級時所面臨的風險就較低,甚至完全重構一個微服務也是可行的。
容錯(fault isolation):當某一組建發生故障時,在單一進程的傳統架構下,故障很有可能在進程內擴散,形成應用全局性的不可用。在微服務架構下,故障會被隔離在單個服務中。若設計良好,其他服務可通過重試、平穩退化等機制實現應用層面的容錯。
擴展:每個服務可以各自進行 x 擴展和 z 擴展,而且,每個服務可以根據自己的需要部署到合適的硬件服務器上。當應用的不同組件在擴展需求上存在差異時,微服務架構便體現出其靈活性,因為每個服務可以根據實際需求獨立進行擴展。

為什麼微服務架構需要 Spring Cloud

簡單來說,服務化的核心就是將傳統的一站式應用根據業務拆分成一個一個的服務,而微服務在這個基礎上要更徹底地去耦合(不再共享 DB、KV,去掉重量級 ESB),並且強調 DevOps 和快速演化。這就要求我們必須採用與一站式時代、泛 SOA 時代不同的技術棧,而 Spring Cloud 就是其中的佼佼者。

DevOps 是英文 Development 和 Operations 的合體,他要求開發、測試、運維進行一體化的合作,進行更小、更頻繁、更自動化的應用發佈,以及圍繞應用架構來構建基礎設施的架構。這就要求應用充分的內聚,也方便運維和管理。這個理念與微服務理念不謀而合。

接下來我們從服務化架構演進的角度來看看為什麼 Spring Cloud 更適應微服務架構。

從使用 nginx 說起

最初的服務化解決方案是給提供相同服務提供一個統一的域名,然後服務調用者向這個域名發送 HTTP 請求,由 Nginx 負責請求的分發和跳轉。

這種架構存在很多問題:

  • Nginx 作為中間層,在配置文件中耦合了服務調用的邏輯,這削弱了微服務的完整性,也使得 Nginx 在一定程度上變成了一個重量級的 ESB。
  • 服務的信息分散在各個系統,無法統一管理和維護。每一次的服務調用都是一次嘗試,服務消費者並不知道有哪些實例在給他們提供服務。這不符合 DevOps 的理念。
  • 無法直觀的看到服務提供者和服務消費者當前的運行狀況和通信頻率。這也不符合 DevOps 的理念。
  • 消費者的失敗重發,負載均衡等都沒有統一策略,這加大了開發每個服務的難度,不利於快速演化。

為了解決上面的問題,我們需要一個現成的中心組件對服務進行整合,將每個服務的信息匯總,包括服務的組件名稱、地址、數量等。服務的調用方在請求某項服務時首先通過中心組件獲取提供這項服務的實例的信息(IP、端口等),再通過默認或自定義的策略選擇該服務的某一提供者直接進行訪問。所以,我們引入了 Dubbo。

基於 Dubbo 實現微服務

Dubbo 是阿里開源的一個 SOA 服務治理解決方案,文檔豐富,在國內的使用度非常高。

使用 Dubbo 構建的微服務,已經可以比較好地解決上面提到的問題:

  • 調用中間層變成了可選組件,消費者可以直接訪問服務提供者。
  • 服務信息被集中到 Registry 中,形成了服務治理的中心組件。
  • 通過 Monitor 監控系統,可以直觀地展示服務調用的統計信息。
  • Consumer 可以進行負載均衡、服務降級的選擇。

但是對於微服務架構而言,Dubbo 也並不是十全十美的:

  • Registry 嚴重依賴第三方組件(zookeeper 或者 redis),當這些組件出現問題時,服務調用很快就會中斷。
  • Dubbo 只支持 RPC 調用。使得服務提供方與調用方在代碼上產生了強依賴,服務提供者需要不斷將包含公共代碼的 jar 包打包出來供消費者使用。一旦打包出現問題,就會導致服務調用出錯。

Dubbo 於 2017 年開始又重啟維護,新的地址://github.com/apache/incubator-dubbo

新的選擇 ——Spring Cloud

作為新一代的服務框架,Spring Cloud 提出的口號是開發 「面向雲環境的應用程序」,它為微服務架構提供了更加全面的技術支持。

根據微服務架構在各方面的要素,我們把 Spring Cloud 與 Dubbo 進行一番對比。

其實把 Spring Cloud 和 Dubbo 放一起對比有點不公平,Dubbo 只是實現了服務治理,而 Spring Cloud 下面有 23 個子項目(截止到 2018.4)分別覆蓋了微服務架構下的方方面面,服務治理只是其中的一個方面,一定程度來說,Dubbo 只是 Spring Cloud Netflix 中的一個子集。但是在選擇框架上,方案完整度恰恰是一個需要重點關注的內容。

  Dubbo Spring Cloud
服務註冊中心 Zookeeper Spring Cloud Netflix Eureka
服務調用方式 RPC REST API
服務監控 Dubbo-monitor Spring Boot Admin
斷路器 不完善 Spring Cloud Netflix Hystrix
服務網關 Spring Cloud Netflix Zuul
分佈式配置 Spring Cloud Config
服務跟蹤 Spring Cloud Sleuth
消息總線 Spring Cloud Bus
數據流 Spring Cloud Stream
批量任務 Spring Cloud Task
…… …… ……

服務調用方式:Spring Cloud 拋棄了 Dubbo 的 RPC 通信,採用的是基於 HTTP 的 REST 方式。嚴格來說,這兩種方式各有優劣。雖然從一定程度上來說,後者犧牲了服務調用的性能,但也避免了上面提到的原生 RPC 帶來的問題。而且 REST 相比 RPC 更為靈活,服務提供方和調用方的依賴只依靠一紙契約,不存在代碼級別的強依賴,這在強調快速演化的微服務環境下,顯得更加合適。

服務註冊和發現:Eureka 相比於 zookeeper,更加適合於服務發現的場景,這點會在下一篇會詳細展開。

很明顯,Dubbo 的功能只是 Spring Cloud 體系的一部分。Spring Cloud 的功能更加強大,涵蓋面更廣,而且作為 Spring 的拳頭項目,它也能夠與 Spring Framework、Spring Boot、Spring Data、Spring Batch 等其他 Spring 項目完美融合,這些對於微服務而言是至關重要的。前面提到,微服務背後一個重要的理念就是持續集成、快速交付,而在服務內部使用一個統一的技術框架,顯然比把分散的技術組合到一起更有效率。更重要的是,Spring Cloud 是 Spring Source 的產物,Spring 社區的強大背書可以說是 Java 企業界最有影響力的組織了,除了 Spring Source 之外,還有 Pivotal 和 Netfix 是其強大的後盾與技術輸出。另外,從目前 Spring Cloud 的被關注度和活躍度上來看,很有可能將來會成為微服務架構的標準框架。

Spring Cloud 是微服務架構的最佳落地方案

有關這兩個框架更詳細的的對比可以看知乎和這兩篇文章:
微服務架構的基礎框架選擇:Spring Cloud 還是 Dubbo?
阿里 Dubbo 瘋狂更新,關 Spring Cloud 什麼事?

Spring Cloud 技術概覽

下圖展示了 Spring Cloud 的完整技術組成:

  • 服務治理:這是 Spring Cloud 的核心。目前 Spring Cloud 主要通過整合 Netflix 的相關產品來實現這方面的功能(Spring Cloud Netflix),包括用於服務註冊和發現的 Eureka,調用斷路器 Hystrix,調用端負載均衡 Ribbon,Rest 客戶端 Feign,智能服務路由 Zuul,用於監控數據收集和展示的 Spectator、Servo、Atlas,用於配置讀取的 Archaius 和提供 Controller 層 Reactive 封裝的 RxJava。除此之外,針對於服務的註冊和發現,除了 Eureka,Spring Cloud 也整合了 Consul 和 Zookeeper 作為備選,但是因為這兩個方案在 CAP 理論上都遵循 CP 而不是 AP(下一篇會詳細介紹這點),所以官方並沒有推薦使用。

    Feign 和 RxJava 並不是 Netiflix 的產品,但是被整合到了 Spring Cloud Netflix 中。

  • 分佈式鏈路監控:Spring Cloud Sleuth 提供了全自動、可配置的數據埋點,以收集微服務調用鏈路上的性能數據,並發送給 Zipkin 進行存儲、統計和展示。
  • 消息組件:Spring Cloud Stream 對於分佈式消息的各種需求進行了抽象,包括發佈訂閱、分組消費、消息分片等功能,實現了微服務之間的異步通信。Spring Cloud Stream 也集成了第三方的 RabbitMQ 和 Apache Kafka 作為消息隊列的實現。而 Spring Cloud Bus 基於 Spring Cloud Stream,主要提供了服務間的事件通信(比如刷新配置)。
  • 配置中心:基於 Spring Cloud Netflix 和 Spring Cloud Bus,Spring 又提供了 Spring Cloud Config,實現了配置集中管理、動態刷新的配置中心概念。配置通過 Git 或者簡單文件來存儲,支持加解密。
  • 安全控制:Spring Cloud Security 基於 OAuth2 這個開放網絡的安全標準,提供了微服務環境下的單點登錄、資源授權、令牌管理等功能。
  • 命令行工具:Spring Cloud Cli 提供了以命令行和腳本的方式來管理微服務及 Spring Cloud 組件的方式。
  • 集群工具:Spring Cloud Cluster 提供了集群選主、分佈式鎖(暫未實現)、一次性令牌(暫未實現)等分佈式集群需要的技術組件。

Eureka

Eureka 是 Netflix 開源的一款提供服務註冊和發現的產品,它提供了完整的 Service Registry 和 Service Discovery 實現。也是 Spring Cloud 體系中最重要最核心的組件之一。

用大白話講,Eureka 就是一個服務中心,將所有的可以提供的服務都註冊到它這裡來管理,其它各調用者需要的時候去註冊中心獲取,然後再進行調用,避免了服務之間的直接調用,方便後續的水平擴展、故障轉移等。

當然服務中心這麼重要的組件一但掛掉將會影響全部服務,因此需要搭建 Eureka 集群來保持高可用性,生產中建議最少兩台。隨着系統的流量不斷增加,需要根據情況來擴展某個服務,Eureka 內部已經提供均衡負載的功能,只需要增加相應的服務端實例既可。那麼在系統的運行期間某個實例掛了怎麼辦?Eureka 內容有一個心跳檢測機制,如果某個實例在規定的時間內沒有進行通訊則會自動被剔除掉,避免了某個實例掛掉而影響服務。

因此使用了 Eureka 就自動具有了註冊中心、負載均衡、故障轉移的功能。

Hystrix

在微服務架構中通常會有多個服務層調用,基礎服務的故障可能會導致級聯故障,進而造成整個系統不可用的情況,這種現象被稱為服務雪崩效應。服務雪崩效應是一種因 「服務提供者」 的不可用導致 「服務消費者」 的不可用,並將不可用逐漸放大的過程。

如下圖所示:A 作為服務提供者,B 為 A 的服務消費者,C 和 D 是 B 的服務消費者。A 不可用引起了 B 的不可用,並將不可用像滾雪球一樣放大到 C 和 D 時,雪崩效應就形成了。

在這種情況下就需要整個服務機構具有故障隔離的功能,避免某一個服務掛掉影響全局。在 Spring Cloud 中 Hystrix 組件就扮演這個角色。

Hystrix 會在某個服務連續調用 N 次不響應的情況下,立即通知調用端調用失敗,避免調用端持續等待而影響了整體服務。Hystrix 間隔時間會再次檢查此服務,如果服務恢復將繼續提供服務。

Hystrix Dashboard 和 Turbine

當熔斷髮生的時候需要迅速的響應來解決問題,避免故障進一步擴散,那麼對熔斷的監控就變得非常重要。熔斷的監控現在有兩款工具:Hystrix-dashboard 和 Turbine

Hystrix-dashboard 是一款針對 Hystrix 進行實時監控的工具,通過 Hystrix Dashboard 我們可以直觀地看到各 Hystrix Command 的請求響應時間,請求成功率等數據。但是只使用 Hystrix Dashboard 的話,你只能看到單個應用內的服務信息,這明顯不夠。我們需要一個工具能讓我們匯總系統內多個服務的數據並顯示到 Hystrix Dashboard 上,這個工具就是 Turbine.
監控的效果圖如下:

Spring Cloud Config

隨着微服務不斷的增多,每個微服務都有自己對應的配置文件。在研發過程中有測試環境、UAT 環境、生產環境,因此每個微服務又對應至少三個不同環境的配置文件。這麼多的配置文件,如果需要修改某個公共服務的配置信息,如:緩存、數據庫等,難免會產生混亂,這個時候就需要引入 Spring Cloud 另外一個組件:Spring Cloud Config。

Spring Cloud Config 是一個解決分佈式系統的配置管理方案。它包含了 Client 和 Server 兩個部分,Server 提供配置文件的存儲、以接口的形式將配置文件的內容提供出去,Client 通過接口獲取數據、並依據此數據初始化自己的應用。

其實就是 Server 端將所有的配置文件服務化,需要配置文件的服務實例去 Config Server 獲取對應的數據。將所有的配置文件統一整理,避免了配置文件碎片化。配置中心 git 實例參考:配置中心 git 示例;

如果服務運行期間改變配置文件,服務是不會得到最新的配置信息,需要解決這個問題就需要引入 Refresh。可以在服務的運行期間重新加載配置文件,具體可以參考這篇文章:配置中心 svn 示例和 refresh

當所有的配置文件都存儲在配置中心的時候,配置中心就成為了一個非常重要的組件。如果配置中心出現問題將會導致災難性的後果,因此在生產中建議對配置中心做集群,來支持配置中心高可用性。

Spring Cloud Bus

上面的 Refresh 方案雖然可以解決單個微服務運行期間重載配置信息的問題,但是在真正的實踐生產中,可能會有 N 多的服務需要更新配置,如果每次依靠手動 Refresh 將是一個巨大的工作量,這時候 Spring Cloud 提出了另外一個解決方案:Spring Cloud Bus

Spring Cloud Bus 通過輕量消息代理連接各個分佈的節點。這會用在廣播狀態的變化(例如配置變化)或者其它的消息指令中。Spring Cloud Bus 的一個核心思想是通過分佈式的啟動器對 Spring Boot 應用進行擴展,也可以用來建立一個或多個應用之間的通信頻道。目前唯一實現的方式是用 AMQP 消息代理作為通道。

Spring Cloud Bus 是輕量級的通訊組件,也可以用在其它類似的場景中。有了 Spring Cloud Bus 之後,當我們改變配置文件提交到版本庫中時,會自動的觸發對應實例的 Refresh,具體的工作流程如下:

Spring Cloud Zuul

在微服務架構模式下,後端服務的實例數一般是動態的,對於客戶端而言很難發現動態改變的服務實例的訪問地址信息。因此在基於微服務的項目中為了簡化前端的調用邏輯,通常會引入 API Gateway 作為輕量級網關,同時 API Gateway 中也會實現相關的認證邏輯從而簡化內部服務之間相互調用的複雜度。

Spring Cloud 體系中支持 API Gateway 落地的技術就是 Zuul。Spring Cloud Zuul 路由是微服務架構中不可或缺的一部分,提供動態路由,監控,彈性,安全等的邊緣服務。Zuul 是 Netflix 出品的一個基於 JVM 路由和服務端的負載均衡器。

它的具體作用就是服務轉發,接收並轉發所有內外部的客戶端調用。使用 Zuul 可以作為資源的統一訪問入口,同時也可以在網關做一些權限校驗等類似的功能。

鏈路跟蹤

隨着服務的越來越多,對調用鏈的分析會越來越複雜,如服務之間的調用關係、某個請求對應的調用鏈、調用之間消費的時間等,對這些信息進行監控就成為一個問題。在實際的使用中我們需要監控服務和服務之間通訊的各項指標,這些數據將是我們改進系統架構的主要依據。因此分佈式的鏈路跟蹤就變的非常重要,Spring Cloud 也給出了具體的解決方案:Spring Cloud Sleuth 和 Zipkin

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

Zipkin 是 Twitter 的一個開源項目,允許開發者收集 Twitter 各個服務上的監控數據,並提供查詢接口

分佈式鏈路跟蹤需要 Sleuth+Zipkin 結合來實現

總結

我們從整體上來看一下 Spring Cloud 各個組件如何來配套使用:

從上圖可以看出 Spring Cloud 各個組件相互配合,合作支持了一套完整的微服務架構。

  • 其中 Eureka 負責服務的註冊與發現,很好將各服務連接起來
  • Hystrix 負責監控服務之間的調用情況,連續多次失敗進行熔斷保護
  • Hystrix dashboard,Turbine 負責監控 Hystrix 的熔斷情況,並給予圖形化的展示
  • Spring Cloud Config 提供了統一的配置中心服務
  • 當配置文件發生變化的時候,Spring Cloud Bus 負責通知各服務去獲取最新的配置信息
  • 所有對外的請求和服務,我們都通過 Zuul 來進行轉發,起到 API 網關的作用
  • 最後我們使用 Sleuth+Zipkin 將所有的請求數據記錄下來,方便我們進行後續分析

後續章節我們會詳細介紹我們實際使用到的 Spring Cloud 組件。