官术网_书友最值得收藏!

1.2 一個(gè)簡(jiǎn)單的例子:分布式架構(gòu)的組成

1.1 節(jié)介紹了軟件架構(gòu)的演進(jìn)歷史,說(shuō)明了軟件架構(gòu)是沿著高性能、高可用、可擴(kuò)展、可伸縮、夠安全的方向發(fā)展的,其中最重要的是高性能和高可用。在分布式時(shí)代,服務(wù)的分布式部署還帶來(lái)了一系列其他問(wèn)題,諸如服務(wù)的拆分、調(diào)用、協(xié)同以及分布式計(jì)算和存儲(chǔ)。這里通過(guò)一個(gè)簡(jiǎn)單的分布式示例帶大家了解分布式架構(gòu)的特征和問(wèn)題,然后針對(duì)發(fā)現(xiàn)的問(wèn)題進(jìn)行拆解,找到解決方案。首先介紹一下例子的業(yè)務(wù)流程,如圖 1-11 所示,該圖描述了從瀏覽商品,到下單、付款以及扣減庫(kù)存,最后通知用戶的整個(gè)過(guò)程。由圖 1-12 可以看出,系統(tǒng)架構(gòu)會(huì)根據(jù)以上業(yè)務(wù)提供對(duì)應(yīng)服務(wù):在用戶通過(guò)商品服務(wù)瀏覽商品,通過(guò)訂單服務(wù)下單,通過(guò)支付服務(wù)付款以后,會(huì)通知庫(kù)存服務(wù)扣減相應(yīng)的庫(kù)存,訂單完成以后,通知服務(wù)會(huì)向用戶發(fā)送訂單完成的消息。這是對(duì)業(yè)務(wù)背景的描述,接下來(lái)會(huì)以架構(gòu)分層為主線,介紹技術(shù)實(shí)現(xiàn)以及在分布式架構(gòu)中會(huì)遇到的問(wèn)題。

圖 1-11 一個(gè)簡(jiǎn)單例子的業(yè)務(wù)流程

1.2.1 架構(gòu)概述與分層

為了完成上面的訂單業(yè)務(wù)流程,將分布式系統(tǒng)分為了四層,如圖 1-12 所示,下面從上到下依次介紹一下這四層。

?客戶端:這是用戶與系統(tǒng)之間的接口,用戶在這里可以瀏覽商品信息,并且對(duì)商品下單。為了提升用戶體驗(yàn),會(huì)利用 HTTP 緩存手段將部分靜態(tài)資源緩存下來(lái),同時(shí)也可以將這部分靜態(tài)資源緩存到 CDN 中,因?yàn)?CDN 服務(wù)器通常會(huì)讓用戶從比較近的網(wǎng)絡(luò)節(jié)點(diǎn)獲取靜態(tài)數(shù)據(jù)。

?負(fù)載均衡器(以下稱接入層):分布式應(yīng)用會(huì)對(duì)業(yè)務(wù)進(jìn)行拆分,并將拆分后的業(yè)務(wù)分別部署到不同系統(tǒng)中去,又或者使用服務(wù)器集群分擔(dān)請(qǐng)求壓力。假設(shè)我們的案例使用的是服務(wù)器集群,這里分別為華南和華中地區(qū)的用戶設(shè)置兩個(gè)應(yīng)用服務(wù)器集群,那么負(fù)載均衡器可以通過(guò)用戶 IP 將用戶的請(qǐng)求路由到不同的服務(wù)器集群。另外,在負(fù)載均衡這一層,還可以進(jìn)行流量控制和身份驗(yàn)證等操作。

?應(yīng)用服務(wù)器(以下稱應(yīng)用層):這一層用于部署主要的應(yīng)用服務(wù),例如商品服務(wù)、訂單服務(wù)、支付服務(wù)、庫(kù)存服務(wù)和通知服務(wù)。這些服務(wù)既可以部署到同一臺(tái)服務(wù)器上,也可以部署到不同服務(wù)器上。當(dāng)負(fù)載均衡器將請(qǐng)求路由到應(yīng)用服務(wù)器或者內(nèi)網(wǎng)以后,會(huì)去找對(duì)應(yīng)的服務(wù),例如商品服務(wù)。當(dāng)請(qǐng)求從外網(wǎng)進(jìn)入內(nèi)網(wǎng)后,需要通過(guò) API 網(wǎng)關(guān)進(jìn)行再次路由,特別是微服務(wù)架構(gòu)中服務(wù)拆分得比較細(xì),就更需要 API 網(wǎng)關(guān)了。API 網(wǎng)關(guān)還可以起到內(nèi)網(wǎng)的負(fù)載均衡、協(xié)議轉(zhuǎn)換、鏈?zhǔn)教幚?、異步?qǐng)求等作用。由于應(yīng)用服務(wù)(一個(gè)或者多個(gè))部署在多個(gè)服務(wù)器上,這些服務(wù)器要想互相調(diào)用,就要考慮各服務(wù)間的通信、協(xié)調(diào)等問(wèn)題,因此加入了服務(wù)注冊(cè)中心、消息隊(duì)列消息中心等組件。同時(shí)由于商品服務(wù)會(huì)經(jīng)常被用戶調(diào)用,因此加入了緩存機(jī)制。

?數(shù)據(jù)服務(wù)器(以下稱存儲(chǔ)層):由于商品信息比較多,所以對(duì)其進(jìn)行分片操作,分別存放到商品表 1 和商品表 2 這兩張表中來(lái)保證數(shù)據(jù)庫(kù)的可用性。這里加入了主、備數(shù)據(jù)庫(kù)的設(shè)計(jì),這兩個(gè)數(shù)據(jù)庫(kù)服務(wù)器會(huì)進(jìn)行同步,當(dāng)主數(shù)據(jù)庫(kù)服務(wù)器掛掉的時(shí)候,備數(shù)據(jù)庫(kù)服務(wù)器就會(huì)接管它的一切。

圖 1-12 訂單業(yè)務(wù)架構(gòu)圖

1.2.2 客戶端與 CDN

對(duì)于一個(gè)電商網(wǎng)站而言,商品信息是用戶訪問(wèn)最多的內(nèi)容。電商網(wǎng)站擁有海量商品,用戶每次請(qǐng)求后,它都會(huì)返回諸如商品的基本參數(shù)、圖片、價(jià)格和款式等信息。而商品一經(jīng)發(fā)布,某些信息被修改的頻率并不是很高,例如商品描述和圖片。如果用戶每次訪問(wèn)時(shí)都需要請(qǐng)求應(yīng)用服務(wù)器,然后再訪問(wèn)數(shù)據(jù)庫(kù),那么效率是非常低的。在大多數(shù)情況下,用戶訪問(wèn)時(shí)使用的協(xié)議是 HTTP,或者說(shuō)用戶使用瀏覽器訪問(wèn)電商網(wǎng)站時(shí)會(huì)發(fā)起 HTTP 請(qǐng)求,因此我們把每次的 HTTP 請(qǐng)求都緩存下來(lái),就可以減小應(yīng)用服務(wù)器的壓力。加入緩存后的用戶請(qǐng)求過(guò)程如圖 1-13 所示,在用戶第一次發(fā)出請(qǐng)求的時(shí)候,客戶端緩存會(huì)判斷是否存在緩存,如果不存在,則向應(yīng)用服務(wù)器發(fā)出請(qǐng)求,此時(shí)應(yīng)用服務(wù)器會(huì)提供數(shù)據(jù)給客戶端,客戶端接收數(shù)據(jù)之后將其放入緩存中。當(dāng)用戶第二次發(fā)出同樣的請(qǐng)求時(shí),客戶端緩存依舊會(huì)判斷是否存在緩存。由于上次請(qǐng)求時(shí)已經(jīng)緩存了這部分?jǐn)?shù)據(jù),因此由客戶端緩存提供數(shù)據(jù),客戶端接收數(shù)據(jù)以后返回給用戶。備注:圖中實(shí)線部分表示沒(méi)有緩存的流程,虛線部分表示存在緩存的流程。

圖 1-13 客戶端通過(guò)發(fā)送 HTTP 請(qǐng)求緩存數(shù)據(jù)

一般信息的傳遞通過(guò) HTTP 請(qǐng)求頭來(lái)完成。目前比較常見(jiàn)的緩存方式有兩種,分別是強(qiáng)制緩存和對(duì)比緩存。細(xì)節(jié)在這里暫不展開(kāi),第 8 章會(huì)介紹不同應(yīng)用場(chǎng)景中緩存的具體用法。HTTP 緩存主要是對(duì)靜態(tài)數(shù)據(jù)進(jìn)行緩存,把從服務(wù)器拿到的數(shù)據(jù)緩存到客戶端/瀏覽器中。如果在客戶端和服務(wù)器之間再加上一層 CDN,就可以讓 CDN 為應(yīng)用服務(wù)器提供緩存,有了 CDN 緩存,就不用再請(qǐng)求應(yīng)用服務(wù)器了。因此可以將商品的基本描述和圖片等信息放到 CDN 中保存,還可以將一些前端通用控制類(lèi)的 JavaScript 腳本也放在里面。需要注意的是,在更新商品信息(例如商品圖片)時(shí),需要同時(shí)更新 CDN 中的文件和 JavaScript 文件。

1.2.3 接入層

用戶瀏覽商品的請(qǐng)求一般是一個(gè) URL,這個(gè) URL 被 DNS 服務(wù)器解析成服務(wù)器的 IP 地址,然后用戶的請(qǐng)求通過(guò)這個(gè) IP 地址訪問(wèn)應(yīng)用服務(wù)器上的服務(wù),這是早期的做法。如果讀了 1.1 節(jié)的架構(gòu)演進(jìn),應(yīng)該就會(huì)知道,這樣讓?xiě)?yīng)用服務(wù)器暴露在互聯(lián)網(wǎng)上是非常危險(xiǎn)的,為了解決這個(gè)問(wèn)題,在應(yīng)用服務(wù)器和客戶端之間加入了反向代理服務(wù)器,它負(fù)責(zé)對(duì)外暴露服務(wù)的入口地址,保護(hù)內(nèi)網(wǎng)的服務(wù)。從我們舉的例子出發(fā),用戶瀏覽商品的請(qǐng)求有可能來(lái)自不同的網(wǎng)絡(luò),這些網(wǎng)絡(luò)的訪問(wèn)速度不盡相同,比如有兩個(gè)用戶分別來(lái)自華中地區(qū)和華南地區(qū),那么就可以針對(duì)這兩個(gè)地區(qū)分別設(shè)置兩個(gè)反向代理服務(wù)器提供服務(wù)。

上面說(shuō)了反向代理服務(wù)器可以服務(wù)于不同網(wǎng)絡(luò)的用戶,同樣,來(lái)自不同網(wǎng)絡(luò)的用戶也可以被負(fù)載均衡器路由到不同的應(yīng)用服務(wù)器集群。瀏覽商品的請(qǐng)求經(jīng)過(guò)負(fù)載均衡器的時(shí)候,是不知道會(huì)被路由到哪臺(tái)應(yīng)用服務(wù)器的。由于分布式部署,位于華中地區(qū)的集群服務(wù)器和華南地區(qū)的集群服務(wù)器都能提供商品信息的服務(wù)。此時(shí)負(fù)載均衡器可以協(xié)助路由工作,但其功能不僅僅局限于路由,它的主要功能是將大量的用戶請(qǐng)求均衡地負(fù)載到后端的應(yīng)用服務(wù)器上。特別是在有集群的情況下,負(fù)載均衡器有專(zhuān)門(mén)的算法可供選擇。如果用戶請(qǐng)求量再次加大,大到系統(tǒng)無(wú)法承受的地步,負(fù)載均衡器還可以起到限流作用,將那些系統(tǒng)無(wú)法處理的請(qǐng)求流量阻斷在系統(tǒng)之外,如圖 1-14 所示。

圖 1-14 負(fù)載均衡

客戶端請(qǐng)求服務(wù)器的過(guò)程可以分為如下四個(gè)步驟:

(1) 客戶端向 DNS 服務(wù)器發(fā)出 URL 請(qǐng)求;

(2) DNS 服務(wù)器向客戶端返回應(yīng)用服務(wù)器入口的 IP 地址;

(3) 客戶端向服務(wù)器發(fā)送請(qǐng)求;

(4) 負(fù)載均衡器接收請(qǐng)求以后,根據(jù)負(fù)載均衡算法找到對(duì)應(yīng)的服務(wù)器,并將請(qǐng)求發(fā)送給此服務(wù)器。

1.2.4 應(yīng)用層

這一層中包含具體的業(yè)務(wù)應(yīng)用服務(wù),例如商品服務(wù)、訂單服務(wù)、支付服務(wù)、庫(kù)存服務(wù)和通知服務(wù)。這些服務(wù)之間有的可以獨(dú)立調(diào)用,例如商品服務(wù),有的存在相互依賴,例如調(diào)用訂單服務(wù)的時(shí)候會(huì)同時(shí)調(diào)用庫(kù)存服務(wù)。接下來(lái),我們從 API 網(wǎng)關(guān)、服務(wù)協(xié)同與通信、分布式互斥、分布式事務(wù)這 4 個(gè)方面來(lái)分析應(yīng)用層的情況。

?API 網(wǎng)關(guān)

經(jīng)過(guò)負(fù)載均衡之后的用戶請(qǐng)求一般會(huì)直接調(diào)用應(yīng)用服務(wù),但是隨著分布式的興起,特別是微服務(wù)的廣泛應(yīng)用,服務(wù)被切割得非常精細(xì),有可能分布在相同或者不同的服務(wù)器中,同一個(gè)服務(wù)也會(huì)被做水平擴(kuò)展,也就是將同一個(gè)服務(wù)復(fù)制為多個(gè),以面對(duì)高并發(fā)請(qǐng)求。比如商品服務(wù)被調(diào)用的次數(shù)是最多的,因此從高性能和可用性的角度來(lái)看,會(huì)做水平擴(kuò)展,此時(shí)用戶在請(qǐng)求商品服務(wù)的時(shí)候需要有個(gè)中介,以便將請(qǐng)求路由到對(duì)應(yīng)的服務(wù)。當(dāng)然,這個(gè)中介也會(huì)起到負(fù)載均衡和限流的作用。另外,由于存在很多服務(wù)之間的調(diào)用,并且這些服務(wù)存在技術(shù)異構(gòu)性,因此為了消除技術(shù)異構(gòu)性,可以在 API 網(wǎng)關(guān)中進(jìn)行協(xié)議轉(zhuǎn)換?,F(xiàn)在,我們把 API 網(wǎng)關(guān)要做的事情總結(jié)為如下幾條,并通過(guò)圖 1-15 來(lái)介紹 API 網(wǎng)關(guān)的功能。

?內(nèi)容為瀏覽商品的用戶請(qǐng)求要想接觸到商品服務(wù),需經(jīng)過(guò) API 網(wǎng)關(guān),由網(wǎng)關(guān)充當(dāng)路由為其找到對(duì)應(yīng)的服務(wù)。

?如果存在水平擴(kuò)展的商品服務(wù),API 網(wǎng)關(guān)需要起到負(fù)載均衡的作用。例如,用戶請(qǐng)求商品服務(wù)時(shí),同時(shí)存在著 2 個(gè)商品服務(wù),此時(shí) API 網(wǎng)關(guān)就需要幫助用戶決定讓哪個(gè)商品服務(wù)響應(yīng)其請(qǐng)求。

?如果用戶需要對(duì)商品進(jìn)行下單操作,則 API 網(wǎng)關(guān)要對(duì)用戶身份進(jìn)行鑒權(quán)。

?一個(gè)服務(wù)調(diào)用其他服務(wù)的時(shí)候,如果兩個(gè)服務(wù)使用的傳輸協(xié)議不一致,那么 API 網(wǎng)關(guān)需要對(duì)協(xié)議進(jìn)行轉(zhuǎn)換。例如,訂單服務(wù)需要調(diào)用庫(kù)存服務(wù)和支付服務(wù)完成業(yè)務(wù)需求,但訂單服務(wù)與其他兩個(gè)服務(wù)使用的是不同協(xié)議,此時(shí) API 應(yīng)負(fù)責(zé)做它們之間的協(xié)議轉(zhuǎn)換。

?一旦大量用戶同時(shí)請(qǐng)求瀏覽商品,其流量超出系統(tǒng)承受的范圍,API 網(wǎng)關(guān)就需要完成限流操作。

?對(duì)于瀏覽商品的操作,API 網(wǎng)關(guān)系統(tǒng)要記錄相應(yīng)的日志。

圖 1-15 API 網(wǎng)關(guān)的功能

如果把請(qǐng)求看成水流,API 網(wǎng)關(guān)就像一個(gè)控制水流的水閥。它控制水的流向、大小,調(diào)整不同蓄水池存儲(chǔ)水流的方式,記錄水流的信息。API 網(wǎng)關(guān)和負(fù)載均衡器在原理上是相同的,區(qū)別在于前者更多是在服務(wù)器內(nèi)部服務(wù)之間實(shí)現(xiàn),而后者是在互聯(lián)網(wǎng)與服務(wù)器之間實(shí)現(xiàn)。

 

?服務(wù)協(xié)同與通信

用戶在瀏覽完商品詳情以后,會(huì)通過(guò)訂單服務(wù)下單,然而訂單服務(wù)又需要調(diào)用支付服務(wù)。訂單服務(wù)和支付服務(wù)分別運(yùn)行在不同的進(jìn)程、容器甚至是服務(wù)器中,兩者如何發(fā)現(xiàn)對(duì)方并進(jìn)行通信呢?在沒(méi)有進(jìn)行服務(wù)切割和分布式部署的時(shí)候,一個(gè)模塊調(diào)用另外一個(gè)模塊需要在代碼中耦合,在代碼中描述調(diào)用條件,并且調(diào)用對(duì)應(yīng)的方法或者模塊。這些屬于進(jìn)程內(nèi)調(diào)用,但是隨著分布式和微服務(wù)的興起,服務(wù)或者應(yīng)用從原有的單進(jìn)程切分到多個(gè)進(jìn)程中,這些進(jìn)程又運(yùn)行在不同的容器或者服務(wù)器上。這時(shí)服務(wù)之間又該如何得知其他服務(wù)的存在以及進(jìn)行調(diào)用呢?

下面以訂單服務(wù)為例來(lái)介紹,具體如圖 1-16 所示。假設(shè)訂單服務(wù)需要調(diào)用支付服務(wù)為商品買(mǎi)單。訂單服務(wù)和支付服務(wù)事先會(huì)在服務(wù)注冊(cè)中心注冊(cè)自己。訂單服務(wù)在調(diào)用支付服務(wù)之前,會(huì)先去注冊(cè)中心獲取所有可用服務(wù)的調(diào)用列表,然后根據(jù)列表上的地址對(duì)支付服務(wù)進(jìn)行調(diào)用。此時(shí)訂單服務(wù)會(huì)對(duì)支付服務(wù)發(fā)起一個(gè) RPC 調(diào)用,把需要傳遞的訂單信息以序列化的方式打包,并經(jīng)過(guò)網(wǎng)絡(luò)協(xié)議傳遞給支付服務(wù),支付服務(wù)在接收到信息以后,通過(guò)反序列化工具解析傳遞的內(nèi)容,然后執(zhí)行接下來(lái)的付款操作。再跟著圖 1-16 將這個(gè)過(guò)程梳理一遍。

(1) 支付服務(wù)到服務(wù)注冊(cè)中心注冊(cè)自己。

(2) 訂單服務(wù)從注冊(cè)中心獲取可用服務(wù)的列表。

(3) 訂單服務(wù)在列表中找到支付服務(wù)的地址和訪問(wèn)方式,并調(diào)用支付服務(wù)。

圖 1-16 服務(wù)注冊(cè)、服務(wù)發(fā)現(xiàn)和服務(wù)調(diào)用

通常在支付完成以后,系統(tǒng)會(huì)通過(guò)短信或者 App 推送的方式通知用戶,此時(shí)就需要調(diào)用通知服務(wù)。由于通知服務(wù)屬于基礎(chǔ)功能服務(wù),與業(yè)務(wù)的關(guān)聯(lián)性不強(qiáng),會(huì)被其他業(yè)務(wù)系統(tǒng)調(diào)用,因此,將其單獨(dú)部署,甚至作為單獨(dú)的系統(tǒng)進(jìn)行維護(hù)。支付系統(tǒng)在支付成功以后,會(huì)將成功的消息發(fā)送到消息隊(duì)列,而通知服務(wù)會(huì)將該信息按照配置好的通知方式發(fā)送給用戶,如圖 1-17 所示。

圖 1-17 通知服務(wù)通過(guò)消息隊(duì)列通信

 

?分布式互斥

在上一部分中,訂單服務(wù)只調(diào)用了支付服務(wù),但是大家知道在下訂單的同時(shí)會(huì)對(duì)庫(kù)存進(jìn)行扣減,因此訂單服務(wù)也會(huì)調(diào)用庫(kù)存服務(wù)。庫(kù)存服務(wù)針對(duì)商品庫(kù)存進(jìn)行操作,如果有兩個(gè)用戶同時(shí)對(duì)同一商品下單,就會(huì)形成對(duì)同一商品庫(kù)存同時(shí)進(jìn)行扣減的情況,這當(dāng)然是不行的。我們將此處的商品庫(kù)存稱作臨界資源,扣減庫(kù)存的動(dòng)作稱作競(jìng)態(tài)。如果是在進(jìn)程內(nèi),可以理解為兩個(gè)線程(兩個(gè)用戶請(qǐng)求)在爭(zhēng)奪庫(kù)存資源,最簡(jiǎn)單的解決辦法就是在這個(gè)資源上加一把鎖,如圖 1-18 所示。當(dāng)線程 B 訪問(wèn)的時(shí)候,讓其持有這把鎖,這樣線程 A 就無(wú)法訪問(wèn),并且進(jìn)入等待隊(duì)列。當(dāng)線程 B 執(zhí)行完庫(kù)存扣減的操作以后,釋放鎖,由線程 A 持有鎖,然后進(jìn)行庫(kù)存扣減操作。

圖 1-18 多線程訪問(wèn)臨界資源

由于分布式服務(wù)是分散部署的,而且可以實(shí)現(xiàn)水平擴(kuò)展,因此問(wèn)題發(fā)生了變化,原來(lái)是一個(gè)進(jìn)程內(nèi)的多線程對(duì)臨界資源的競(jìng)態(tài),現(xiàn)在變成應(yīng)用系統(tǒng)中的多個(gè)服務(wù)(進(jìn)程)對(duì)臨界資源的競(jìng)態(tài)。接下來(lái)這種情況對(duì)庫(kù)存服務(wù)進(jìn)行了水平擴(kuò)展,將其從原來(lái)的一個(gè)擴(kuò)展成兩個(gè)。庫(kù)存服務(wù) A 和庫(kù)存服務(wù) B 可能會(huì)同時(shí)扣減庫(kù)存,這里通過(guò) ZooKeeper 的 DataNode 保證兩個(gè)進(jìn)程的訪問(wèn)順序,兩個(gè)庫(kù)存扣減進(jìn)程會(huì)在 ZooKeeper 上建立順序的 DataNode,節(jié)點(diǎn)的順序就是訪問(wèn)資源的順序,能夠避免兩個(gè)進(jìn)程同時(shí)訪問(wèn)庫(kù)存,起到了鎖的作用。具體的做法是在 ZooKeeper 中建立一個(gè) DataNode 節(jié)點(diǎn)起到鎖(locker)的作用,通過(guò)在此節(jié)點(diǎn)下面建立子 DataNode 來(lái)保證訪問(wèn)資源的先后順序,即便是兩個(gè)服務(wù)同時(shí)申請(qǐng)新建子 DataNode 節(jié)點(diǎn),也會(huì)按照先后順序建立。圖 1-19 中的具體步驟如下。

(1) 當(dāng)庫(kù)存服務(wù) A 訪問(wèn)庫(kù)存的時(shí)候,需要先申請(qǐng)鎖,于是在 ZooKeeper 的 Locker 節(jié)點(diǎn)下面新建一個(gè) DataNode1 節(jié)點(diǎn),表明它可以扣減庫(kù)存。

(2) 庫(kù)存服務(wù) B 在庫(kù)存服務(wù) A 后面申請(qǐng)庫(kù)存的訪問(wèn)權(quán)限,由于其申請(qǐng)鎖操作排在庫(kù)存服務(wù) A 后面,因此其按照次序建立的節(jié)點(diǎn)會(huì)排在 DataNode1 下面,名字為 DataNode2。

(3) 庫(kù)存服務(wù) A 在申請(qǐng)鎖成功以后訪問(wèn)庫(kù)存資源,并進(jìn)行庫(kù)存扣減。在此期間庫(kù)存服務(wù) B 一直處于等待狀態(tài),直到庫(kù)存服務(wù) A 完成扣減操作以后,ZooKeeper 中 Locker 下面的 DataNode1 節(jié)點(diǎn)被刪除,庫(kù)存資源被釋放。

(4) DataNode1 被刪除以后,DataNode2 成為序號(hào)最靠前的節(jié)點(diǎn),庫(kù)存服務(wù) B 因此得到了對(duì)庫(kù)存的訪問(wèn)權(quán)限,并且可以完成庫(kù)存扣減操作。

圖 1-19 多個(gè)庫(kù)存服務(wù)訪問(wèn)庫(kù)存資源

 

?分布式事務(wù)

下訂單和扣減庫(kù)存兩個(gè)操作通常是同時(shí)完成的,如果庫(kù)存為 0 則表示沒(méi)有庫(kù)存可以扣減,那么下訂單的操作也將無(wú)法執(zhí)行。如果把訂單服務(wù)中的下訂單操作和庫(kù)存服務(wù)的扣減庫(kù)存操作當(dāng)作一個(gè)事務(wù),那么由于這兩個(gè)操作跨越了不同的應(yīng)用(服務(wù)器),因此可以將這個(gè)事務(wù)視為分布式事務(wù)。類(lèi)似的情況在分布式架構(gòu)中比較常見(jiàn)。由于應(yīng)用服務(wù)的分散性,操作也會(huì)分散,如果這些分散的操作共同完成一個(gè)事務(wù),就需要進(jìn)行特殊處理。一般做法是在訂單服務(wù)和庫(kù)存服務(wù)上建立一個(gè)事務(wù)協(xié)調(diào)器,用來(lái)協(xié)調(diào)兩個(gè)服務(wù)的操作,保證兩個(gè)操作能在一個(gè)事務(wù)中完成。事務(wù)協(xié)調(diào)器會(huì)分兩個(gè)階段來(lái)處理事務(wù)。

事務(wù)提交第一階段如圖 1-20 所示。

(1) 事務(wù)協(xié)調(diào)器分別向訂單服務(wù)和庫(kù)存服務(wù)發(fā)送“CanCommit?”消息,確定這兩個(gè)服務(wù)是否準(zhǔn)備好了。準(zhǔn)備好的意思是訂單服務(wù)已準(zhǔn)備好添加訂單記錄以及庫(kù)存服務(wù)已準(zhǔn)備好扣減庫(kù)存。

(2) 訂單服務(wù)接收到消息以后,檢查訂單信息,并準(zhǔn)備增加商品訂單記錄,同時(shí)將消息 Yes 回復(fù)給事務(wù)協(xié)調(diào)器。如果庫(kù)存服務(wù)在準(zhǔn)備過(guò)程中發(fā)現(xiàn)庫(kù)存不足,就向事務(wù)協(xié)調(diào)器回復(fù) No,意思是終止操作。

圖 1-20 事務(wù)提交第一階段

事務(wù)提交第二階段如圖 1-21 所示。

(1) 事務(wù)協(xié)調(diào)器接收到庫(kù)存服務(wù)操作不成功的消息后,向訂單服務(wù)和庫(kù)存服務(wù)發(fā)送 DoAbort 消息,意思是放棄操作。訂單服務(wù)在接收到此消息后,通過(guò)日志回滾增加商品訂單的操作并釋放相關(guān)資源。

(2) 這兩個(gè)服務(wù)在完成相應(yīng)操作后,向事務(wù)協(xié)調(diào)器發(fā)送 Committed 消息,表示完成撤銷(xiāo)操作。

說(shuō)明

倘若兩個(gè)服務(wù)都準(zhǔn)備好了,事務(wù)協(xié)調(diào)器就會(huì)發(fā)送執(zhí)行的命令,兩個(gè)服務(wù)會(huì)分別執(zhí)行對(duì)應(yīng)的操作,共同完成事務(wù)。

圖 1-21 事務(wù)提交第二階段

看到這里,有些朋友會(huì)說(shuō):“這不就是 2PC 嗎?”是的,這是一種簡(jiǎn)單的處理分布式事務(wù)的方式,這里我們只做一個(gè)引子。在 4.3 節(jié)中,還會(huì)介紹 ACID、CAP、TCC 等處理分布式事務(wù)的方法。

1.2.5 存儲(chǔ)層

存儲(chǔ)層用來(lái)存放業(yè)務(wù)數(shù)據(jù)。和單體應(yīng)用不同的是,分布式存儲(chǔ)會(huì)將數(shù)據(jù)分別放置在不同的數(shù)據(jù)表、數(shù)據(jù)庫(kù)和服務(wù)器上面。如果說(shuō)單體應(yīng)用是通過(guò)直接訪問(wèn)數(shù)據(jù)庫(kù),針對(duì)某張數(shù)據(jù)表的方式來(lái)獲取數(shù)據(jù),那么分布式數(shù)據(jù)庫(kù)獲取數(shù)據(jù)的方式就要復(fù)雜一些。這里先看一個(gè)例子,理論和實(shí)踐方法會(huì)在第 6 章中詳細(xì)說(shuō)明。

?分布式存儲(chǔ)

電商系統(tǒng)中商品信息的數(shù)據(jù)量比較大,為了提高訪問(wèn)效率,通常會(huì)將數(shù)據(jù)分片存放,被拆分以后的商品表會(huì)分布到不同的數(shù)據(jù)庫(kù)或者服務(wù)器中。例如,商品表中有 1000 條數(shù)據(jù),我們將它分成兩張表來(lái)存儲(chǔ),將商品 ID 為 1~500 的記錄分配到商品表 1,ID 為 501~1000 的記錄分配到商品表 2。假設(shè) ID 是順序增長(zhǎng)的,查詢商品時(shí)會(huì)傳入其 ID。如果按上述那樣劃分?jǐn)?shù)據(jù),在查詢商品信息的時(shí)候就需要從兩張表中獲取數(shù)據(jù),這兩張表可能存儲(chǔ)在相同數(shù)據(jù)庫(kù)中,也可能存儲(chǔ)在兩個(gè)數(shù)據(jù)庫(kù)中。如果存儲(chǔ)在兩個(gè)數(shù)據(jù)庫(kù)中,那這兩個(gè)數(shù)據(jù)庫(kù)既可能存在于同一臺(tái)服務(wù)器上,也可能存在于兩臺(tái)不同的服務(wù)器上,因此,還需要在代碼中建立兩個(gè)數(shù)據(jù)庫(kù)的連接,分別做兩次查詢,這樣的效率會(huì)很低。此時(shí)可以加入 MyCat 數(shù)據(jù)庫(kù)中間件,它的作用是解決由數(shù)據(jù)分片帶來(lái)的數(shù)據(jù)路由、SQL 解析等問(wèn)題。如圖 1-22 所示,當(dāng)接收到商品查詢請(qǐng)求之后,MyCat 對(duì) SQL 進(jìn)行解析,獲得需要獲取的商品表的信息。假設(shè) SQL 中傳入的商品 ID 是 100,100 在 1~500 的范圍內(nèi),通過(guò)數(shù)據(jù)分片的路由規(guī)則可以知道,返回的商品信息需要從“數(shù)據(jù)庫(kù)服務(wù)器 1”中的“商品表 1”中獲取。

圖 1-22 MyCat 實(shí)現(xiàn)商品表分片

以此類(lèi)推,也可以定義其他的數(shù)據(jù)庫(kù)分片規(guī)則,例如根據(jù)區(qū)域、品類(lèi)等。

 

?讀寫(xiě)分離與主從同步

針對(duì)商品表的數(shù)據(jù)量比較大這一點(diǎn),對(duì)其進(jìn)行了分片操作。同樣商品表被讀取的機(jī)會(huì)也比較大,更新的機(jī)會(huì)相對(duì)較小,對(duì)此可以設(shè)置讀寫(xiě)分離。還是分析上面商品表的例子,由于這里主要講讀寫(xiě)分離和主從同步,因此只針對(duì)“商品表 1”進(jìn)行操作。如圖 1-23 所示,配置主節(jié)點(diǎn) writeHost 來(lái)負(fù)責(zé)寫(xiě)入操作,配置從節(jié)點(diǎn) readHost 來(lái)負(fù)責(zé)讀取操作。圖中處于上方的 MyCat 數(shù)據(jù)庫(kù)中間件會(huì)通過(guò) ZooKeeper 定期向兩個(gè)節(jié)點(diǎn)服務(wù)器發(fā)起心跳檢測(cè)(虛線部分)。圖 1-23 中實(shí)線部分描述的信息有:MyCat 開(kāi)啟讀寫(xiě)分離模式之后,中間件接收到商品讀/寫(xiě)的請(qǐng)求時(shí),會(huì)通過(guò) SQL 解析,將寫(xiě)入請(qǐng)求的 DML(Data Manipulation Language)SQL 發(fā)送到 writeHost 服務(wù)器上,將讀取請(qǐng)求的 Select SQL 發(fā)送到 readHost 服務(wù)器上。writeHost 在完成寫(xiě)入信息以后,會(huì)和 readHost 進(jìn)行數(shù)據(jù)同步,也就是主從復(fù)制。由于存在心跳檢測(cè)機(jī)制,當(dāng) writeHost 掛掉時(shí),如果在默認(rèn)N次心跳檢測(cè)后(N可以配置)仍舊沒(méi)有恢復(fù),MyCat 就會(huì)發(fā)起選舉,選舉出一臺(tái)服務(wù)器成為新的 writeHost。它會(huì)接替之前的 writeHost,負(fù)責(zé)處理寫(xiě)入數(shù)據(jù)的數(shù)據(jù)同步。當(dāng)之前的 writeHost 恢復(fù)以后,會(huì)成為從節(jié)點(diǎn) readHost 并且接收來(lái)自新 writeHost 的數(shù)據(jù)同步。

圖 1-23 讀寫(xiě)分離與主從復(fù)制

主站蜘蛛池模板: 信丰县| 凉城县| 黑龙江省| 怀远县| 定日县| 石柱| 和顺县| 万载县| 东安县| 凤凰县| 县级市| 睢宁县| 多伦县| 侯马市| 达孜县| 长岛县| 达日县| 民县| 无极县| 棋牌| 大埔区| 武鸣县| 昆山市| 格尔木市| 镇坪县| 延边| 拉萨市| 罗江县| 中超| 金乡县| 墨竹工卡县| 宿迁市| 浑源县| 高平市| 长海县| 洪泽县| 丽水市| 越西县| 桐柏县| 渝北区| 丹巴县|