您好,登錄后才能下訂單哦!
這篇文章主要介紹“Java分布式架構(gòu)原理是什么”的相關(guān)知識(shí),小編通過(guò)實(shí)際案例向大家展示操作過(guò)程,操作方法簡(jiǎn)單快捷,實(shí)用性強(qiáng),希望這篇“Java分布式架構(gòu)原理是什么”文章能幫助大家解決問(wèn)題。
內(nèi)存錯(cuò)誤、服務(wù)器停電等都會(huì)導(dǎo)致服務(wù)器宕機(jī),此時(shí)節(jié)點(diǎn)無(wú)法正常工作,稱為不可用。
服務(wù)器宕機(jī)會(huì)導(dǎo)致節(jié)點(diǎn)失去所有內(nèi)存信息,因此需要將內(nèi)存信息保存到持久化介質(zhì)上。
有一種特殊的網(wǎng)絡(luò)異常稱為——網(wǎng)絡(luò)分區(qū) ,即集群的所有節(jié)點(diǎn)被劃分為多個(gè)區(qū)域,每個(gè)區(qū)域內(nèi)部可以通信,但是區(qū)域之間無(wú)法通信。
磁盤(pán)故障是一種發(fā)生概率很高的異常。
使用冗余機(jī)制,將數(shù)據(jù)存儲(chǔ)到多臺(tái)服務(wù)器。
在分布式系統(tǒng)中,一個(gè)請(qǐng)求除了成功和失敗兩種狀態(tài),還存在著超時(shí)狀態(tài)。
可以將服務(wù)器的操作設(shè)計(jì)為具有 冪等性 ,即執(zhí)行多次的結(jié)果與執(zhí)行一次的結(jié)果相同。如果使用這種方式,當(dāng)出現(xiàn)超時(shí)的時(shí)候,可以不斷地重新請(qǐng)求直到成功。
常見(jiàn)的性能指標(biāo)有:吞吐量、響應(yīng)時(shí)間。
其中,吞吐量指系統(tǒng)在某一段時(shí)間可以處理的請(qǐng)求總數(shù),通常為每秒的讀操作數(shù)或者寫(xiě)操作數(shù);響應(yīng)時(shí)間指從某個(gè)請(qǐng)求發(fā)出到接收到返回結(jié)果消耗的時(shí)間。
這兩個(gè)指標(biāo)往往是矛盾的,追求高吞吐的系統(tǒng),往往很難做到低響應(yīng)時(shí)間,解釋如下:
在無(wú)并發(fā)的系統(tǒng)中,吞吐量為響應(yīng)時(shí)間的倒數(shù),例如響應(yīng)時(shí)間為 10 ms,那么吞吐量為 100 req/s,因此高吞吐也就意味著低響應(yīng)時(shí)間。
但是在并發(fā)的系統(tǒng)中,由于一個(gè)請(qǐng)求在調(diào)用 I/O 資源的時(shí)候,需要進(jìn)行等待。服務(wù)器端一般使用的是異步等待方式,即等待的請(qǐng)求被阻塞之后不需要一直占用 CPU 資源。這種方式能大大提高 CPU 資源的利用率,例如上面的例子中,單個(gè)請(qǐng)求在無(wú)并發(fā)的系統(tǒng)中響應(yīng)時(shí)間為 10 ms,如果在并發(fā)的系統(tǒng)中,那么吞吐量將大于 100 req/s。因此為了追求高吞吐量,通常會(huì)提高并發(fā)程度。但是并發(fā)程度的增加,會(huì)導(dǎo)致請(qǐng)求的平均響應(yīng)時(shí)間也增加,因?yàn)檎?qǐng)求不能馬上被處理,需要和其它請(qǐng)求一起進(jìn)行并發(fā)處理,響應(yīng)時(shí)間自然就會(huì)增高。
可用性指系統(tǒng)在面對(duì)各種異常時(shí)可以提供正常服務(wù)的能力??梢杂孟到y(tǒng)可用時(shí)間占總時(shí)間的比值來(lái)衡量,4 個(gè) 9 的可用性表示系統(tǒng) 99.99% 的時(shí)間是可用的。
可以從兩個(gè)角度理解一致性:從客戶端的角度,讀寫(xiě)操作是否滿足某種特性;從服務(wù)器的角度,多個(gè)數(shù)據(jù)副本之間是否一致。
指系統(tǒng)通過(guò)擴(kuò)展集群服務(wù)器規(guī)模來(lái)提高性能的能力。理想的分布式系統(tǒng)需要實(shí)現(xiàn)“線性可擴(kuò)展”,即隨著集群規(guī)模的增加,系統(tǒng)的整體性能也會(huì)線性增加。
分布式存儲(chǔ)系統(tǒng)的數(shù)據(jù)分布在多個(gè)節(jié)點(diǎn)中,常用的數(shù)據(jù)分布方式有哈希分布和順序分布。
數(shù)據(jù)庫(kù)的水平切分(Sharding)也是一種分布式存儲(chǔ)方法,下面的數(shù)據(jù)分布方法同樣適用于 Sharding。
哈希分布就是將數(shù)據(jù)計(jì)算哈希值之后,按照哈希值分配到不同的節(jié)點(diǎn)上。例如有 N 個(gè)節(jié)點(diǎn),數(shù)據(jù)的主鍵為 key,則將該數(shù)據(jù)分配的節(jié)點(diǎn)序號(hào)為:hash(key)%N。
傳統(tǒng)的哈希分布算法存在一個(gè)問(wèn)題:當(dāng)節(jié)點(diǎn)數(shù)量變化時(shí),也就是 N 值變化,那么幾乎所有的數(shù)據(jù)都需要重新分布,將導(dǎo)致大量的數(shù)據(jù)遷移。
一致性哈希
Distributed Hash Table(DHT):對(duì)于哈??臻g [0, 2n-1],將該哈希空間看成一個(gè)哈希環(huán),將每個(gè)節(jié)點(diǎn)都配置到哈希環(huán)上。每個(gè)數(shù)據(jù)對(duì)象通過(guò)哈希取模得到哈希值之后,存放到哈希環(huán)中順時(shí)針?lè)较虻谝粋€(gè)大于等于該哈希值的節(jié)點(diǎn)上。
一致性哈希的優(yōu)點(diǎn)是在增加或者刪除節(jié)點(diǎn)時(shí)只會(huì)影響到哈希環(huán)中相鄰的節(jié)點(diǎn),例如下圖中新增節(jié)點(diǎn) X,只需要將數(shù)據(jù)對(duì)象 C 重新存放到節(jié)點(diǎn) X 上即可,對(duì)于節(jié)點(diǎn) A、B、D 都沒(méi)有影響。
哈希分布式破壞了數(shù)據(jù)的有序性,順序分布則不會(huì)。
順序分布的數(shù)據(jù)劃分為多個(gè)連續(xù)的部分,按數(shù)據(jù)的 ID 或者時(shí)間分布到不同節(jié)點(diǎn)上。例如下圖中,User 表的 ID 范圍為 1 ~ 7000,使用順序分布可以將其劃分成多個(gè)子表,對(duì)應(yīng)的主鍵范圍為 1 ~ 1000,1001 ~ 2000,...,6001 ~ 7000。
順序分布的優(yōu)點(diǎn)是可以充分利用每個(gè)節(jié)點(diǎn)的空間,而哈希分布很難控制一個(gè)節(jié)點(diǎn)存儲(chǔ)多少數(shù)據(jù)。
但是順序分布需要使用一個(gè)映射表來(lái)存儲(chǔ)數(shù)據(jù)到節(jié)點(diǎn)的映射,這個(gè)映射表通常使用單獨(dú)的節(jié)點(diǎn)來(lái)存儲(chǔ)。當(dāng)數(shù)據(jù)量非常大時(shí),映射表也隨著變大,那么一個(gè)節(jié)點(diǎn)就可能無(wú)法存放下整個(gè)映射表。并且單個(gè)節(jié)點(diǎn)維護(hù)著整個(gè)映射表的開(kāi)銷很大,查找速度也會(huì)變慢。為了解決以上問(wèn)題,引入了一個(gè)中間層,也就是 Meta 表,從而分擔(dān)映射表的維護(hù)工作。
衡量負(fù)載的因素很多,如 CPU、內(nèi)存、磁盤(pán)等資源使用情況、讀寫(xiě)請(qǐng)求數(shù)等。
分布式系統(tǒng)存儲(chǔ)應(yīng)當(dāng)能夠自動(dòng)負(fù)載均衡,當(dāng)某個(gè)節(jié)點(diǎn)的負(fù)載較高,將它的部分?jǐn)?shù)據(jù)遷移到其它節(jié)點(diǎn)。
每個(gè)集群都有一個(gè)總控節(jié)點(diǎn),其它節(jié)點(diǎn)為工作節(jié)點(diǎn),由總控節(jié)點(diǎn)根據(jù)全局負(fù)載信息進(jìn)行整體調(diào)度,工作節(jié)點(diǎn)定時(shí)發(fā)送心跳包(Heartbeat)將節(jié)點(diǎn)負(fù)載相關(guān)的信息發(fā)送給總控節(jié)點(diǎn)。
一個(gè)新上線的工作節(jié)點(diǎn),由于其負(fù)載較低,如果不加控制,總控節(jié)點(diǎn)會(huì)將大量數(shù)據(jù)同時(shí)遷移到該節(jié)點(diǎn)上,造成該節(jié)點(diǎn)一段時(shí)間內(nèi)無(wú)法工作。因此負(fù)載均衡操作需要平滑進(jìn)行,新加入的節(jié)點(diǎn)需要較長(zhǎng)的一段時(shí)間來(lái)達(dá)到比較均衡的狀態(tài)。
分布式系統(tǒng)不可能同時(shí)滿足一致性(C:Consistency)、可用性(A:Availability)和分區(qū)容忍性(P:Partition Tolerance),最多只能同時(shí)滿足其中兩項(xiàng)。
一致性指的是多個(gè)數(shù)據(jù)副本是否能保持一致的特性。
在一致性的條件下,系統(tǒng)在執(zhí)行數(shù)據(jù)更新操作之后能夠從一致性狀態(tài)轉(zhuǎn)移到另一個(gè)一致性狀態(tài)。
對(duì)系統(tǒng)的一個(gè)數(shù)據(jù)更新成功之后,如果所有用戶都能夠讀取到最新的值,該系統(tǒng)就被認(rèn)為具有強(qiáng)一致性。
可用性指分布式系統(tǒng)在面對(duì)各種異常時(shí)可以提供正常服務(wù)的能力,可以用系統(tǒng)可用時(shí)間占總時(shí)間的比值來(lái)衡量,4 個(gè) 9 的可用性表示系統(tǒng) 99.99% 的時(shí)間是可用的。
在可用性條件下,系統(tǒng)提供的服務(wù)一直處于可用的狀態(tài),對(duì)于用戶的每一個(gè)操作請(qǐng)求總是能夠在有限的時(shí)間內(nèi)返回結(jié)果。
網(wǎng)絡(luò)分區(qū)指分布式系統(tǒng)中的節(jié)點(diǎn)被劃分為多個(gè)區(qū)域,每個(gè)區(qū)域內(nèi)部可以通信,但是區(qū)域之間無(wú)法通信。
在分區(qū)容忍性條件下,分布式系統(tǒng)在遇到任何網(wǎng)絡(luò)分區(qū)故障的時(shí)候,仍然需要能對(duì)外提供一致性和可用性的服務(wù),除非是整個(gè)網(wǎng)絡(luò)環(huán)境都發(fā)生了故障。
在分布式系統(tǒng)中,分區(qū)容忍性必不可少,因?yàn)樾枰偸羌僭O(shè)網(wǎng)絡(luò)是不可靠的。因此,CAP 理論實(shí)際在是要在可用性和一致性之間做權(quán)衡。
可用性和一致性往往是沖突的,很難都使它們同時(shí)滿足。在多個(gè)節(jié)點(diǎn)之間進(jìn)行數(shù)據(jù)同步時(shí),
為了保證一致性(CP),就需要讓所有節(jié)點(diǎn)下線成為不可用的狀態(tài),等待同步完成;
為了保證可用性(AP),在同步過(guò)程中允許讀取所有節(jié)點(diǎn)的數(shù)據(jù),但是數(shù)據(jù)可能不一致。
BASE 是基本可用(Basically Available)、軟狀態(tài)(Soft State)和最終一致性(Eventually Consistent)三個(gè)短語(yǔ)的縮寫(xiě)。
BASE 理論是對(duì) CAP 中一致性和可用性權(quán)衡的結(jié)果,它的理論的核心思想是:即使無(wú)法做到強(qiáng)一致性,但每個(gè)應(yīng)用都可以根據(jù)自身業(yè)務(wù)特點(diǎn),采用適當(dāng)?shù)姆绞絹?lái)使系統(tǒng)達(dá)到最終一致性。
指分布式系統(tǒng)在出現(xiàn)故障的時(shí)候,保證核心可用,允許損失部分可用性。
例如,電商在做促銷時(shí),為了保證購(gòu)物系統(tǒng)的穩(wěn)定性,部分消費(fèi)者可能會(huì)被引導(dǎo)到一個(gè)降級(jí)的頁(yè)面。
指允許系統(tǒng)中的數(shù)據(jù)存在中間狀態(tài),并認(rèn)為該中間狀態(tài)不會(huì)影響系統(tǒng)整體可用性,即允許系統(tǒng)不同節(jié)點(diǎn)的數(shù)據(jù)副本之間進(jìn)行同步的過(guò)程存在延時(shí)。
最終一致性強(qiáng)調(diào)的是系統(tǒng)中所有的數(shù)據(jù)副本,在經(jīng)過(guò)一段時(shí)間的同步后,最終能達(dá)到一致的狀態(tài)。
ACID 要求強(qiáng)一致性,通常運(yùn)用在傳統(tǒng)的數(shù)據(jù)庫(kù)系統(tǒng)上。而 BASE 要求最終一致性,通過(guò)犧牲強(qiáng)一致性來(lái)達(dá)到可用性,通常運(yùn)用在大型分布式系統(tǒng)中。
在實(shí)際的分布式場(chǎng)景中,不同業(yè)務(wù)單元和組件對(duì)一致性的要求是不同的,因此 ACID 和 BASE 往往會(huì)結(jié)合在一起使用。
兩階段提交(Two-phase Commit,2PC)
主要用于實(shí)現(xiàn)分布式事務(wù),分布式事務(wù)指的是事務(wù)操作跨越多個(gè)節(jié)點(diǎn),并且要求滿足事務(wù)的 ACID 特性。
通過(guò)引入?yún)f(xié)調(diào)者(Coordinator)來(lái)調(diào)度參與者的行為,并最終決定這些參與者是否要真正執(zhí)行事務(wù)。
準(zhǔn)備階段
協(xié)調(diào)者詢問(wèn)參與者事務(wù)是否執(zhí)行成功,參與者發(fā)回事務(wù)執(zhí)行結(jié)果。
提交階段
如果事務(wù)在每個(gè)參與者上都執(zhí)行成功,事務(wù)協(xié)調(diào)者發(fā)送通知讓參與者提交事務(wù);否則,協(xié)調(diào)者發(fā)送通知讓參與者回滾事務(wù)。
需要注意的是,在準(zhǔn)備階段,參與者執(zhí)行了事務(wù),但是還未提交。只有在提交階段接收到協(xié)調(diào)者發(fā)來(lái)的通知后,才進(jìn)行提交或者回滾。
同步阻塞
所有事務(wù)參與者在等待其它參與者響應(yīng)的時(shí)候都處于同步阻塞狀態(tài),無(wú)法進(jìn)行其它操作。
單點(diǎn)問(wèn)題
協(xié)調(diào)者在 2PC 中起到非常大的作用,發(fā)生故障將會(huì)造成很大影響,特別是在階段二發(fā)生故障,所有參與者會(huì)一直等待狀態(tài),無(wú)法完成其它操作。
數(shù)據(jù)不一致
在階段二,如果協(xié)調(diào)者只發(fā)送了部分 Commit 消息,此時(shí)網(wǎng)絡(luò)發(fā)生異常,那么只有部分參與者接收到 Commit 消息,也就是說(shuō)只有部分參與者提交了事務(wù),使得系統(tǒng)數(shù)據(jù)不一致。
太過(guò)保守
任意一個(gè)節(jié)點(diǎn)失敗就會(huì)導(dǎo)致整個(gè)事務(wù)失敗,沒(méi)有完善的容錯(cuò)機(jī)制。
優(yōu)點(diǎn):盡量保證了數(shù)據(jù)的強(qiáng)一致,適合對(duì)數(shù)據(jù)強(qiáng)一致要求很高的關(guān)鍵領(lǐng)域。(其實(shí)也不能 100%保證強(qiáng)一致) 缺點(diǎn):實(shí)現(xiàn)復(fù)雜,犧牲了可用性,對(duì)性能影響較大,不適合高并發(fā)高性能場(chǎng)景。
補(bǔ)償事務(wù)(TCC)其核心思想是:針對(duì)每個(gè)操作,都要注冊(cè)一個(gè)與其對(duì)應(yīng)的確認(rèn)和補(bǔ)償(撤銷)操作。它分為三個(gè)階段:
Try 階段主要是對(duì)業(yè)務(wù)系統(tǒng)做檢測(cè)及資源預(yù)留。
Confirm 階段主要是對(duì)業(yè)務(wù)系統(tǒng)做確認(rèn)提交,Try 階段執(zhí)行成功并開(kāi)始執(zhí)行 Confirm 階段時(shí),默認(rèn) Confirm 階段是不會(huì)出錯(cuò)的。即:只要 Try 成功,Confirm 一定成功。
Cancel 階段主要是在業(yè)務(wù)執(zhí)行錯(cuò)誤,需要回滾的狀態(tài)下執(zhí)行的業(yè)務(wù)取消,預(yù)留資源釋放。
舉個(gè)例子,假設(shè) Bob 要向 Smith 轉(zhuǎn)賬,思路大概是:
首先在 Try 階段,要先調(diào)用遠(yuǎn)程接口把 Smith 和 Bob 的錢(qián)給凍結(jié)起來(lái)。
在 Confirm 階段,執(zhí)行遠(yuǎn)程調(diào)用的轉(zhuǎn)賬的操作,轉(zhuǎn)賬成功進(jìn)行解凍。
如果第 2 步執(zhí)行成功,那么轉(zhuǎn)賬成功,如果第二步執(zhí)行失敗,則調(diào)用遠(yuǎn)程凍結(jié)接口對(duì)應(yīng)的解凍方法 (Cancel)。
優(yōu)點(diǎn):跟 2PC 比起來(lái),實(shí)現(xiàn)以及流程相對(duì)簡(jiǎn)單了一些,但數(shù)據(jù)的一致性比 2PC 也要差一些。
缺點(diǎn):缺點(diǎn)還是比較明顯的,在 2,3 步中都有可能失敗。TCC 屬于應(yīng)用層的一種補(bǔ)償方式,所以需要程序員在實(shí)現(xiàn)的時(shí)候多寫(xiě)很多補(bǔ)償?shù)拇a,在一些場(chǎng)景中,一些業(yè)務(wù)流程可能用 TCC 不太好定義及處理。
本地消息表與業(yè)務(wù)數(shù)據(jù)表處于同一個(gè)數(shù)據(jù)庫(kù)中,這樣就能利用本地事務(wù)來(lái)保證在對(duì)這兩個(gè)表的操作滿足事務(wù)特性。
在分布式事務(wù)操作的一方完成寫(xiě)業(yè)務(wù)數(shù)據(jù)的操作之后向本地消息表發(fā)送一個(gè)消息,本地事務(wù)能保證這個(gè)消息一定會(huì)被寫(xiě)入本地消息表中。
之后將本地消息表中的消息轉(zhuǎn)發(fā)到 Kafka 等消息隊(duì)列(MQ)中,如果轉(zhuǎn)發(fā)成功則將消息從本地消息表中刪除,否則繼續(xù)重新轉(zhuǎn)發(fā)。
在分布式事務(wù)操作的另一方從消息隊(duì)列中讀取一個(gè)消息,并執(zhí)行消息中的操作。
這種方案遵循 BASE 理論,采用的是最終一致性。
本地消息表利用了本地事務(wù)來(lái)實(shí)現(xiàn)分布式事務(wù),并且使用了消息隊(duì)列來(lái)保證最終一致性。
優(yōu)點(diǎn):一種非常經(jīng)典的實(shí)現(xiàn),避免了分布式事務(wù),實(shí)現(xiàn)了最終一致性。
缺點(diǎn):消息表會(huì)耦合到業(yè)務(wù)系統(tǒng)中,如果沒(méi)有封裝好的解決方案,會(huì)有很多雜活需要處理。
有一些第三方的 MQ 是支持事務(wù)消息的,比如 RocketMQ,他們支持事務(wù)消息的方式也是類似于采用的二階段提交。但是市面上一些主流的 MQ 都是不支持事務(wù)消息的,比如 RabbitMQ 和 Kafka 都不支持。
以阿里的 RocketMQ 中間件為例,其思路大致為:
Prepared 消息,會(huì)拿到消息的地址。
執(zhí)行本地事務(wù)。
通過(guò)第一階段拿到的地址去訪問(wèn)消息,并修改狀態(tài)。
也就是說(shuō)在業(yè)務(wù)方法內(nèi)要想消息隊(duì)列提交兩次請(qǐng)求,一次發(fā)送消息和一次確認(rèn)消息。如果確認(rèn)消息發(fā)送失敗了 RocketMQ 會(huì)定期掃描消息集群中的事務(wù)消息,這時(shí)候發(fā)現(xiàn)了 Prepared 消息,它會(huì)向消息發(fā)送者確認(rèn),所以生產(chǎn)方需要實(shí)現(xiàn)一個(gè) check 接口,RocketMQ 會(huì)根據(jù)發(fā)送端設(shè)置的策略來(lái)決定是回滾還是繼續(xù)發(fā)送確認(rèn)消息。這樣就保證了消息發(fā)送與本地事務(wù)同時(shí)成功或同時(shí)失敗。
優(yōu)點(diǎn):實(shí)現(xiàn)了最終一致性,不需要依賴本地?cái)?shù)據(jù)庫(kù)事務(wù)。
缺點(diǎn):實(shí)現(xiàn)難度大,主流 MQ 不支持。
用于達(dá)成共識(shí)性問(wèn)題,即對(duì)多個(gè)節(jié)點(diǎn)產(chǎn)生的值,該算法能保證只選出唯一一個(gè)值。
主要有三類節(jié)點(diǎn):
提議者(Proposer):提議一個(gè)值;
接受者(Acceptor):對(duì)每個(gè)提議進(jìn)行投票;
告知者(Learner):被告知投票的結(jié)果,不參與投票過(guò)程。
算法需要滿足 safety 和 liveness 兩方面的約束要求(實(shí)際上這兩個(gè)基礎(chǔ)屬性是大部分分布式算法都該考慮的):
safety:保證決議結(jié)果是對(duì)的,無(wú)歧義的,不會(huì)出現(xiàn)錯(cuò)誤情況。
決議(value)只有在被 proposers 提出的 proposal 才能被最終批準(zhǔn);
在一次執(zhí)行實(shí)例中,只批準(zhǔn)(chosen)一個(gè)最終決議,意味著多數(shù)接受(accept)的結(jié)果能成為決議;
liveness:保證決議過(guò)程能在有限時(shí)間內(nèi)完成。
決議總會(huì)產(chǎn)生,并且 learners 能獲得被批準(zhǔn)(chosen)的決議。
基本過(guò)程包括 proposer 提出提案,先爭(zhēng)取大多數(shù) acceptor 的支持,超過(guò)一半支持時(shí),則發(fā)送結(jié)案結(jié)果給所有人進(jìn)行確認(rèn)。一個(gè)潛在的問(wèn)題是 proposer 在此過(guò)程中出現(xiàn)故障,可以通過(guò)超時(shí)機(jī)制來(lái)解決。極為湊巧的情況下,每次新的一輪提案的 proposer 都恰好故障,系統(tǒng)則永遠(yuǎn)無(wú)法達(dá)成一致(概率很?。?。
Paxos 能保證在超過(guò) $1/2$ 的正常節(jié)點(diǎn)存在時(shí),系統(tǒng)能達(dá)成共識(shí)。
如果系統(tǒng)中限定只有某個(gè)特定節(jié)點(diǎn)是提案者,那么一致性肯定能達(dá)成(只有一個(gè)方案,要么達(dá)成,要么失敗)。提案者只要收到了來(lái)自多數(shù)接收者的投票,即可認(rèn)為通過(guò),因?yàn)橄到y(tǒng)中不存在其他的提案。
但一旦提案者故障,則系統(tǒng)無(wú)法工作。
限定某個(gè)節(jié)點(diǎn)作為接收者。這種情況下,共識(shí)也很容易達(dá)成,接收者收到多個(gè)提案,選第一個(gè)提案作為決議,拒絕掉后續(xù)的提案即可。
缺陷也是容易發(fā)生單點(diǎn)故障,包括接收者故障或首個(gè)提案者節(jié)點(diǎn)故障。
以上兩種情形其實(shí)類似主從模式,雖然不那么可靠,但因?yàn)樵砗?jiǎn)單而被廣泛采用。
當(dāng)提案者和接收者都推廣到多個(gè)的情形,會(huì)出現(xiàn)一些挑戰(zhàn)。
既然限定單提案者或單接收者都會(huì)出現(xiàn)故障,那么就得允許出現(xiàn)多個(gè)提案者和多個(gè)接收者。問(wèn)題一下子變得復(fù)雜了。
一種情況是同一時(shí)間片段(如一個(gè)提案周期)內(nèi)只有一個(gè)提案者,這時(shí)可以退化到單提案者的情形。需要設(shè)計(jì)一種機(jī)制來(lái)保障提案者的正確產(chǎn)生,例如按照時(shí)間、序列、或者大家猜拳(出一個(gè)數(shù)字來(lái)比較)之類??紤]到分布式系統(tǒng)要處理的工作量很大,這個(gè)過(guò)程要盡量高效,滿足這一條件的機(jī)制非常難設(shè)計(jì)。
另一種情況是允許同一時(shí)間片段內(nèi)可以出現(xiàn)多個(gè)提案者。那同一個(gè)節(jié)點(diǎn)可能收到多份提案,怎么對(duì)他們進(jìn)行區(qū)分呢?這個(gè)時(shí)候采用只接受第一個(gè)提案而拒絕后續(xù)提案的方法也不適用。很自然的,提案需要帶上不同的序號(hào)。節(jié)點(diǎn)需要根據(jù)提案序號(hào)來(lái)判斷接受哪個(gè)。比如接受其中序號(hào)較大(往往意味著是接受新提出的,因?yàn)榕f提案者故障概率更大)的提案。
如何為提案分配序號(hào)呢?一種可能方案是每個(gè)節(jié)點(diǎn)的提案數(shù)字區(qū)間彼此隔離開(kāi),互相不沖突。為了滿足遞增的需求可以配合用時(shí)間戳作為前綴字段。
此外,提案者即便收到了多數(shù)接收者的投票,也不敢說(shuō)就一定通過(guò)。因?yàn)樵诖诉^(guò)程中系統(tǒng)可能還有其它的提案。
Raft 算法是 Paxos 算法的一種簡(jiǎn)化實(shí)現(xiàn)。
包括三種角色:leader、candidate 和 follower,其基本過(guò)程為:
Leader 選舉 - 每個(gè) candidate 隨機(jī)經(jīng)過(guò)一定時(shí)間都會(huì)提出選舉方案,最近階段中得票最多者被選為 leader;
同步 log - leader 會(huì)找到系統(tǒng)中 log 最新的記錄,并強(qiáng)制所有的 follower 來(lái)刷新到這個(gè)記錄;
注:此處 log 并非是指日志消息,而是各種事件的發(fā)生記錄。
有三種節(jié)點(diǎn):Follower、Candidate 和 Leader。Leader 會(huì)周期性的發(fā)送心跳包給 Follower。每個(gè) Follower 都設(shè)置了一個(gè)隨機(jī)的競(jìng)選超時(shí)時(shí)間,一般為 150ms~300ms,如果在這個(gè)時(shí)間內(nèi)沒(méi)有收到 Leader 的心跳包,就會(huì)變成 Candidate,進(jìn)入競(jìng)選階段。
下圖表示一個(gè)分布式系統(tǒng)的最初階段,此時(shí)只有 Follower,沒(méi)有 Leader。Follower A 等待一個(gè)隨機(jī)的競(jìng)選超時(shí)時(shí)間之后,沒(méi)收到 Leader 發(fā)來(lái)的心跳包,因此進(jìn)入競(jìng)選階段。
此時(shí) A 發(fā)送投票請(qǐng)求給其它所有節(jié)點(diǎn)。
其它節(jié)點(diǎn)會(huì)對(duì)請(qǐng)求進(jìn)行回復(fù),如果超過(guò)一半的節(jié)點(diǎn)回復(fù)了,那么該 Candidate 就會(huì)變成 Leader。
之后 Leader 會(huì)周期性地發(fā)送心跳包給 Follower,F(xiàn)ollower 接收到心跳包,會(huì)重新開(kāi)始計(jì)時(shí)。
如果有多個(gè) Follower 成為 Candidate,并且所獲得票數(shù)相同,那么就需要重新開(kāi)始投票,例如下圖中 Candidate B 和 Candidate D 都獲得兩票,因此需要重新開(kāi)始投票。
當(dāng)重新開(kāi)始投票時(shí),由于每個(gè)節(jié)點(diǎn)設(shè)置的隨機(jī)競(jìng)選超時(shí)時(shí)間不同,因此能下一次再次出現(xiàn)多個(gè) Candidate 并獲得同樣票數(shù)的概率很低。
來(lái)自客戶端的修改都會(huì)被傳入 Leader。注意該修改還未被提交,只是寫(xiě)入日志中。
Leader 會(huì)把修改復(fù)制到所有 Follower。
Leader 會(huì)等待大多數(shù)的 Follower 也進(jìn)行了修改,然后才將修改提交。
此時(shí) Leader 會(huì)通知的所有 Follower 讓它們也提交修改,此時(shí)所有節(jié)點(diǎn)的值達(dá)成一致。
緩存雪崩是指:在高并發(fā)場(chǎng)景下,由于原有緩存失效,新緩存未到期間(例如:我們?cè)O(shè)置緩存時(shí)采用了相同的過(guò)期時(shí)間,在同一時(shí)刻出現(xiàn)大面積的緩存過(guò)期),所有原本應(yīng)該訪問(wèn)緩存的請(qǐng)求都去查詢數(shù)據(jù)庫(kù)了,而對(duì)數(shù)據(jù)庫(kù) CPU 和內(nèi)存造成巨大壓力,嚴(yán)重的會(huì)造成數(shù)據(jù)庫(kù)宕機(jī)。從而形成一系列連鎖反應(yīng),造成整個(gè)系統(tǒng)崩潰。
解決方案:
用加鎖或者隊(duì)列的方式保證來(lái)保證不會(huì)有大量的線程對(duì)數(shù)據(jù)庫(kù)一次性進(jìn)行讀寫(xiě),從而避免失效時(shí)大量的并發(fā)請(qǐng)求落到底層存儲(chǔ)系統(tǒng)上。
還有一個(gè)簡(jiǎn)單的方案,就是將緩存失效時(shí)間分散開(kāi),不要所有緩存時(shí)間長(zhǎng)度都設(shè)置成 5 分鐘或者 10 分鐘;比如我們可以在原有的失效時(shí)間基礎(chǔ)上增加一個(gè)隨機(jī)值,比如 1-5 分鐘隨機(jī),這樣每一個(gè)緩存的過(guò)期時(shí)間的重復(fù)率就會(huì)降低,就很難引發(fā)集體失效的事件。
緩存失效時(shí)產(chǎn)生的雪崩效應(yīng),將所有請(qǐng)求全部放在數(shù)據(jù)庫(kù)上,這樣很容易就達(dá)到數(shù)據(jù)庫(kù)的瓶頸,導(dǎo)致服務(wù)無(wú)法正常提供。盡量避免這種場(chǎng)景的發(fā)生。
緩存穿透是指:用戶查詢的數(shù)據(jù),在數(shù)據(jù)庫(kù)沒(méi)有,自然在緩存中也不會(huì)有。這樣就導(dǎo)致用戶查詢的時(shí)候,在緩存中找不到,每次都要去數(shù)據(jù)庫(kù)再查詢一遍,然后返回空(相當(dāng)于進(jìn)行了兩次無(wú)用的查詢)。這樣請(qǐng)求就繞過(guò)緩存直接查數(shù)據(jù)庫(kù),這也是經(jīng)常提的緩存命中率問(wèn)題。
當(dāng)在流量較大時(shí),出現(xiàn)這樣的情況,一直請(qǐng)求 DB,很容易導(dǎo)致服務(wù)掛掉。
解決方案:
在封裝的緩存 SET 和 GET 部分增加個(gè)步驟,如果查詢一個(gè) KEY 不存在,就以這個(gè) KEY 為前綴設(shè)定一個(gè)標(biāo)識(shí) KEY;以后再查詢?cè)?KEY 的時(shí)候,先查詢標(biāo)識(shí) KEY,如果標(biāo)識(shí) KEY 存在,就返回一個(gè)協(xié)定好的非 false 或者 NULL 值,然后 APP 做相應(yīng)的處理,這樣緩存層就不會(huì)被穿透。當(dāng)然這個(gè)驗(yàn)證 KEY 的失效時(shí)間不能太長(zhǎng)。
如果一個(gè)查詢返回的數(shù)據(jù)為空(不管是數(shù)據(jù)不存在,還是系統(tǒng)故障),我們?nèi)匀话堰@個(gè)空結(jié)果進(jìn)行緩存,但它的過(guò)期時(shí)間會(huì)很短,一般只有幾分鐘。
采用布隆過(guò)濾器,將所有可能存在的數(shù)據(jù)哈希到一個(gè)足夠大的 bitmap 中,一個(gè)一定不存在的數(shù)據(jù)會(huì)被這個(gè) bitmap 攔截掉,從而避免了對(duì)底層存儲(chǔ)系統(tǒng)的查詢壓力。
緩存預(yù)熱這個(gè)應(yīng)該是一個(gè)比較常見(jiàn)的概念,相信很多小伙伴都應(yīng)該可以很容易的理解,緩存預(yù)熱就是系統(tǒng)上線后,將相關(guān)的緩存數(shù)據(jù)直接加載到緩存系統(tǒng)。這樣就可以避免在用戶請(qǐng)求的時(shí)候,先查詢數(shù)據(jù)庫(kù),然后再將數(shù)據(jù)緩存的問(wèn)題!用戶直接查詢事先被預(yù)熱的緩存數(shù)據(jù)!
解決方案:
直接寫(xiě)個(gè)緩存刷新頁(yè)面,上線時(shí)手工操作下;
數(shù)據(jù)量不大,可以在項(xiàng)目啟動(dòng)的時(shí)候自動(dòng)進(jìn)行加載;
定時(shí)刷新緩存;
除了緩存服務(wù)器自帶的緩存失效策略之外(Redis 默認(rèn)的有 6 中策略可供選擇),我們還可以根據(jù)具體的業(yè)務(wù)需求進(jìn)行自定義的緩存淘汰,常見(jiàn)的策略有兩種:
定時(shí)去清理過(guò)期的緩存;
當(dāng)有用戶請(qǐng)求過(guò)來(lái)時(shí),再判斷這個(gè)請(qǐng)求所用到的緩存是否過(guò)期,過(guò)期的話就去底層系統(tǒng)得到新數(shù)據(jù)并更新緩存。
兩者各有優(yōu)劣,第一種的缺點(diǎn)是維護(hù)大量緩存的 key 是比較麻煩的,第二種的缺點(diǎn)就是每次用戶請(qǐng)求過(guò)來(lái)都要判斷緩存失效,邏輯相對(duì)比較復(fù)雜!具體用哪種方案,大家可以根據(jù)自己的應(yīng)用場(chǎng)景來(lái)權(quán)衡。
當(dāng)訪問(wèn)量劇增、服務(wù)出現(xiàn)問(wèn)題(如響應(yīng)時(shí)間慢或不響應(yīng))或非核心服務(wù)影響到核心流程的性能時(shí),仍然需要保證服務(wù)還是可用的,即使是有損服務(wù)。系統(tǒng)可以根據(jù)一些關(guān)鍵數(shù)據(jù)進(jìn)行自動(dòng)降級(jí),也可以配置開(kāi)關(guān)實(shí)現(xiàn)人工降級(jí)。
降級(jí)的最終目的是保證核心服務(wù)可用,即使是有損的。而且有些服務(wù)是無(wú)法降級(jí)的(如加入購(gòu)物車、結(jié)算)。
關(guān)于“Java分布式架構(gòu)原理是什么”的內(nèi)容就介紹到這里了,感謝大家的閱讀。如果想了解更多行業(yè)相關(guān)的知識(shí),可以關(guān)注億速云行業(yè)資訊頻道,小編每天都會(huì)為大家更新不同的知識(shí)點(diǎn)。
免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點(diǎn)不代表本網(wǎng)站立場(chǎng),如果涉及侵權(quán)請(qǐng)聯(lián)系站長(zhǎng)郵箱:is@yisu.com進(jìn)行舉報(bào),并提供相關(guān)證據(jù),一經(jīng)查實(shí),將立刻刪除涉嫌侵權(quán)內(nèi)容。