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