自拍偷在线精品自拍偷,亚洲欧美中文日韩v在线观看不卡

多層依賴:如何避免落入數(shù)據(jù)服務(wù)接口的陷阱?

開發(fā) 前端
我們先看看熟悉的傳統(tǒng)單體架構(gòu)設(shè)計(jì)思路,重點(diǎn)關(guān)注一下它有什么缺點(diǎn),這有助于你理解為什么會(huì)出現(xiàn) CQRS 這種模式。這里我們繼續(xù)沿用前面課程里用戶中心的案例,請(qǐng)看下圖。圖里面展示了單體服務(wù)狀態(tài)下的用戶中心,這時(shí)候高頻和低頻的讀寫服務(wù)放在了一起。

前面,我們討論了不同類型系統(tǒng)(如讀多寫少、強(qiáng)一致、寫多讀少和讀寫密集)的優(yōu)化方法。但在很多復(fù)雜的業(yè)務(wù)系統(tǒng)中,讀寫邏輯往往相互交織、互相制約,這讓優(yōu)化工作變得更具挑戰(zhàn)。遇到這樣的情況,不妨嘗試一種更為高級(jí)的拆分模式——CQRS。你或許已經(jīng)聽說過 CQRS,卻因?yàn)樗此茝?fù)雜而猶豫不決。不過,今天的課程會(huì)通過一些實(shí)例來展示 CQRS 與傳統(tǒng)單體服務(wù)架構(gòu)的不同之處。學(xué)完后,你會(huì)深入理解 CQRS 的設(shè)計(jì)思路,找到避免數(shù)據(jù)服務(wù)接口陷阱的方法,并理解一些“反直覺”的設(shè)計(jì)選擇,比如在微服務(wù)架構(gòu)中為何會(huì)把 5 個(gè)項(xiàng)目拆分成 200 個(gè)。

傳統(tǒng)單體架構(gòu)缺點(diǎn)

我們先看看熟悉的傳統(tǒng)單體架構(gòu)設(shè)計(jì)思路,重點(diǎn)關(guān)注一下它有什么缺點(diǎn),這有助于你理解為什么會(huì)出現(xiàn) CQRS 這種模式。這里我們繼續(xù)沿用前面課程里用戶中心的案例,請(qǐng)看下圖。圖里面展示了單體服務(wù)狀態(tài)下的用戶中心,這時(shí)候高頻和低頻的讀寫服務(wù)放在了一起。

圖片圖片

在我們的印象中,用戶中心讀并發(fā)流量大。但實(shí)際上用戶中心里,不是所有功能都是讀多寫少類型的,你可以參考后面表格列出的例子。

圖片圖片

可以看到,除了讀多寫少的服務(wù)功能外,還有很多其它類型的功能(事實(shí)上這取決于調(diào)用方的場(chǎng)景)。

圖片圖片

這張圖展示了傳統(tǒng)單體架構(gòu)的優(yōu)化方式,我們可以清晰地看到在讀寫操作優(yōu)化上的差異。首先,關(guān)于高并發(fā)場(chǎng)景下的寫優(yōu)化,通常需要使用隊(duì)列作為緩沖,以保證數(shù)據(jù)一致性。對(duì)于一些特殊的業(yè)務(wù)場(chǎng)景,為了確保分布式事務(wù)的一致性,寫操作的服務(wù)器數(shù)量不能過多,因?yàn)閰⑴c事務(wù)協(xié)調(diào)的機(jī)器越多,響應(yīng)速度就越慢。至于讀優(yōu)化,主要是通過增加服務(wù)器的部署來分擔(dān)請(qǐng)求壓力,常見做法是對(duì)高頻訪問的數(shù)據(jù)進(jìn)行多副本緩存。由于讀操作占用大量?jī)?nèi)存,并且需要更多的數(shù)據(jù)層連接,且請(qǐng)求壓力較大,因此我們的部署還必須支持服務(wù)和數(shù)據(jù)層的動(dòng)態(tài)擴(kuò)展。

通過這些分析,我們發(fā)現(xiàn),在單體架構(gòu)中,同時(shí)優(yōu)化讀寫操作時(shí),很難在成本和性能之間找到理想的平衡。此外,單體架構(gòu)的數(shù)據(jù)庫(kù)層也存在一些問題。正如圖中所示,讀寫操作都依賴同一個(gè)數(shù)據(jù)庫(kù),這意味著數(shù)據(jù)層的性能上限也受到集群性能的限制。而且,由于數(shù)據(jù)庫(kù)和代碼層緊密耦合,一旦業(yè)務(wù)流量增長(zhǎng),我們就需要擴(kuò)容或更換數(shù)據(jù)層,而這個(gè)過程會(huì)變得非常復(fù)雜。

圖片圖片

既然單體服務(wù)讀寫混合部署這么復(fù)雜,有沒有更簡(jiǎn)單的解決方案呢?這時(shí) CQRS 就能派上用場(chǎng)了。

CQRS 的讀寫拆分策略

CQRS,全稱是命令查詢責(zé)任分離(Command Query Responsibility Segregation),它是一種將應(yīng)用程序中的命令(Commands)和查詢(Queries)職責(zé)分開的方式。如果你對(duì) CQRS 的詳細(xì)理論感興趣,可以課后自行深入學(xué)習(xí)。今天我們主要聚焦于讀寫拆分的部分。CQRS 最吸引我的地方就是它將讀和寫拆分成不同的項(xiàng)目,這種方式可以說非常符合“微服務(wù)”的理念。接下來,我們繼續(xù)通過用戶中心的案例來對(duì)比和分析。

圖片圖片

在示意圖中可以看到,我們將用戶中心的業(yè)務(wù)根據(jù)高并發(fā)寫入和高并發(fā)讀取兩個(gè)類別,分拆成了獨(dú)立的項(xiàng)目部署。這種拆分讓讀寫優(yōu)化更加靈活,投入成本也更為精準(zhǔn)。拆分后,我們可以根據(jù)流量調(diào)整服務(wù)器和基礎(chǔ)服務(wù)的規(guī)模,顯著降低運(yùn)維成本、減少服務(wù)壓力。此外,讀寫拆分有助于提升數(shù)據(jù)庫(kù)性能,擴(kuò)大我們?cè)跀?shù)據(jù)層的選擇,比如可以使用 ElasticSearch、ClickHouse、NoSQL 等特殊的數(shù)據(jù)服務(wù)。如果細(xì)心觀察,你會(huì)發(fā)現(xiàn)許多流行的分布式數(shù)據(jù)服務(wù)中也存在類似的讀寫分離實(shí)現(xiàn)。

總結(jié)來說,CQRS 允許我們將讀寫操作分別優(yōu)化和部署,最大化利用讀寫優(yōu)化的混合優(yōu)勢(shì),同時(shí)為系統(tǒng)提供靈活的擴(kuò)展能力。在此基礎(chǔ)上,我們還可以將常見的讀寫操作封裝成標(biāo)準(zhǔn)模塊,提升優(yōu)化效率,讓業(yè)務(wù)在復(fù)用這些模塊時(shí)能直接具備動(dòng)態(tài)擴(kuò)展能力。

不過,讀寫分離也有一定代價(jià),因此除非在核心業(yè)務(wù)中,一般不建議大規(guī)模應(yīng)用。這些優(yōu)化技巧對(duì)運(yùn)維有較高要求,通常需要運(yùn)維來配合修改配置和服務(wù)調(diào)整。比如在數(shù)據(jù)同步方面,我們一般使用隊(duì)列(如 binlog 數(shù)據(jù)同步隊(duì)列或業(yè)務(wù)變更廣播)來實(shí)現(xiàn)。這類同步通常不需編寫額外代碼,而是依賴基礎(chǔ)服務(wù),但如果沒有自動(dòng)化工具,每次擴(kuò)展都需要手動(dòng)調(diào)整配置,既繁瑣又易出錯(cuò)。

此外,一些業(yè)務(wù)場(chǎng)景可能需要讀強(qiáng)一致性,拆分后的架構(gòu)可能難以滿足。這里建議引入一個(gè)類似 Raft 的讀強(qiáng)一致性 Proxy,來確保業(yè)務(wù)輕松獲得最新數(shù)據(jù)。

數(shù)據(jù)服務(wù)接口導(dǎo)致的多層依賴

高并發(fā)的業(yè)務(wù)常常很復(fù)雜,而 CQRS 比較適合拆分和優(yōu)化數(shù)據(jù)接口,但對(duì)于復(fù)雜的業(yè)務(wù)接口我們還要做更多處理。想要理解這一點(diǎn),先要審視一下我們的編程習(xí)慣。很多人認(rèn)為,把業(yè)務(wù)接口寫成數(shù)據(jù)接口這個(gè)方式是衡量服務(wù)是否靈活的標(biāo)準(zhǔn),甚至催生出 RESTful 這種 API 設(shè)計(jì)思路,我們看一個(gè)例子。

圖片圖片

觀察上面的網(wǎng)址,可以發(fā)現(xiàn) RESTful 是圍繞數(shù)據(jù)實(shí)體來設(shè)計(jì)的數(shù)據(jù)服務(wù)。這么做雖然方便快速迭代,但很容易讓上層業(yè)務(wù)依賴底層的數(shù)據(jù)結(jié)構(gòu),而且接口的隔離性很差,修改的時(shí)候影響范圍很大。

不僅如此,圍繞數(shù)據(jù)實(shí)體去設(shè)計(jì)接口還會(huì)帶來更多的問題。下圖是一個(gè)常見的在線商城服務(wù),可以看到每個(gè)具體業(yè)務(wù)應(yīng)用(藍(lán)色方塊)都是通過拼合多個(gè) Service 服務(wù)實(shí)現(xiàn)的。

圖片圖片

這種結(jié)構(gòu)日常運(yùn)轉(zhuǎn)沒什么問題,但一旦流量增大,對(duì) QPS 要求更高的時(shí)候,這個(gè)結(jié)構(gòu)就會(huì)有大量的網(wǎng)絡(luò)損耗,接口的請(qǐng)求耗時(shí)鏈路分析如下圖。

圖片圖片

我們目前接口的總返回時(shí)間是 420ms,而這個(gè)接口依賴了兩個(gè) Service 服務(wù),而這兩個(gè) Service 又各自依賴了其他服務(wù)。顯然,這種依賴關(guān)系消耗了大量的時(shí)間。在這種情況下,使用緩存來減少底層服務(wù)接口的調(diào)用次數(shù)可以提高接口性能,但這種方法也有局限性。它主要適用于優(yōu)化那些不需要強(qiáng)一致性的讀取服務(wù),而且可能會(huì)導(dǎo)致緩存不一致等維護(hù)問題。

再想想我們?cè)趯戫?xiàng)目時(shí),為何通常會(huì)自覺地為每個(gè)功能多封裝一層 Service,并且將 Model 層設(shè)計(jì)成多層目錄?其實(shí),原因很簡(jiǎn)單,因?yàn)槎鄬右蕾嚨那闆r非常常見,而實(shí)體之間的關(guān)系又往往比較復(fù)雜,一層 Model 很難涵蓋所有的邏輯和關(guān)系。所以,我們通常會(huì)不自覺地通過這種方式來進(jìn)行優(yōu)化,確保代碼結(jié)構(gòu)更加清晰、靈活,也更易于維護(hù)。

圖片圖片

如果是大型系統(tǒng),業(yè)務(wù)實(shí)體關(guān)系更多、更復(fù)雜,那么反映在代碼上就會(huì)有更多的層級(jí)。當(dāng)我們的系統(tǒng)復(fù)雜到一定程度,就會(huì)出現(xiàn)多層項(xiàng)目依賴問題,呈現(xiàn)出下圖所示的“搭樂高”狀態(tài)。

圖片圖片

可以說,這種用數(shù)據(jù)實(shí)體思路設(shè)計(jì)接口的方法確實(shí)存在一些缺點(diǎn),也暴露了貧血模型的問題。為了避免這種“搭積木”式的依賴關(guān)系,在項(xiàng)目初期,我們通常盡量將依賴邏輯直接在 Model 層實(shí)現(xiàn)。不過,如何在 Model 層內(nèi)部進(jìn)行分層,功能如何拆分,并沒有統(tǒng)一的參考標(biāo)準(zhǔn),因此團(tuán)隊(duì)之間的實(shí)現(xiàn)往往各不相同,很難形成一致規(guī)范,這也導(dǎo)致了后續(xù)的優(yōu)化和維護(hù)成本居高不下。

為了解決多層依賴的問題,我們可以考慮充血模型作為一種替代方案。接下來,我們結(jié)合接口實(shí)現(xiàn)的實(shí)例來直觀對(duì)比一下這種方法,從而幫助你更好地理解充血模型的優(yōu)點(diǎn)和應(yīng)用場(chǎng)景。

圖片圖片

可以看到,和貧血模型直接寫增刪改查不同,充血模型通過拼合多個(gè)實(shí)體的功能直接提供業(yè)務(wù)服務(wù)。同時(shí),為了更好地拆分復(fù)雜的依賴實(shí)體關(guān)系,充血模型按業(yè)務(wù)領(lǐng)域劃分了 Model 層,每個(gè)領(lǐng)域會(huì)包含更多的子領(lǐng)域,這會(huì)導(dǎo)致模型內(nèi)實(shí)現(xiàn)有更多的分層。但是因?yàn)槟P蛢?nèi)的實(shí)體實(shí)現(xiàn)都是業(yè)務(wù)操作,這樣項(xiàng)目之間接口只有業(yè)務(wù)依賴關(guān)系,而不是數(shù)據(jù)依賴。業(yè)務(wù)依賴關(guān)系的接口之間只有流程和事務(wù)的拼接,訪問頻率也不會(huì)那么高,可以很好地緩解系統(tǒng)壓力。

微服務(wù)的平層設(shè)計(jì)

不過,即使我們使用了充血模型,仍然會(huì)有一些公共數(shù)據(jù)存在多層依賴的情況。為此,微服務(wù)做出了新嘗試,直接去掉所有服務(wù)的層級(jí),設(shè)計(jì)成大平層的架構(gòu)。如果你曾經(jīng)翻過一些微服務(wù)資料,可能會(huì)疑惑微服務(wù)的設(shè)計(jì)為什么和我們的習(xí)慣差異這么大?比如幾個(gè)小接口就能封裝成一個(gè)項(xiàng)目,再比如服務(wù)之間的依賴不是樹形而是星狀。

圖片圖片

圖片圖片

這是因?yàn)?,在微服?wù)里一個(gè) Service 就是一個(gè)獨(dú)立部署的項(xiàng)目,同層 Service 服務(wù)之間沒有上下層級(jí)關(guān)系,可以相互調(diào)用(雖然不推薦)。這和我們以往的橫切習(xí)慣有很大不同,屬于縱切拆分。橫切是按照依賴關(guān)系把服務(wù)劃分為內(nèi)網(wǎng)服務(wù)、外網(wǎng)服務(wù),縱切可以理解成按業(yè)務(wù)服務(wù)切分,具體效果如圖。

圖片圖片

結(jié)合之前的知識(shí),不難發(fā)現(xiàn),橫切服務(wù)適用于項(xiàng)目的初期驗(yàn)證階段,具有較高的復(fù)用性,但同時(shí)也帶來了復(fù)雜的依賴關(guān)系,性能優(yōu)化較為困難。而縱切服務(wù)則更適合復(fù)雜業(yè)務(wù)場(chǎng)景,沒有多層依賴的負(fù)擔(dān),性能更優(yōu)。但由于依賴關(guān)系需在充血模型內(nèi)部實(shí)現(xiàn),因此還要解決數(shù)據(jù)共享的問題,數(shù)據(jù)同步的難度較高,且業(yè)務(wù)邏輯的梳理也較為復(fù)雜。

盡管縱切結(jié)構(gòu)并不常見,但近年來微服務(wù)和領(lǐng)域驅(qū)動(dòng)設(shè)計(jì)(DDD)都選擇了這種方法。

責(zé)任編輯:武曉燕 來源: 二進(jìn)制跳動(dòng)
相關(guān)推薦

2023-05-17 10:53:43

AICIO

2021-04-22 11:22:12

云計(jì)算數(shù)據(jù)遷移混合云

2021-03-01 15:52:14

開源開源軟件陷阱

2017-10-20 10:19:49

Kotlin語言陷阱

2018-06-11 15:26:47

云計(jì)算企業(yè)云陷阱

2019-02-11 10:00:23

云網(wǎng)絡(luò)云平臺(tái)微服務(wù)

2013-01-06 10:15:02

大數(shù)據(jù)分析數(shù)據(jù)分析師大數(shù)據(jù)

2016-09-08 23:47:17

大數(shù)據(jù)大數(shù)據(jù)服務(wù)

2024-11-01 10:37:31

2016-10-10 23:01:48

安全認(rèn)證云供應(yīng)商安全評(píng)估

2021-02-28 13:19:42

大數(shù)據(jù)IT數(shù)據(jù)管理

2022-09-19 09:19:24

云存儲(chǔ)TCO云服務(wù)

2021-05-21 14:19:45

數(shù)據(jù)服務(wù)API技術(shù)

2022-12-05 11:57:10

2022-08-26 05:12:29

IT運(yùn)營(yíng)工具蔓延

2014-07-11 10:23:54

2012-04-28 10:00:42

2019-10-29 14:15:25

云存檔數(shù)據(jù)服務(wù)技術(shù)

2015-06-02 11:10:20

2011-06-07 15:34:15

點(diǎn)贊
收藏

51CTO技術(shù)棧公眾號(hào)