當前位置:
首頁 > 科技 > 使用Spring 5實現響應式微服務架構,簡潔版來了

使用Spring 5實現響應式微服務架構,簡潔版來了

隨著以Dubbo、Spring Cloud等框架為代表的分散式服務調用和治理工具的大行其道,以及以Docker、Kubernetes等容器技術的日漸成熟,微服務架構(Microservices Architecture)毫無疑問是近年來最熱門的一種服務化架構模式。所謂微服務,就是一些具有足夠小的粒度、能夠相互協作且自治的服務體系。正因為每個微服務都比較簡單,僅關注於完成一個業務功能,所以具備技術、業務和組織上的優勢[1]

另一方面,隨著Spring 5的正式發布,我們引來了響應式編程(Reactive Programming)的全新發展時期。Spring 5中內嵌了響應式Web框架、響應式數據訪問、響應式消息通信等多種響應式組件,從而極大地簡化了響應式應用程序的開發過程和難度。

本章作為全書的開篇,將對微服務架構和響應式系統(Reactive System)的核心概念做簡要介紹,同時給出兩者之間的整合點,即如何構建響應式微服務架構。在本章最後,我們也會給出全書的組織架構以總領全書。

響應式系統核心概念

在本節中,我們將帶領大家進入響應式系統的世界。為了讓大家更好地理解響應式編程和響應式系統的核心概念,我們將先從傳統編程方法出發逐步引出響應式編程方法。同時,我們還將通過響應式宣言(Reactive Manifesto)了解響應式系統的基本特性和設計理念。

從傳統編程方法到響應式編程方法

在電商系統中,訂單查詢是一個典型的業務場景。用戶可以通過多種維度獲取自己已下訂單的列表信息和各個訂單的明細信息。我們就通過訂單查詢這一特定場景來分析傳統編程方法和響應式編程方法之間的區別。

1.訂單查詢場景的傳統方法

在典型的三層架構中,圖1-1展示了基於傳統實現方法的訂單查詢場景時序圖。一般用戶會使用前端組件所提供的操作入口進行訂單查詢,然後該操作入口會調用後台系統的服務層,服務層再調用數據訪問層,進而訪問資料庫,數據從資料庫中獲取之後逐層返回,最後顯示在包括前端服務或用戶操作界面在內的前端組件上。

使用Spring 5實現響應式微服務架構,簡潔版來了

圖1-1訂單查詢場景的傳統實現方法時序圖

顯然,在圖1-1所展示的整個過程中,前端組件通過主動拉取的方式從資料庫中獲取數據。如果用戶不觸發前端操作,那麼就無法獲取資料庫中的數據狀態。也就是說,前端組件對資料庫中的任何數據變更一無所知。

2.訂單查詢場景的響應式方法

主動拉取數據的方式在某些場景下可以運作得很好,但如果我們希望資料庫中的數據一有變化就通知到前端組件,這種方式就不是很合理。這種場景下,我們希望前端組件通過註冊機制獲取數據變更的事件,圖1-2展示了這一過程。

在圖1-2中,我們並不是直接訪問資料庫來獲取數據,而是訂閱了OrderChangedEvent事件。當訂單數據發生任何變化時,系統就會生成這一事件,然後通過一定的方式傳播出來。而訂閱了該事件的服務就會捕獲該事件,從而通過前端組件響應該事件。事件處理的基本步驟涉及對某個特定事件進行訂閱,然後等待事件的發生。如果不需要再對該事件做出響應,我們就可以取消對事件的訂閱。

使用Spring 5實現響應式微服務架構,簡潔版來了

圖1-2訂單查詢場景的響應式實現方法時序圖

圖1-2體現的是響應式系統中一種變化傳遞(Propagation Of Change)思想,即當數據變化之後,會像多米諾骨牌一樣,導致直接和間接引用它的其他數據均發生相應變化。一般而言,生產者只負責生成並發出事件,然後消費者來監聽並負責定義如何處理事件的變化傳遞方式。

顯然,這些事件連起來會形成一串數據流(Data Stream),如果我們能夠及時對數據流的每一個事件做出響應,就會有效提高系統的響應能力。基於數據流是響應式系統的另一個核心特點。

我們再次回到圖1-1,如果從底層資料庫驅動,經過數據訪問層到服務層,最後到前端組件的這個服務訪問鏈路全部都採用響應式的編程方式,從而搭建一條能夠傳遞變化的管道,這樣一旦資料庫中的數據有更新,系統的前端組件上就能相應地發生變化。而且,當這種變化發生時,我們不需要通過各種傳統調用方式來傳遞這種變化,而是由搭建好的數據流自動進行傳遞。

3.傳統方法與響應式方法的對比

圖1-1展示的傳統方法和圖1-2展示的響應式方法具有明顯的差異性,我們分別從處理過程、線程管理和伸縮性角度做簡要對比。

(1)處理過程

傳統開發方式下,我們拉取(Pull)數據的變化,這意味著整個過程是一種間歇性、互不相關的處理過程。前端組件不關心資料庫中的數據是否有變化。

在響應式開發方式下,一旦對事件進行註冊,處理過程只有在數據變化時才會被觸發,類似一種推(Push)的工作方式。

(2)線程管理

在傳統開發方式下,線程的生命周期比較長。在線程存活的狀態下,該線程所使用的資源都會被鎖住。當伺服器在同時處理多個線程時,就會存在資源的競爭問題。

在響應式開發方式下,生成事件和消費事件的線程的存活時間都很短,所以資源之間存在較少的競爭關係。

(3)伸縮性

傳統開發方式下,系統伸縮性涉及資料庫和應用伺服器的伸縮,一般我們需要專門採用一些伺服器架構和資源來應對伸縮性需求。

在響應式開發方式下,因為線程的生命周期很短,同樣的基礎設施可以處理更多的用戶請求。同時,響應式開發方式同樣支持傳統開發方式下的各種伸縮性實現機制,並提供了更多的分散式實現選擇。圖1-3展示了事件處理與系統伸縮性之間的關係。

使用Spring 5實現響應式微服務架構,簡潔版來了

圖1-3 事件處理與系統伸縮性示意圖

在圖1-3中,顯然Web應用程序和事件處理程序可以分別進行伸縮,這為伸縮性實現機制提供更多的選型餘地。

響應式宣言與響應式系統

如同業界的其他宣言一樣,響應式宣言是一組設計原則,符合這些原則的系統可以認為是響應式系統。同時,響應式宣言也是一種架構風格,是一種關於分散式環境下系統設計的思考方式,響應式系統也是具備這一架構風格的系統。

1.響應式系統特性

響應式宣言給出了響應式系統所應該具備的特性,包括即時響應性(Responsive)、回彈性(Resilient)、彈性(Elastic)以及消息驅動(Message Driven)。具備這些特性的系統可以稱為響應式系統。圖1-4給出了響應式宣言的圖形化描述。

使用Spring 5實現響應式微服務架構,簡潔版來了

圖1-4響應式宣言(來自響應式宣言官網)

在圖1-4中,響應式宣言認為,響應式系統的價值在於提供了即時響應性、可維護(Maintainable)和擴展性(E意味著可以快速地檢測到問題並且有效地對其進行處理。即時響應的系統專註於提供快速而一致的響應時間,確立可靠的反饋上限,以提供一致的服務質量。這種一致的行為轉而將簡化錯誤處理、建立最終用戶的信任,並促使用戶與系統做進一步互動。

(2)回彈性

回彈性指的是系統在出現失敗時依然保持即時響應性。這不僅適用於高可用的、任務關鍵型系統——任何不具備回彈性的系統都將會在發生失敗之後丟失即時響應性。回彈性是通過複製、遏制、隔離以及委託來實現的。失敗的擴散被遏制在了每個組件內部,與其他組件相互隔離,從而確保系統某部分的失敗不會危及整個系統,並能獨立恢復。每個組件的恢復都被委託給了另一個內部或外部組件。此外,在必要時可以通過複製來保證高可用性。因此,組件的客戶端不再承擔組件失敗的處理。

(3)彈性

彈性指的是系統在不斷變化的工作負載之下依然保持即時響應性。響應式系統可以對輸入的速率變化做出反應,比如,通過增加或者減少被分配用於服務這些輸入的資源。這意味著設計上並沒有競爭點和中央瓶頸,系統得以進行組件的分片或者複製,並在它們之間分布輸入。通過提供相關的實時性能指標,響應式系統能支持預測式以及響應式的伸縮演算法。這些系統可以在常規的硬體以及軟體平台上實現高效的彈性。

(4)消息驅動

消息驅動指的是響應式系統依賴非同步的消息傳遞,從而確保松耦合、隔離、位置透明的組件之間有著明確邊界。這一邊界還提供了將失敗作為消息委託出去的手段。使用顯式的消息傳遞,可以通過在系統中塑造並監視消息流隊列,並在必要時應用背壓,從而實現負載管理、彈性以及流量控制。使用位置透明的消息傳遞作為通信的手段,使得跨集群或者在單個主機中使用相同的結構成分和語義來管理失敗成為了可能。非阻塞的通信使得接收者可以只在活動時才消耗資源,從而減少系統開銷。

2.響應式的維度

響應式的概念還體現在不同維度上,包含響應事件、響應壓力、響應錯誤和響應用戶。

(1)響應事件

基於消息驅動機制,響應式系統可以對事件做出快速響應。

(2)響應壓力

響應式系統可以在不同的系統壓力下進行靈活響應。當壓力較大時,使用更多的資源;而當壓力變小時,則釋放不需要的資源。

(3)響應錯誤

響應式系統可以優雅地處理錯誤,監控組件的可用性,並在必要時冗餘組件。

(4)響應用戶

響應式系統一方面能夠積極響應用戶請求,但當消費者沒有訂閱事件時,就不會浪費資源進行不必要的處理。

剖析微服務架構

目前,微服務架構已經成為一種主流的軟體開發方法論,它把一種特定的軟體應用設計方法描述為能夠獨立部署的服務套件。本節將對微服務設計原理與架構做精簡而全面的介紹。

分散式系統與微服務架構

微服務架構首先表現為一種分散式系統(Distributed System),而分散式系統是傳統單塊系統(Monolith System)的一種演進。

1.單塊系統

在軟體技術發展過程的很長一段時間內,軟體系統都表現為一種單塊系統。時至今日,很多單塊系統仍然在一些行業和組織中得到開發和維護。所謂單塊系統,簡單講就是把一個系統所涉及的各個組件都打包成一個一體化結構並進行部署和運行。在Java EE領域,這種一體化結構很多時候就體現為一個WAR包,而部署和運行的環境就是以Tomcat為代表的各種應用伺服器。

單塊系統有其存在和發展的固有優勢。當團隊規模並不是太大的時候,一個單塊應用可以由一個開發者團隊進行獨立維護。該團隊的成員能夠對單塊應用進行快速學習、理解和修改,因為其結構非常簡單。同時,因為單塊系統的表現形式就是一個獨立的WAR包,想要對它進行集成、部署以及實現無狀態集群相對也比較簡單,通常只要採用負載均衡機制並運行該單塊系統的多個實例,就能達到系統伸縮性要求。

但在另一方面,隨著公司或者組織業務的不斷擴張、業務結構的不斷變化以及用戶量的不斷增加,單塊系統的優勢已無法適應互聯網時代的快速發展,面臨著越來越多的挑戰,例如,如何處理業務複雜度、如何防止代碼腐化、如何處理團隊協作問題以及如何應對系統伸縮性問題[1]。針對以上集中式單塊系統所普遍存在的問題,基本的解決方案就要依賴於分散式系統的合理構建。

2.分散式系統

所謂分散式系統,是指硬體或軟體組件分布在不同的網路計算機上,彼此通過一定的通信機制進行交互和協調的系統。我們從這個定義中可以看出分散式系統包含兩個區別於單塊系統的本質性特徵:一個是網路,分散式系統的所有組件都位於網路之中,對互聯網應用而言,則位於更為複雜的互聯網環境中;另一個是通信和協調,與單塊系統不同,位於分散式系統中的各個組件只有通過約定、高效且可靠的通信機制進行相關協作,才能完成某項業務功能。這是我們在設計和實現分散式系統時首先需要考慮的兩個方面。

分散式系統相較於集中式系統而言具備優勢的同時,也存在一些我們不得不考慮的特性,包括但不限於網路傳輸的三態性、系統之間的異構性、數據一致性、服務的可用性等[1]。以上問題是分散式系統的基本特性,我們無法避免,只能想辦法進行利用和管理,這就給我們設計和實現分散式系統提出了挑戰。微服務架構本質上也是一種分散式系統,但在遵循通用分散式特性的基礎上,微服務架構還表現出一定的特殊性。接下來我們將圍繞微服務架構的這些特殊性展開討論。

3.微服務架構

Martin Fowler指出[2],微服務架構具有以下特點。

(1)服務組件化

組件(Component)是一種可獨立替換和升級的軟體單元。在日常開發過程中,我們可能會設計和使用很多組件,這些組件可能服務於系統內部,也可能存在於系統所運行的進程之外。而服務就是一種進程外組件,服務之間利用諸如RPC(Remote Procedure Call,遠程過程調用)的通信機制完成交互。服務組件化的主要目的是服務可以獨立部署。如果你的應用程序由一個運行在獨立進程中的很多組件組成,那麼對任何一個組件的改變都將導致必須重新部署整個應用程序。但是如果你把應用程序拆分成很多服務,顯然,通常情況下,你只需要重新部署那個改變的服務。在微服務架構中,每個服務運行在其獨立的進程中,服務與服務之間採用輕量級通信機制互相溝通。

(2)按業務能力組織服務

當尋找把一個大的應用程序進行拆分的方法時,研發過程通常都會圍繞產品團隊、UED團隊、APP前端團隊和伺服器端團隊而展開,這些團隊也就是通常所說的職能團隊(Function Team)。當使用這種標準對團隊進行劃分時,任何一個需求變更,無論大小,都將導致跨團隊協作,從而增加溝通和協作成本。而微服務架構下的劃分方法有所不同,它傾向圍繞業務功能的組織來分割服務。這些服務面向具體業務結構,而不是面向某項技術能力。因此,團隊是跨職能的(Cross-Functional)的特徵團隊(Feature Team),包含用戶體驗、項目管理和技術研發等開發過程中要求的所有技能。每個服務都圍繞著業務進行構建,並且能夠被獨立部署到生產/類生產環境。

(3)去中心化

服務集中治理的一種好處是在單一平台上進行標準化,但採用微服務的團隊更喜歡不同的標準。把集中式系統中的組件拆分成不同的服務,我們在構建這些服務時就會有更多的選擇。對具體的某一個服務而言,應該根據業務上下文選擇合適的語言和工具進行構建。

另一方面,微服務架構也崇尚於對數據進行分散管理。當集中式的應用使用單一邏輯資料庫進行數據持久化時,通常選擇在應用的範圍內使用一個資料庫。然而,微服務讓每個服務管理自己的資料庫,無論是相同資料庫的不同實例,還是不同的資料庫系統。

(4)基礎設施自動化

許多使用微服務架構產品或者系統的團隊擁有豐富的持續集成(Continue Integration)和持續交付(Continuous Delivery)經驗。團隊使用微服務架構構建軟體需要更廣泛地依賴基礎設施自動化技術。

在微服務中同樣需要考慮服務容錯性設計等分散式系統所需要考慮的問題,我們對以上特點進行總結和提煉,認為微服務具備業務獨立、進程隔離、團隊自主、技術無關輕量級通信和交付獨立性等「微」特性。

服務拆分與集成

本節在微服務架構基本概念的基礎上,簡要分析服務拆分的策略和手段,同時也給出對拆分之後的服務進行集成的各種實現方法和技術體系。

1.服務拆分

在微服務架構中,我們認為服務是業務能力的代表,需要圍繞業務進行組織。服務拆分的關鍵在於正確理解業務,識別單個服務內部的業務領域及其邊界,並按邊界進行拆分。所以微服務的拆分模式本質上是基於不同的業務進行拆分。業務體現在各種功能代碼中,通過確定業務的邊界,並使用領域與界限上下文(Boundary Context)、領域事件(Domain Event)等技術手段可以實現拆分。

數據對微服務架構而言同樣可以認為是一種依賴關係,因為任務業務都需要使用某個數據容器作為持久化的機制或者數據處理的媒介,這裡的數據容器不僅指關係型資料庫,還泛指包括消息隊列、搜索引擎索引以及各種Nosql在內的數據媒介。微服務架構中存在一種說法,即我們需要將微服務用到的所有資源全部嵌入到該服務中,從而確保微服務的獨立性。而數據的拆分則體現在如何將集中式的中心化數據轉變為各微服務各自擁有的獨立數據,這部分工作同樣十分具有挑戰性。

關於業務和數據應該先拆分誰的問題,可以是先資料庫後業務代碼,也可以是先業務代碼後資料庫。然而在拆分中遇到的最大挑戰可能會是數據層的拆分,因為在資料庫中,可能會存在各種跨表連接查詢、跨庫連接查詢以及不同業務模塊的代碼與數據耦合得非常緊密的場景,這會導致服務的拆分非常困難。因此,在拆分步驟上我們更多地推薦資料庫先行。數據模型能否徹底分開,很大程度上決定了微服務的邊界功能是否徹底劃清。

服務拆分的方法根據系統自身的特點和運行狀態,通常可分為絞殺者與修繕者兩種模式。絞殺者模式(Strangler Pattern)[3]指的是在現有系統外圍將新功能用新的方式構建為新的服務的策略,通過將新功能做成微服務方式,而不是直接修改原有系統,逐步實現對老系統替換。採用這種策略,隨著時間的推移,新的服務就會逐漸「絞殺」老的系統。對於那些規模很大又難以對現有架構進行修改的遺留系統,推薦採用絞殺者模式。而修繕者模式就如修房或修路一樣,將老舊待修繕的部分進行隔離,用新的方式對其進行單獨修復。修復的同時,需保證與其他部分仍能協同功能。從這種思路出發,修繕者模式更多地表現為一種重構技術,具體實現上可以參考Martine Fowler的BranchByAbstraction重構方法[4]

2.服務集成

服務之間勢必需要集成,而這種集成關係遠比簡單的API調用要複雜。對於微服務架構而言,我們的思路是盡量採用標準化的數據結構和通信機制並降低系統集成的耦合度。我們把微服務架構中服務之間的集成模式分為以下四大類[1],同時還會引入其他一些手段來達到服務與服務之間的有效集成。

(1)介面集成

介面集成是服務之間集成的最常見手段,通常基於業務邏輯的需要進行集成。RPC、REST、消息傳遞和服務匯流排都可以歸為這種集成方式。

RPC架構是服務之間進行集成的最基本方式。在RPC架構實現思路上,遠程服務提供者以某種形式提供服務調用相關信息,遠程代理對象通過動態代理攔截機制生成遠程服務的本地代理,讓遠程調用在使用上如同本地調用一樣。而網路通信應該與具體協議無關,通過序列化和反序列化方式對網路數據進行有效傳輸。

REST(Representational State Transfer,表述性狀態轉移)從技術上講也可以認為是RPC架構的一種具體表現形式,因為RPC架構中最基本的網路通信、序列化/反序列化、傳輸協議和服務調用等組件都能在REST中有所體現。但REST代表的並不是一種技術,也不是一種標準和規範,而是一種設計風格。要理解RESTful架構,最好的方法就是去理解它的全稱Representational State Transfer這個片語,直譯過來就是「表述性狀態轉移」,針對的是網路上的各種資源(Resource)。所以REST通俗地講就是:資源在網路中以某種表現形式進行狀態轉移。

消息通信(Messaging)機制(或者稱為消息傳遞機制)可以認為是一種系統集成組件,是在分散式系統中完成消息的發送和接收的基礎軟體,用於消除服務交互過程中的耦合度。關於耦合度的具體表現形式,我們在下一節中還會具體展開,消息通信機制實現系統解耦的做法是在服務與服務之間添加一個中間層,這樣緊耦合的單階段方法調用就轉變成松耦合的兩階段過程,可以通過中間層進行消息的存儲和處理,這個中間層就是以各種消息中間件為代表的消息通信系統(Messaging System)。

企業服務匯流排(Enterprise Service Bus,ESB)本質上也是一種系統集成組件,用於解決分散式環境下的非同步協作問題,可以看作是對消息通信系統的擴展和延伸。ESB提供了一批核心組件,包括路由器、轉換器和端點。路由器(Router)在一個位置上維護消息目標地址並基於消息本身或上下文進行路由;轉換器(Transformer)用於異構系統之間進行數據適配,數據結構、類型、表現形式、傳輸方式都是潛在的需要轉換的對象;端點(Endpoint)封裝了應用系統與服務匯流排系統的交互。

(2)數據集成

數據集成同樣可以用於微服務之間的交互,常見的共享資料庫(Shared Database)是一個選擇,但也可以通過數據複製(Data Replication)的方式實現數據集成。

在微服務架構中,我們追求數據的獨立性。但對於一些遺留系統而言,我們無法重新打造數據體系,數據複製就成為一種折中的集成方法。所謂數據複製,就是在不同的數據容器中保存同一份業務數據。這裡的同一份業務數據的概念不在於數據內容的完全一致性,而在於這些數據背後的業務邏輯的一致性。

(3)客戶端集成

由於微服務是一個能夠獨立運行的整體,有些微服務會包含一些UI界面,這也意味著微服務之間也可以通過UI界面進行集成。從某一個微服務的角度講,調用它的服務就是該服務的客戶端。關於客戶端與微服務之間的集成可以分為三種方式,即直接集成、使用FrontEnd伺服器和使用API網關。

直接集成方式比較簡單,就是客戶端通過微服務提供的訪問入口直接對微服務進行集成。這種方式適合於微服務數量不是太多的場景。如果採用直接集成的方式,服務按照業務模塊進行邊界劃分和命名是一項最佳實踐。

FrontEnd伺服器有時候也可以認為是一種Portal(門戶)機制,即把客戶端所需要的各種CSS、Javascript等公共資源統一放在FrontEnd伺服器,然後每個微服務包含自身特有的HTML等客戶端代碼片段以及業務邏輯,通過集成FrontEnd伺服器上的公共資源完成獨立服務的運行。

當微服務數量較多且客戶端集成場景比較複雜時,通常就需要單獨抽取一層作為客戶端訪問的統一入口,這一層在微服務架構里稱為API網關(Gateway)。API網關的主要作用是對後端的各個微服務進行整合,從而為不同的客戶端提供定製化的內容。

(4)外部集成

這裡把外部集成單獨剝離出來的原因在於現實中很多服務之間的集成需求來自於與外部服務的依賴和整合,而在集成方式上也可以綜合採用介面集成、數據集成和客戶端集成。

以上集成方式各有其應用場景和特點,現實中的很多系統包含的集成方式並不限於其中一種。關於服務拆分和服務集成的方法論與工程實踐不是本書的重點,讀者可參看筆者的《微服務設計原理與架構》[1]一書做進一步了解。在本書中,我們重點介紹的介面集成,並試圖通過響應式編程的方式實現基於RESTful風格以及消息通信的微服務集成需求。

微服務架構的核心組件

微服務架構的實現首先需要提供一系列基礎組件,包括事件驅動、集群與負載均衡、服務路由等分散式環境下的通用組件,也包括API網關和配置管理等微服務架構所特有的功能組件。同時,基於服務註冊中心的服務發布和訂閱機制是微服務體系下實現服務治理的基本手段。而關於如何保證服務的可靠性,我們也需要考慮服務容錯、服務隔離、服務限流和服務降級等需求和實現方案。最後,我們也需要使用服務監控手段來管理服務質量和運行時狀態。

1.事件驅動

事件驅動架構(Event-Driven Architecture,EDA)定義了一個設計和實現應用系統的架構風格,在這個架構風格里事件可傳輸於鬆散耦合的組件和服務之間。事件處理架構的優勢就在於當系統中需要添加另一個業務邏輯來完成整個流程時,只需要對處於該流程中的事件添加一個訂閱者即可,不需要對原有系統做大量修改。考慮到在微服務架構中服務數量較多且不可避免地需要對服務進行重構,事件處理在系統擴展性上的優勢就尤為明顯。而在技術實現上,通過消息通信機制,我們不必花費太大代價就能實現事件驅動架構。響應式編程從一定程度上也是事件驅動架構的一種表現形式。

2.負載均衡

集群(Cluster)指的是將幾台伺服器集中在一起實現同一業務。而負載均衡(Load Balance)就是將請求分攤到位於集群中的多個伺服器上進行執行。負載均衡根據伺服器地址列表所存放的位置可以分成兩大類,一類是伺服器端負載均衡,另一類是客戶端負載均衡。另一方面,以各種負載均衡演算法為基礎的分發策略決定了負載均衡的效果。在集群化環境中,當客戶端請求到達集群,如何確定由某一台伺服器進行請求響應就是服務路由(Routing)問題。從這個角度講,負載均衡也是一種路由方案,但是負載均衡的出發點是提供服務分發而不是解決路由問題,常見的靜態、動態負載均衡演算法也無法實現精細化的路由管理。服務路由的管理可以歸為幾個大類,包括直接路由、間接路由和路由規則[1]

3.API網關

API網關本質上就是一種外觀模式(Fa?adePattren)的具體實現,它是一種伺服器端應用程序並作為系統訪問的唯一入口。API網關封裝了系統內部架構,為每個客戶端提供一個定製的API。同時,它可能還具有身份驗證、監控、緩存、請求管理、靜態響應處理等功能。在微服務架構中,API網關的核心要點是所有的客戶端和消費端都通過統一的網關接入微服務,在網關層處理通用的非業務功能。

4.配置中心

在微服務架構中,一般都需要引入配置中心(Configuration Center)的相關工具。採用配置中心也就意味著採用集中式配置管理的設計思想。對於集中式配置中心而言,開發、測試和生產等不同的環境配置信息保存在統一存儲媒介中,這是一個維度。而另一個維度就是分散式集群環境,需要確保集群中同一類服務的所有伺服器保存同一份配置文件,並且能夠同步更新。

5.服務治理

在微服務架構中,服務治理(Service Governance)可以說是最關鍵的一個要素,因為各個微服務需要通過服務治理實現自動化的服務註冊(Registration)和發現(Discovery)。服務治理的需求來自服務的數量。如果在服務數量並不是太多的場景下,服務消費者獲取服務提供者地址的基本思路是通過配置中心,當服務的消費者需要調用某個服務時,基於配置中心中存儲的目標服務的具體地址構建鏈路完成調用。但當服務數量較多時,為了實現微服務架構中的服務註冊和發現,通常都需要構建一個獨立的媒介來管理服務的實例,這個媒介一般被稱為服務註冊中心(Service Registration Center)。

另一方面,服務提供者和服務消費者都相當於服務註冊中心的客戶端應用程序。在系統運行時,服務提供者的註冊中心客戶端程序會向註冊中心註冊自身提供的服務,而服務消費者的註冊中心客戶端程序則從註冊中心查詢當前訂閱的服務信息並周期性地刷新服務狀態。同時,為了提高服務路由的效率和容錯性,服務消費者可以配備緩存機制以加速服務路由。更重要的是,當服務註冊中心不可用時,服務消費者可以利用本地緩存路由實現對現有服務的可靠調用。

6.服務可靠

在微服務架構中,各個服務獨立部署且服務與服務之間存在相互依賴關係。和單塊系統相比,微服務架構中出現服務訪問失敗的原因和場景非常複雜,這就需要我們從服務可靠性的角度出發對服務自身以及服務與服務之間的交互過程進行設計。

針對服務失敗,常見的應對策略包括超時(Timeout)和重試(Retry)機制。超時機制指的是調用服務的操作可以配置為執行超時,如果服務未能在這個時間內響應,將回復一個失敗消息。同時,為了降低網路瞬態異常所造成的網路通信問題,可以使用重試機制。這兩種方式都會產生同步等待,因此合理限制超時時間和重試次數是一般的做法。

當服務運行在一個集群中,出現通信鏈路故障、服務端超時以及業務異常等場景都會導致服務調用失敗。容錯(Fault Tolerance)機制的基本思想是冗餘和重試,即當一個伺服器出現問題時不妨試試其他伺服器。集群的建立已經滿足冗餘的條件,而圍繞如何進行重試就產生了Failover、Failback等幾種常見的集群容錯策略。

服務隔離(Isolation)包括一些常見的隔離思路以及特定的隔離實現技術框架。所謂隔離,本質上是對系統或資源進行分割,從而實現當系統發生故障時能限定傳播範圍和影響範圍,即發生故障後只有出問題的服務不可用,保證其他服務仍然可用。常見的隔離措施包括線程隔離、進程隔離、集群隔離、機房隔離和讀寫隔離等[5]

關於服務可靠性,還有一個重要的概念稱為服務熔斷(Circuit Breaker)。服務熔斷類似現實世界中的「保險絲」,當某個異常條件被觸發時,就直接熔斷整個服務,並不是一直等到此服務超時。而服務降級就是當某個服務熔斷之後,服務端準備一個本地的回退(Fallback)方法,該方法返回一個默認值。

7.服務監控

我們知道在傳統的單塊系統中,所有的代碼都在同一台伺服器上,如果服務運行時出現錯誤和異常,我們只要關注一台伺服器就可以快速定位和處理問題。但在微服務架構中,事情顯然沒有那麼簡單。微服務架構的本質也是一種分散式架構,微服務架構的特點決定了各個服務部署在分散式環境中。各個微服務獨立部署和運行,彼此通過網路交互,而且都是無狀態的服務,一個客戶端請求可能需要經過很多個微服務的處理和傳遞才能完成業務邏輯。在這種場景下,我們首先面臨的一個核心問題是如何管理服務之間的調用關係;另一方面,如何跟蹤業務流的處理順序和結果也是服務監控的核心問題。通常,我們需要藉助於日誌聚合和服務跟蹤技術來解決這兩個核心問題。

微服務架構技術體系

本書的定位是討論響應式微服務架構構建過程中的工程實踐。無論是實現響應式微服務架構還是傳統的微服務架構,都需要藉助於某一種具體的技術體系。

為了實現微服務架構,首先需要選擇一種主流的工具來構建單個微服務。當系統中存在多個微服務時,我們就應該提供服務治理、負載均衡、服務容錯、API網關、配置中心、事件驅動等實現方案以完成服務之間的交互和集成。同時,微服務架構的技術體系也包括如何對微服務進行測試,以及基於日誌聚合和服務跟蹤的服務監控管理。

1.微服務核心組件的實現技術

微服務之間首先需要進行通信。對於服務通信,微服務架構明確要求服務之間通過跨進程的遠程調用方式進行通信。關於遠程調用,有三種風格的解決方案,即RPC、REST和自定義實現。而在服務與服務的交互方式上,也存在兩個維度,即按照交互對象的數量分為一對一和一對多,以及按照請求響應的方式分為同步和非同步。目前RPC框架可供選型的餘地很大,如AlibabaDubbo、Facebook Thrift以及Google gRPC等都是非常主流的實現,而基於REST的實現框架則包括Jersey、Spring MVC以及本書中將要詳細介紹的響應式Spring WebFlux等。

事件驅動架構實現工具的表現形式通常是各種消息中間件,如基於JMS(Java Message Service,Java消息服務)規範的ActiveMQ、基AMQP(Advanced Message Queuing Protocol,高級消息隊列協議)規範的RabbitMQ、在大數據流式計算領域中應用非常廣泛的Kafka,當然還有像Alibaba自研的RocketMQ。在這些消息中間件中,ActiveMQ一般不考慮,如果是相對比較輕量級的應用,則可以選擇RabbitMQ,而Kafka和RocketMQ則適合大型應用的場景。

負載均衡分為伺服器端負載均衡和客戶端負載均衡兩大類實現方案。在伺服器軟體中,我們可以選擇Nginx、HA proxy、Apache、LVS等工具。而類似Netflix Ribbon的工具則是一種單獨可以使用的負載均衡機制。事實上,所有的分散式服務框架幾乎都內置了負載均衡的實現,所以負載均衡本身並不需要做太多的選擇。

API網關是微服務架構的核心組件之一。Netflix OSS(Open Source Software)中有一個Zuul提供了一套過濾器機制,可以很好地支持簽名校驗、登錄校驗等前置過濾功能處理,同時它也維護了路由規則和服務實例,以便完成服務路由功能。其他可供參考的API網關還有開源的Spring Cloud Gateway和Kong等。

配置管理的作用是完成集中式的配置信息管理。目前比較主流的包括Spring旗下的Spring Cloud Config、淘寶的Diamond和百度的Disconf等。在實現上,Spring Cloud Config支持將配置信息存放在配置服務本地的內存中,也支持放在遠程Git倉庫中,這點與其他工具在設計上有較大不同。Diamond和Disconf都是基於Mysql作為存儲媒介,Diamond採用拉模型,即每隔15s拉一次全量數據;而Disconf基於Zookeeper的推模型,實時推送。在配置數據模型上,Diamond只支持Key-Value結構的數據,採用的是非配置文件模式;而Disconf支持傳統的配置文件模式,也支持Key-Value結構數據。

關於服務註冊和服務發現,比較常見的分散式一致性協議是Paxos協議[6]和Raft協議[7]。相比Paxos協議,Raft 協議易於理解和實現,因此,最新的分散式一致性方案大都選擇Raft協議。我們知道Zookeeper 採用的是基於Paxos協議改進的ZAB(Zookeeper Atomic Broadcast,Zookeeper原子消息廣播)協議,而 Raft 協議的實現工具主要是Consul 和Etcd。註冊中心是任何一個微服務框架所必不可少的組件,很多框架都內建了對服務註冊中心的支持。例如,Alibaba的Dubbo框架支持包括Zookeeper、Redis等在內的多種註冊中心實現,默認採用的是Zookeeper;新浪的Motan支持Zookeeper,也支持Consul;Spring Cloud也同時提供了Spring Cloud Consul和Spring Cloud Zookeeper兩種實現方案;而Netflix OSS中使用的是Eureka。

服務可靠性相關的功能主要包括服務容錯、服務隔離、服務限流和服務降級,其中大多數機制都偏向於實現策略而不是實現工具。我們需要明確的是如何實現服務隔離和服務熔斷機制的框架。服務熔斷器可選的開源方案包括NetflixHystrix和Resilience4j。

2.Spring Cloud

在本書中,我們將採用Spring Cloud作為微服務架構的實現框架。組件完備性是我們選擇該框架的主要原因。Spring Cloud是Spring家族中新的一員,重點打造面向服務化的功能組件,在功能上服務註冊中心、API網關、服務熔斷器、分散式配置中心和服務監控等組件都能在Spring Cloud中找到對應的實現。

另一個選擇Spring Cloud的原因在於服務之間的交互方式。我們知道微服務架構中推崇基於HTTP協議的RESTful風格實現服務間通信,而諸如Dubbo等框架的服務調用基於長連接的RPC實現。採用RPC方式會導致服務提供方與調用方介面產生較強依賴,而且服務對技術敏感,無法做到完全通用。Spring Cloud採用的就是RESTful風格,這方面更加符合微服務架構的設計理念。

Spring Cloud還具備一個天生的優勢,因為它是 Spring 家族中的一員,而Spring在開發領域的強大地位給Spring Cloud起到了很好的推動作用。同時,Spring Cloud基於Spring Boot,而Spring Boot目前已經在越來越多的公司得到應用和推廣,用來簡化Spring應用的框架搭建以及開發過程。Spring Cloud也繼承了Spring Boot 簡單配置、快速開發、輕鬆部署的特點,讓原本複雜的開發工作變得相對容易上手。

在本書後續章節中,我們將看到如何使用Spring Cloud實現微服務架構中的各個核心組件。

構建響應式微服務架構

使用微服務架構最關鍵的一個原則就是將系統劃分成一個個相互隔離、無依賴的微服務,這些微服務通過定義良好的協議進行通信。本節將討論構建響應式微服務架構的一些設計原則和理念,並探討整合響應式編程和微服務架構的方法。

響應式微服務架構設計原則

Reactive Microservices Architecture[8]一書講述了響應式微服務架構的核心概念以及實施過程中的一些最佳實踐。本節將介紹這些核心概念和最佳實踐,以便讀者能夠更好地理解響應式微服務架構。

1.隔離一切事物

在微服務架構中,我們經常會提到雪崩效應(Avalanche Effect)這一概念。服務雪崩的產生是一種擴散效應。當系統中存在兩個服務A和B,如果A服務出現問題,而B服務會通過用戶不斷提交服務請求等手工重試或代碼邏輯自動重試等手段進一步加大對A服務的訪問流量。因為B服務使用同步調用,會產生大量的等待線程佔用系統資源。一旦線程資源被耗盡,B服務提供的服務本身也將處於不可用狀態,整個過程的演變可參考圖1-5。而這一效果在整個服務訪問鏈路上進行擴散,就形成了雪崩效應。

雪崩效應的預防需要依賴於架構設計中的一種稱為艙壁隔離(Bulkhead Isolation)的架構模式。所謂艙壁隔離,顧名思義就是像艙壁一樣對資源或失敗單元進行隔離,如果一個船艙破了進水,只損失一個船艙,其他船艙可以不受影響。艙壁隔離模式在微服務架構中的應用就是各種服務隔離思想。

使用Spring 5實現響應式微服務架構,簡潔版來了

圖1-5雪崩效應示意圖

隔離是微服務架構中最重要的特性,也是實現響應式宣言中所提倡的彈性、可伸縮系統的前提。所謂彈性,就是從失敗中恢復的能力,依賴於這種艙壁和失敗隔離的設計,並且需要打破同步通信機制。由此,微服務一般是在邊界之間使用非同步消息傳輸,從而使得正常的業務邏輯避免對捕獲錯誤、錯誤處理的依賴。

2.自主行動

上面所講的隔離性是自主性的前提。只有當服務之間是完全隔離的,才可能實現完全的自主,包括獨立的決策、獨立的行動以及與其他服務協調合作來解決問題。

從實現上講,服務自主性僅僅需要確保其對外公布協議的正確性即可。自主性不僅能夠讓我們更好地了解微服務系統以及各個服務的領域模型,也能夠在面對衝突和失敗狀況時,確保快速定位到問題出在具體的哪一個微服務中。

3.只做一件事,並且盡量做好

大家都知道面向對象設計中有一條單一職責原則(Single Responsibility Principle,SRP),而在微服務架構中一個很大的問題是如何正確地劃分各個服務的大小。多大的粒度才能被稱之為「微」服務,顯然,這個「微」是和服務本身的職責有直接關係,我們希望一個服務只做一件事,而且在服務內部要把相關的功能做到盡量好。

每一個服務都應該只有一個存在的原因,業務和職責不應該糅雜在一起。如果滿足這個要求,所有的服務組織在一起從整體上就能夠便於擴展、具有彈性、易理解和維護。

4.擁有自己的私有狀態

在軟體設計領域經常會提到狀態(State)這個詞,而服務之間的狀態本質上體現的還是一種數據關係。如果一個數據需要在多個服務之間共享才能完成一項業務功能,那麼這項業務功能就被稱為有狀態。基於這項業務功能所設計和實現的一系列服務之間就形成了一種狀態性,這一系列服務就是有狀態服務。

很多服務都會把自己的狀態下沉到一個龐大的共享資料庫中,這也是一些傳統Web框架的做法。這種做法就會造成在擴展性、可用性以及數據集成上很難做好把控。而在本質上,一個使用共享資料庫的微服務架構本質還是一個單體應用。一個服務既然具有單一職責,那麼合理的方式就應該是該服務擁有自己的狀態和持久化機制,建模成一個邊界上下文。這裡就需要充分應用領域驅動設計(Domain Driven Design,DDD)中的相關策略設計和技術設計方面的方法和工程實踐。關於領域驅動設計以及背後的Event Sourcing(事件溯源)和CQRS(Command Query Responsibility Segreation,命令查詢職責分離)等概念,讀者可參考《實現領域驅動設計》[9],這裡不做具體展開。

5.擁抱非同步消息傳遞

從軟體設計上講存在三種不同層級的耦合度,即技術耦合、空間耦合和時間耦合。技術耦合度表現在服務提供者與服務消費者之間需要使用同一種技術實現方式。如圖1-6a中服務提供者與服務消費者都使用RMI(Remote Method Invocation)作為通信的基本技術,而RMI是Java領域特有的技術,也就意味著其他服務消費者想要使用該服務也只能採用Java作為它的基本開發語言;空間耦合度指的是服務提供者與服務消費者都需要使用統一的方法簽名才能相互協作,圖1-6b中的getUserById(id)方法名稱和參數的定義就是這種耦合的具體體現;而時間耦合度則表現在服務提供者與服務消費者只有同時在線才能完成一個完整的服務調用過程,如果出現圖1-6c中所示的服務提供者不可用的情況,顯然,服務消費者調用該服務就會發生失敗。

使用Spring 5實現響應式微服務架構,簡潔版來了

圖1-6耦合度的三種表現形式

微服務之間通信的最佳機制就是非同步消息傳遞,消息傳遞能夠從技術、空間和時間等多個維度上緩解甚至消除圖1-6中的三種耦合度。我們在第5章中會進一步對該話題展開討論。

同時,非同步非阻塞執行是對資源的高效操作,能夠最小化訪問共享資源時的阻塞消耗,從而提升整體系統的性能。

6.保持移動,但可定址

非同步消息傳遞帶來了服務的位置透明性。所謂位置透明,指的是在多核或者多結點上的微服務在運行時無須改變結點,即可動態擴展的能力。這也決定了系統的彈性和移動性,要實現這些需要依賴雲計算帶來的一些特性和按需使用的模型。

另一方面,可定址則是指服務的地址需要穩定,從而可以無限地引用此服務,無論服務目前是否可以被定位到。當服務在運行中、已停止、被掛起、升級中、已崩潰等情形下,地址都應該是可用的,任意客戶端能夠隨時發送消息給一個地址。從這個角度講,地址應該是虛擬的,可以代表一組實例提供的服務。使用虛擬地址能夠讓服務消費方無須關心服務目前是如何配置操作的,只要知道地址即可。

整合響應式編程與微服務架構

構建一個分散式系統是複雜而困難的一項工作,微服務架構基於分散式,同時又需要考慮彈性、可伸縮性、隔離性等一系列問題。作為一個微服務架構,服務與服務之間、服務與外部系統之間的通信都是必需的。當我們對被依賴的服務和外部系統無法把控時,就會有很大的失敗風險。因此,即使雙方之間的通信協議定義得再好,也不能信賴外部服務或系統,需要做好各種措施以保證自身服務的安全。這裡我們就可以充分整合響應式編程和微服務架構來實現這一目標。

響應式編程和微服務架構的一個整合點在於我們可以採用響應式編程中的背壓(Backpressure)機制來實現數據流處理速度的一致性。在背壓機制下,接收方根據自己的接受狀況調節接受速率,通過反向的響應來控制發送方的發送速率,以防止一個系統中快速生成數據的部分壓垮處理數據較慢的部分。目前,越來越多的工具和框架都在開始擁抱響應式流(Reactive Streams)規範,這些技術使用非同步背壓實時流來橋接系統,從而在總體上提高系統的可靠性、性能以及互操作性。關於背壓和響應式流的具體概念和實現方法,將在下一章具體展開討論。

在微服務架構的通信模式上,要盡量避免使用同步通信機制,否則就把自身服務的可用性放在了所依賴的第三方服務的控制範圍中。上一節中對雪崩效應的產生原因分析已經非常明確地說明了這一點。避免級聯失敗需要服務足夠解耦和隔離,使用非同步通信機制是一個最佳的方案。當然,傳統的RESTful風格的服務調用仍然適用於可控的服務調用上。本書也會分別介紹響應式編程環境下RESTful風格和非同步通信風格的服務通信模式及實現方法。

另一方面,整個微服務架構需要的是一種全棧式的響應式環境,即響應式微服務開發方式的有效性取決於在整個請求鏈路中採用了全棧的響應式編程模型。如果某一個環節或步驟不是響應式的,就會出現同步阻塞,從而導致背壓機制無法生效。常見的同步阻塞產生的環節除了服務與服務之間的同步通信,還有基於關係型資料庫的數據訪問,因為傳統的關係型資料庫都是採用非響應式的數據訪問機制。本書也會詳細介紹如何使用響應式的數據訪問組件實現全棧的響應式編程模型。

本文節選自電子工業出版社《Spring響應式微服務:Spring Boot 2+Spring 5+Spring Cloud實戰》第一章,由電子工業出版社博文視點授權。本書主要包含構建響應式微服務架構過程中所應具備的技術體系和工程實踐。圍繞響應式編程和微服務 架構的整合,討論如何使用 Reactor 響應式編程框架、如何構建響應式 RESTful 服務、如何構建響應式數據訪問組件、如何構建響應式消息通信組件、如何構建響應式微服務架構,以及如何測試響應式微服務 架構等核心主題,並基於這些核心主題給出具體的案例分析。

使用Spring 5實現響應式微服務架構,簡潔版來了

基於篇幅的考慮,部分內容進行了簡化,想了解本書全部詳細內容,可以點擊閱讀原文直接購買。

本次活動我們採取文章留言送書的活動。在本周末前,留言點贊數最高的前 5 名我們將免費贈送本書!圖書由電子工業出版社博文視點提供。

參考閱讀:

  • 終於有人把服務調用說清楚了

  • 一文理解分散式服務架構下的混沌工程實踐(含PPT)

  • 從工具到社區,美圖秀秀大規模性能優化實踐

技術原創及架構實踐文章,歡迎通過公眾號菜單「聯繫我們」進行投稿。轉載請註明來自高可用架構「ArchNotes」微信公眾號及包含以下二維碼。

高可用架構

改變互聯網的構建方式

長按二維碼 關注「高可用架構」公眾號

喜歡這篇文章嗎?立刻分享出去讓更多人知道吧!

本站內容充實豐富,博大精深,小編精選每日熱門資訊,隨時更新,點擊「搶先收到最新資訊」瀏覽吧!


請您繼續閱讀更多來自 高可用架構 的精彩文章:

專訪微軟亞洲研究院首席研發經理鄒欣:AI 時代程序員將往哪走?
終於有人把服務調用說清楚了

TAG:高可用架構 |