本文將介紹微服務架構設計中的設計模式、原則及最佳實踐。我們將使用適當?shù)募軜嬙O計模式和技術。 通過本文,你將了解到如何利用微服務分布式架構設計一個高可用、高可擴展、低延遲且對網(wǎng)絡故障有彈性的系統(tǒng),以處理數(shù)以百萬計的請求。 事件驅(qū)動的架構 本文將教給你如何從單體架構一步步演進到事件驅(qū)動的微服務架構。 我們將從基本的軟件架構設計入手,設計一個可以處理少量請求的單體架構的電子商務應用。 架構設計之旅 之后,我們將介紹該架構如何一步步演進:
本文既有理論知識,又有實用信息:
因此,我們會對架構進行迭代設計,從單體架構逐步演進為事件驅(qū)動的微服務架構。 我們將根據(jù)以下問題來演進架構:
因此,我們是從以下幾個方面來改進架構: 可擴展性和可靠性可以衡量應用程序能夠為終端用戶提供何種程度的服務。如果我們的電子商務應用可以為數(shù)百萬用戶提供服務而不出現(xiàn)可以覺察的停機,那么我們就可以說這個系統(tǒng)是高度可擴展和可靠的。可擴展性和可用性可能是設計良好的架構需要考慮的主要因素。 可擴展性 = 電子商務應用程序應能為數(shù)百萬用戶提供服務 可用性 = 電子商務應用應該 7*24 小時可用 可維護性 = 電子商務應用應該可以發(fā)展數(shù)年之久 效率 = 電子商務應用的響應延遲在可接受的范圍內(nèi),如小于 2 秒,即低延遲 現(xiàn)在讓我們看下可接受的延遲。如果我們的應用程序用戶越來越多,我們?nèi)绾巫寫贸绦虻难舆t在可接受的范圍內(nèi)?請看下表: 從表中可以看出,我們的電子商務應用是一個小型應用,開始只有 2K 并發(fā)用戶,每秒 500 個請求。我們將根據(jù)預期的體量來設計該電子商務應用的架構。 之后,隨著業(yè)務不斷增長,它將需要更多的資源來適應更大的請求數(shù),你將看到我們?nèi)绾胃鶕?jù)這些數(shù)值來演進我們的架構。 幾十年的軟件開發(fā)演變出了許多方法和模式,它們各有各的優(yōu)勢,也都面臨著各自的挑戰(zhàn)。 因此,我們將從理解現(xiàn)有的方法入手來設計電子商務應用的架構,并逐步演進轉移到云上。 為了理解云原生微服務,我們需要理解什么是單體應用程序,以及我們?nèi)绾螐膯误w架構遷移到微服務架構。 對于遺留應用,可以說大部分都是以單體架構為主實現(xiàn)的。 如果一個項目的所有功能都在一個代碼庫中,那么該應用就是單體應用。在單體模式中,用戶界面、業(yè)務代碼和數(shù)據(jù)訪問的所有東西都在同一個代碼庫里。 所有應用關注點都包含在一個大的部署中。即使是單體應用也可以設計出不同的層次,如表現(xiàn)層、業(yè)務層和數(shù)據(jù)層,然后將該代碼庫部署為單個 jar/war 文件。 單體方法也有不少優(yōu)點,我們將在即將推出的視頻中討論它們。在這里,我將介紹一些主要的優(yōu)缺點。 由于只有一個代碼庫,所以很容易拉取并參與其中。而且,因為在同一個項目中,所以不同模塊間的業(yè)務交互很容易調(diào)試。 遺憾的是,單體架構有許多許多缺點,如:
如你所見,我們了解單體架構。 雖然單體架構有很多缺點,但如果你正在構建一個小型應用程序,那么單體架構仍然是你可以在項目中采用的最佳架構之一。因為,在許多方面,單體應用程序都比較簡單。 它們很容易:
與需要有經(jīng)驗的開發(fā)人員來識別和開發(fā)服務的微服務架構相比,它的開發(fā)相對簡單。它更容易部署,因為只需要部署一個 jar/war 文件。 在這一節(jié)中,我們將使用單體架構一步一步地設計我們的電子商務應用程序。我們將根據(jù)需求逐步對架構設計進行迭代。 我們應該總是從編寫 FR(功能需求)和 NFR(非功能需求)開始。
此外,最好在架構圖中加上規(guī)則,以免忘記。
我們在設計架構時會考慮這些規(guī)則。 如你所見,我們使用單體架構設計了電子商務應用。 我們添加了一個大的 E-Commerce 框,它是電子商務應用程序的組成部分,其中包括商店用戶界面、分類服務、SC 服務、折扣服務、訂單服務。如你所見,這個傳統(tǒng) Web 應用程序的所有模塊是容器中的一個工件。 這個單體應用有一個龐大的代碼庫,其中包括所有模塊。如果要在這個應用程序中增加新模塊,就必須對現(xiàn)有的代碼進行修改,然后將代碼修改后的工件部署到 Tomcat 服務器上。簡單起見,我們遵循 KISS 原則。 我們將根據(jù)需求重構我們的設計,并一步步進行迭代。 從圖中可以看出,我們增加了 2 臺應用服務器,對單體架構做了橫向擴展,并在單體應用的客戶端和電子商務應用之間加了一個負載均衡器。 在單體架構中,為了實現(xiàn)擴展,我們需要增加 E-Commerce 應用服務器,并在應用程序之前放一個負載均衡器。 本質(zhì)上,負載平衡器將接受請求并使用一致性哈希算法將請求發(fā)送到電子商務應用服務器,保證服務器的負載都一樣。 現(xiàn)在我們看下技術選項——適配技術棧。 從圖中可以看出,我們已經(jīng)為電子商務單體應用選出了潛在的選項。負載均衡,NGINX 是很好的選擇,還有 Java——Oracle 為這類應用提供了標準實現(xiàn)。 微服務是小型的業(yè)務服務,它們可以自主 / 獨立部署,協(xié)同工作。
因此,我們可以說,微服務架構是一種云原生的架構方法,利用這種方法設計出的應用程序由許多松耦合的、獨立部署的小型組件組成。 ——有自己的技術棧,包括數(shù)據(jù)庫和數(shù)據(jù)管理模型; ——通過 REST API、事件流和消息代理等相互通信; ——按業(yè)務能力來組織,劃分服務的界限通常被稱為有界上下文。 在接下來的章節(jié)中,我們還將看到,如何利用有界上下文解耦微服務。 微服務的特點是小、獨立和松耦合。一個小型開發(fā)團隊就可以編寫和維護一個服務。每個服務都有一個獨立的代碼庫,可以由一個小型開發(fā)團隊來管理。 服務可以獨立部署。團隊可以更新一個現(xiàn)有的服務,而不需要重新構建和部署整個應用程序。 服務負責持久化它們自己的數(shù)據(jù)或外部狀態(tài)。這點與傳統(tǒng)模式不同,在傳統(tǒng)模式中,有一個單獨的數(shù)據(jù)層處理數(shù)據(jù)持久性。 微服務最重要的一個特點是小,可以獨立部署。 微服務應該足夠小,以至于一個單功能團隊就可以構建、測試和部署它。 微服務可以獨立擴展,你可以單獨擴展某個子服務,而無需擴展整個應用程序。 微服務應用程序有很多服務組成,這些服務需要協(xié)同工作來創(chuàng)造價值。由于服務很多,與單體應用相比,這意味著更多的移動部件。 由于微服務很小,而且服務之間需要通信,所以我們要管理網(wǎng)絡問題。 微服務有自己的數(shù)據(jù)持久化。因此,數(shù)據(jù)一致性會成為一項挑戰(zhàn)。 在這一節(jié)中,我們將一步步地設計微服務架構,并根據(jù)需求,逐步迭代架構設計。 在設計微服務架構時,我們遵循了'服務獨享數(shù)據(jù)庫模式'。微服務是單體應用模塊分解而成的獨立服務。 如此一來,現(xiàn)在這些數(shù)據(jù)庫就可以是混合持久化。也就是說,根據(jù)每個微服務的存儲需求不同,Product 微服務可以使用 NoSQL 文檔數(shù)據(jù)庫,SC 微服務可以使用 NoSQL 鍵值對數(shù)據(jù)庫,Order 微服務可以使用關系型數(shù)據(jù)庫。 讓我們看下這個微服務架構圖,并思考一下這個架構缺少了什么?這個架構的痛點是什么?我們怎么改進這個架構,才能提供更高的可擴展性、可用性,并且支撐更多的并發(fā)請求? 我們看到,UI 和微服務是直接通信的,這看上去很難管理。我們現(xiàn)在應該重點關注下微服務通信。 當遷移到基于微服務的應用程序時,最大的挑戰(zhàn)之一是通信機制的變化。因為微服務是分布式的,微服務之間的通信是通過網(wǎng)絡層面的服務間通信完成的。每個微服務都有自己的實例和進程。 因此,服務必須使用服務間通信協(xié)議,如 HTTP、gRPC 或消息代理協(xié)議 AMQP 進行交互。 由于微服務擁有復雜的結構,服務都是獨立開發(fā)和部署,所以我們在考慮通信類型時應該謹慎,并在設計階段做妥善處理。 如果你想基于微服務設計和構建具有多個客戶端應用程序的復雜的大型應用程序,則建議使用 API 網(wǎng)關模式。 該模式提供了一個反向代理,將請求重定向或路由到內(nèi)部微服務端點。API 網(wǎng)關為客戶端應用程序提供一個單一的端點,它會在內(nèi)部將請求映射到內(nèi)部微服務。我們應該在客戶端和內(nèi)部微服務之間使用 API 網(wǎng)關。 API 網(wǎng)關可以處理像授權這樣的橫切關注點。因此,授權可以在集中式的 API 網(wǎng)關中處理,并發(fā)送給內(nèi)部微服務,而不是在每個微服務中編寫相關代碼。同時,API 網(wǎng)關控制到內(nèi)部微服務的路由,并能夠?qū)讉€微服務的請求匯總到一個響應中。 總之,API 網(wǎng)關位于客戶端應用程序和內(nèi)部微服務之間。作為一個反向代理,它將請求從客戶端路由到后端服務。它還提供橫切關注點,如身份認證、SSL 終止和緩存。 我們將對電子商務應用程序的架構進行迭代,增加 API 網(wǎng)關模式。 ![]() 從上圖可以看出,客戶端請求由單個入口點收集并路由到內(nèi)部微服務。 它將處理客戶端請求,并提供內(nèi)部微服務路由。它還可以聚合多個內(nèi)部微服務來響應一個客戶端請求,并提供橫切關注點,如身份認證和授權、速率限制和節(jié)流等等。 我們將繼續(xù)演進我們的架構,但請看一下當前的設計,考慮下我們可以如何改進? 這里,有多個客戶應用程序連接到單個 API 網(wǎng)關。我們應該小心這種情況,因為如果我們在這里只放置一個 API 網(wǎng)關,這意味著這里存在單點故障的風險。如果客戶端應用程序增加,或 API 網(wǎng)關中業(yè)務邏輯的復雜性增加,這將是一種反模式。 因此,我們應該用 BFF-backends-for-frontends 模式解決這個問題。 基本上,Backends for Frontends 模式是針對特定的前端應用程序設置單獨的 API 網(wǎng)關。我們有多個供前端應用消費的后端服務,我們在它們之間設置了 API 網(wǎng)關,用于處理路由和聚合操作。 不過,這產(chǎn)生了單一故障點。為了解決這個問題,BFF 提供了多個 API 網(wǎng)關,并根據(jù)客戶端應用程序的邊界進行分組,然后劃分到不同的 API 網(wǎng)關。 ![]() 單個復雜的 API 網(wǎng)關存在風險,并且會成為架構的瓶頸。通常,比較大的系統(tǒng)會按照客戶端類型(如移動、Web 和桌面功能)暴露多個 API 網(wǎng)關。當你不想為多個界面定制單一的后端時,BFF 模式很有用。 所以我們應該根據(jù)用戶界面的不同創(chuàng)建多個 API 網(wǎng)關。這些 API 網(wǎng)關可以與前端環(huán)境實現(xiàn)最佳匹配,而不用擔心影響其他前端應用程序。 Backend for Frontends 模式為實現(xiàn)多網(wǎng)關指明了方向。 我們將根據(jù) Backends for Frontends(BFF)模式迭代我們的電子商務應用架構,增加 API 網(wǎng)關。 ![]() 如你所見,我們在應用程序中加入了多個 API 網(wǎng)關。 我們已經(jīng)在微服務架構中創(chuàng)建了 API 網(wǎng)關,而且已經(jīng)說過,來自客戶端的所有同步請求都通過 API 網(wǎng)關進入內(nèi)部微服務。 但是,如果客戶端請求需要訪問多個內(nèi)部微服務怎么辦?我們?nèi)绾翁幚韮?nèi)部微服務之間的通信? ![]() 在設計微服務應用程序時,我們應該注意后端內(nèi)部微服務之間的通信方式。最好的做法是盡可能地減少服務間通信。 然而,在某些情況下,由于客戶的要求或所請求的操作需要訪問幾個內(nèi)部服務,我們無法減少內(nèi)部通信。 例如,對照上圖考慮這樣一種情況:用戶想要結賬并創(chuàng)建一個訂單。 我們該如何滿足這個請求? 這些調(diào)用把微服務耦合在了一起,在我們的例子里,微服務 Product 和 Pricing 就會相互依賴并耦合。如果其中一個微服務發(fā)生故障,它就不能向客戶端返回數(shù)據(jù),所以它沒有任何容錯性。如果微服務之間的依賴性和耦合性增加,就會產(chǎn)生很多問題,并縮小微服務架構的優(yōu)勢。 如果客戶要對購物車進行結賬,這將觸發(fā)一系列的操作。因此,如果我們試圖用請求 / 響應這種同步消息模式來執(zhí)行這個下單用例,那就會像上面這個圖一樣。 可以看到,一個客戶端的 http 請求會觸發(fā) 6 個同步 http 請求。所以顯然會增加延遲并對系統(tǒng)的性能、可擴展性和可用性產(chǎn)生負面影響。 如果我們有這樣的用例,如果第 5 步或第 6 步失敗了,或者中間的某些服務中斷了怎么辦?即使沒有中斷,某些服務也可能非常繁忙,無法及時響應,造成不可接受的高延遲。 那么,這類需求的解決方案是什么? 我們可以通過兩種方法來解決這種問題:
為了盡量減少服務之間的通信,我們可以使用服務聚合模式。基本上,服務聚合設計模式是接收來自客戶端或 API 網(wǎng)關的請求,然后分配給內(nèi)部多個后端微服務,再將結果合并,并在一個響應結構中發(fā)給請求發(fā)起人。 ![]() 通過實現(xiàn)服務聚合模式,可以減少客戶端和微服務之間的通信量和通信開銷。 在這一節(jié)中,我們將通過添加服務聚合模式 / 服務注冊模式,來迭代我們的電子商務應用架構。 ![]() 如上圖所示,我們在電子商務應用的架構中應用了服務聚合模式 / 服務注冊模式。 如果通信只是在少數(shù)幾個微服務之間進行,那么同步通信就很好。但當涉及到多個微服務相互調(diào)用,并且要等待一些長時間的操作完成時,我們應該使用異步通信。 ![]() 否則,微服務的相互依賴和耦合會導致瓶頸和嚴重的架構問題。 如果你有多個微服務需要彼此交互,而且,你希望這種交互沒有任何依賴性或是松耦合的,那么我們就應該在微服務架構中使用基于異步消息的通信。 因為基于異步消息的通信有賴于事件,所以我們稱這種通信為事件驅(qū)動的通信。 發(fā)布 - 訂閱是一種消息傳遞模式,它的消息發(fā)送者被稱為發(fā)布者,而特定的接收者被稱為訂閱者。 ![]() 因此,發(fā)布者不是直接將消息發(fā)送給訂閱者,而是將發(fā)布的消息進行歸類,并送入消息代理系統(tǒng),但并不知道有哪些訂閱者。同樣地,訂閱者只接收感興趣的消息,而不知道哪些發(fā)布者在發(fā)布消息。 在這一節(jié)中,我們將添加發(fā)布 / 訂閱消息代理,提供微服務異步通信設計,完成電子商務應用的架構迭代。 ![]() 可以看到,我們應用了發(fā)布 / 訂閱消息代理。 如果要適配技術棧,那么我們首先會考慮選用什么發(fā)布 / 訂閱消息代理。下面這兩個都是不錯的選項:
在單體架構中,查詢不同的實體非常方便,因為是由單個數(shù)據(jù)庫來管理數(shù)據(jù),這會很簡單。多表關聯(lián)查詢也很簡單。對數(shù)據(jù)的任何修改都會一起更新,或是一起回滾。關系型數(shù)據(jù)庫具有嚴格的一致性,有 ACID 事務保證,所以管理和查詢數(shù)據(jù)都很容易。 但在微服務架構中,當我們使用“混合持久化”時,這意味著每個微服務都有不同的數(shù)據(jù)庫,包括關系型數(shù)據(jù)庫和 NoSQL 數(shù)據(jù)庫,我們應該制定一個策略,在進行用戶交互時管理好這些數(shù)據(jù)。 因此,這意味著我們在處理微服務之間的數(shù)據(jù)交互時有幾種模式和做法,我們將在本節(jié)中學習這些模式和原則。 微服務是獨立的,只執(zhí)行特定的功能要求。在我們的電子商務應用中,我們有產(chǎn)品、購物車、折扣、訂單等微服務,它們需要彼此交互來滿足客戶的要求。這意味著它們需要頻繁地交互。而這些交互大多是查詢每個服務的數(shù)據(jù)以進行聚合或執(zhí)行邏輯。 CQRS 是跨微服務查詢的重要模式之一。我們可以使用 CQRS 設計模式,以避免復雜的查詢,擺脫低效的連接。CQRS 是命令和查詢責任隔離的意思。本質(zhì)上,這種模式實現(xiàn)了數(shù)據(jù)庫讀取和更新操作的分離。 為了隔離命令和查詢,最好的做法是用 2 個數(shù)據(jù)庫物理地分離讀和寫數(shù)據(jù)庫。此時,如果我們的應用程序是讀密集型的,也就是讀比寫多,我們就可以通過定義自定義數(shù)據(jù)模式來優(yōu)化查詢。 ![]() 物化視圖模式是實現(xiàn)讀數(shù)據(jù)庫的一個很好的例子。因為通過這種方式,我們可以用預定義的細粒度數(shù)據(jù)來避免復雜的連接和映射,進行查詢操作。 通過這種隔離,對于讀數(shù)據(jù)庫和寫數(shù)據(jù)庫,我們甚至可以使用不同的數(shù)據(jù)庫類型,如使用 NoSQL 文檔數(shù)據(jù)庫進行讀取,使用關系數(shù)據(jù)庫進行 CRUD 操作。 我們已經(jīng)學習了 CQRS 模式,該模式主要是與事件源模式一起使用。當搭配使用 CQRS 與事件源模式時,主要的理念是將事件存儲到寫數(shù)據(jù)庫中,這將是作為真相來源的事件數(shù)據(jù)庫。 之后,CQRS 設計模式的讀數(shù)據(jù)庫通過非規(guī)范化表提供數(shù)據(jù)的物化視圖。當然,這個物化視圖的讀數(shù)據(jù)庫消費了來自寫數(shù)據(jù)庫的事件,并將它們轉換為非規(guī)范化的視圖。 ![]() 事件源模式改變了數(shù)據(jù)保存至數(shù)據(jù)庫的操作。它不是將數(shù)據(jù)的最新狀態(tài)保存到數(shù)據(jù)庫,而是將所有事件按數(shù)據(jù)事件發(fā)生的順序保存到數(shù)據(jù)庫。這個事件數(shù)據(jù)庫稱為事件存儲。 它不更新數(shù)據(jù)記錄的狀態(tài),而是將每個修改追加到一個事件的順序表中。因此,事件存儲成為數(shù)據(jù)的真實來源。之后,這些事件存儲通過物化視圖轉換為讀數(shù)據(jù)庫。這種轉換操作可以通過發(fā)布 / 訂閱模式來處理,實現(xiàn)方式是用消息代理系統(tǒng)發(fā)布事件。 我們將在電子商務應用的架構中應用 CQRS、事件源、最終一致性、物化視圖。 ![]() 因此,當用戶創(chuàng)建或更新訂單時,我將使用關系型寫數(shù)據(jù)庫,而當用戶查詢訂單或訂單歷史時,我將使用 NoSQL 讀數(shù)據(jù)庫,并在通過發(fā)布 / 訂閱模式使用消息代理系統(tǒng)同步兩個數(shù)據(jù)庫時使它們保持一致。 現(xiàn)在我們可以考慮這些數(shù)據(jù)庫的技術棧了,我打算用 SQL Server 作為關系型寫數(shù)據(jù)庫,用 Cassandra 作為 NoSQL 讀數(shù)據(jù)庫。當然,我們將使用 Kafka 的發(fā)布 / 訂閱主題交換來同步這兩個數(shù)據(jù)庫??梢钥吹?,我們已經(jīng)完成了微服務數(shù)據(jù)庫模式的設計?,F(xiàn)在,讓我們深入了解下微服務中的事件驅(qū)動架構。 本質(zhì)上,事件驅(qū)動的微服務架構是指通過事件消息傳遞實現(xiàn)微服務之間的通信。在微服務異步通信那一節(jié),我們已經(jīng)從發(fā)布 / 訂閱模式和 Kafka 消息代理系統(tǒng)中了解了這種方式。 我們說,通過事件驅(qū)動架構,我們可以實現(xiàn)異步行為和松耦合的結構。例如,服務不是在需要數(shù)據(jù)時發(fā)送請求,而是通過事件來消費它們。這可以提高性能。 ![]() 事件驅(qū)動的微服務架構也有很大的創(chuàng)新,比如使用實時消息平臺、流處理、事件中心、實時處理、批處理、數(shù)據(jù)智能等等。 因此,我們可以使這種事件驅(qū)動的方法更加通用,并通過實時事件處理特性來演進架構。 在這個新的事件驅(qū)動的微服務架構中,所有通信都是通過事件中心(Event-Hub)進行的??梢哉J為,事件中心是一個可以完成實時處理的大型事件存儲數(shù)據(jù)庫。 我們將利用事件驅(qū)動的微服務架構來設計我們的電子商務應用。 ![]() 現(xiàn)在,讓我們確定下這個架構的技術棧。當然,我們應該選擇 Apache Kafka 作為事件中心,將 Apache Spark 作為實時和準實時流處理應用,對數(shù)據(jù)流進行轉換或回應。 如你所見,現(xiàn)在我們用事件驅(qū)動的微服務架構實現(xiàn)了反應式設計。 現(xiàn)在,我們可以問同樣的問題:我們的設計可以滿足多大的并發(fā)請求? ![]() 這個最新的事件驅(qū)動的微服務架構(通過容器和編排器來部署),可以在低延遲的情況下滿足目標并發(fā)請求。 這個架構是完全松耦合的,并且做了高可擴展性和高可用性設計。 如你所見,我們完成了電子商務微服務架構的設計,這個過程涉及了所有的設計原則和模式。通過學習,你已經(jīng)了解如何在設計中使用這些設計模式了,現(xiàn)在你可以設計自己的架構了。 原文鏈接: https:///design-microservices-architecture-with-patterns/monolithic-to-microservices-architecture-with-patterns-best-practices-a768272797b2 |
|