01
微服務(wù)與分布式
什么是分布式?
首先,我們對(duì)上圖提到的部分關(guān)鍵詞進(jìn)行講解。
單體,是指一個(gè)進(jìn)程完成全部的后端處理;水平拆分,是同一個(gè)后端多環(huán)境部署,他們都處理相同的內(nèi)容,使用反向代理來均衡負(fù)載,這種也叫集群;垂直拆分,則是把不同業(yè)務(wù)拆分為各個(gè)節(jié)點(diǎn),反向代理通過路由將請(qǐng)求分發(fā)給每個(gè)業(yè)務(wù)節(jié)點(diǎn)上。
分布式,就是不同的組件或者同一個(gè)組件在多個(gè)機(jī)器節(jié)點(diǎn)上部署,關(guān)鍵在于是否通過交換信息的方式進(jìn)行協(xié)作,也就是說,分布式是指通過網(wǎng)絡(luò)連接的多個(gè)組件,通過交換信息協(xié)作而形成的系統(tǒng)。而集群,是指同一種組件的多個(gè)實(shí)例,形成的邏輯上的整體,它們之間不會(huì)互相通信。
分布式和微服務(wù)有何不同?
簡(jiǎn)單來說,微服務(wù)是很小的服務(wù),一個(gè)服務(wù)可能負(fù)責(zé)幾個(gè)功能或者一個(gè)業(yè)務(wù),是一種面向SOA架構(gòu)的,服務(wù)之間也是通過RPC來交互或者是webservice來交互的,這個(gè)服務(wù)可以單獨(dú)部署運(yùn)行,每個(gè)微服務(wù)都是由獨(dú)立的小團(tuán)隊(duì)開發(fā),測(cè)試,部署,上線,負(fù)責(zé)它的整個(gè)生命周期。
再說分布式系統(tǒng),其是由一組通過網(wǎng)絡(luò)進(jìn)行通信、為了完成共同的任務(wù)而協(xié)調(diào)工作的計(jì)算機(jī)節(jié)點(diǎn)組成的系統(tǒng)分布式服務(wù)顧名思義服務(wù)是分散部署在不同的機(jī)器上的,微服務(wù)架構(gòu)是面向邏輯的架構(gòu)。邏輯架構(gòu)設(shè)計(jì)完后就該做物理架構(gòu)設(shè)計(jì),系統(tǒng)應(yīng)用部署在超過一臺(tái)服務(wù)器或虛擬機(jī)上,且各分開部署的部分彼此通過各種通訊協(xié)議交互信息,就可算作分布式部署,生產(chǎn)環(huán)境下的微服務(wù)肯定是分布式部署的,分布式部署的應(yīng)用不一定是微服務(wù)架構(gòu)的,比如集群部署,它是把相同應(yīng)用復(fù)制到不同服務(wù)器上,但是邏輯功能上還是單體應(yīng)用。
分布式和微服的架構(gòu)很相似,只是部署的方式不一樣。假設(shè)去大飯店吃飯就是一個(gè)完整的業(yè)務(wù)的話, 飯店的廚師、洗碗阿姨、服務(wù)員就是分布式,特點(diǎn)是各司其職。廚師、洗碗阿姨和服務(wù)員都不止一個(gè)人,這就是集群。分布式就是微服務(wù)的一種表現(xiàn)形式,分布式是部署層面,微服務(wù)是設(shè)計(jì)層面。
為什么要分布式部署?
分布式部署優(yōu)點(diǎn):
分布式計(jì)算可以把大型計(jì)算分布到多臺(tái)計(jì)算機(jī)上進(jìn)行,它可以根據(jù)不同的任務(wù)和場(chǎng)景來配置不同數(shù)量的計(jì)算資源,滿足所需要的快速響應(yīng)時(shí)間,提供更高的性能。
分布式計(jì)算系統(tǒng)可以根據(jù)需要,增加更多的計(jì)算機(jī)來滿足技術(shù)需求
分布式計(jì)算因?yàn)椴捎煤芏嘤?jì)算機(jī)來完成計(jì)算,一臺(tái)服務(wù)器的崩潰并不影響到其余的服務(wù)器,失敗的任務(wù)也會(huì)被調(diào)度到其他服務(wù)器上重新執(zhí)行,不影響總體任務(wù)的完成
分布式部署缺點(diǎn):
而要定位具體的故障機(jī)器及原因,并進(jìn)行故障調(diào)試就存在著很多的問題。引起故障的原因也是多方面的,可能是網(wǎng)絡(luò)問題、硬件問題、權(quán)限問題、同步問題等,要進(jìn)行問題的重現(xiàn)和跟蹤診斷遠(yuǎn)不如一臺(tái)服務(wù)器或是一個(gè)集中的運(yùn)行環(huán)境來得方便。
經(jīng)常會(huì)遇到網(wǎng)絡(luò)基礎(chǔ)設(shè)施的問題,如傳輸問題、網(wǎng)絡(luò)擁堵、信息丟失等,需要在應(yīng)用層面處理所有這些故障,造成比較大的開銷
開發(fā)過程中需要考慮到分布式可能帶來的數(shù)據(jù),安全等的問題,需要開發(fā)來解決,增加工作難度。
02
分布式系統(tǒng)挑戰(zhàn)
挑戰(zhàn)
分布式系統(tǒng)需要大量機(jī)器協(xié)作,面臨諸多的挑戰(zhàn):
#1
異構(gòu)的機(jī)器與網(wǎng)絡(luò)。
分布式系統(tǒng)中的機(jī)器,配置不一樣,其上運(yùn)行的服務(wù)也可能由不同的語言、架構(gòu)實(shí)現(xiàn),因此處理能力也不一樣;節(jié)點(diǎn)間通過網(wǎng)絡(luò)連接,而不同網(wǎng)絡(luò)運(yùn)營商提供的網(wǎng)絡(luò)的帶寬、延時(shí)、丟包率又不一樣。怎么保證大家齊頭并進(jìn),共同完成目標(biāo),這四個(gè)不小的挑戰(zhàn)。
普遍的節(jié)點(diǎn)故障。
雖然單個(gè)節(jié)點(diǎn)的故障概率較低,但節(jié)點(diǎn)數(shù)目達(dá)到一定規(guī)模,出故障的概率就變高了。分布式系統(tǒng)需要保證故障發(fā)生的時(shí)候,系統(tǒng)仍然是可用的,這就需要監(jiān)控節(jié)點(diǎn)的狀態(tài),在節(jié)點(diǎn)故障的情況下將該節(jié)點(diǎn)負(fù)責(zé)的計(jì)算、存儲(chǔ)任務(wù)轉(zhuǎn)移到其他節(jié)點(diǎn)。
#2
#3
不可靠的網(wǎng)絡(luò)。
節(jié)點(diǎn)間通過網(wǎng)絡(luò)通信,而網(wǎng)絡(luò)是不可靠的??赡艿木W(wǎng)絡(luò)問題包括:網(wǎng)絡(luò)分割、延時(shí)、丟包、亂序。
相比單機(jī)過程調(diào)用,網(wǎng)絡(luò)通信最讓人頭疼的是超時(shí):節(jié)點(diǎn) A 向節(jié)點(diǎn) B 發(fā)出請(qǐng)求,在約定的時(shí)間內(nèi)沒有收到節(jié)點(diǎn) B 的響應(yīng),那么 B 是否處理了請(qǐng)求,這個(gè)是不確定的,這個(gè)不確定會(huì)帶來諸多問題,最簡(jiǎn)單的,是否要重試請(qǐng)求,節(jié)點(diǎn) B 會(huì)不會(huì)多次處理同一個(gè)請(qǐng)求。
分布式系統(tǒng)CAP+Base
CAP原則又稱CAP定理,指的是在一個(gè)分布式系統(tǒng)中,一致性(Consistency)、可用性(Availability)、分區(qū)容錯(cuò)性(Partition tolerance)。
在分布式系統(tǒng)的設(shè)計(jì)中,沒有一種設(shè)計(jì)可以同時(shí)滿足一致性,可用性,分區(qū)容錯(cuò)性 3個(gè)特性 :
為什么在分布式系統(tǒng)的設(shè)計(jì)中,沒有一種設(shè)計(jì)可以同時(shí)滿足一致性,可用性,分區(qū)容錯(cuò)性 3個(gè)特性?
分布式系統(tǒng)CP案例 (保證強(qiáng)一致性zookeeper)
知識(shí)點(diǎn)預(yù)熱(ZooKeeper 集群中的角色簡(jiǎn)介):
當(dāng)Follower發(fā)現(xiàn)Leader節(jié)點(diǎn)掛了之后,F(xiàn)ollower會(huì)拒絕現(xiàn)有的客戶端連接,參與主從選舉,在成功選舉出leader之前,zk將不可用;這個(gè)時(shí)候如果ZK作為注冊(cè)中心,注冊(cè)中心將不可用。
分布式系統(tǒng)AP案例 (保證高可用性)
從上圖中可看出,Eureka注冊(cè)中心采用集群部署,滿足高可用的特性,當(dāng)訂單服務(wù)在擴(kuò)容的時(shí)候,新增訂單服務(wù)實(shí)例4,數(shù)據(jù)注冊(cè)到Eureka A節(jié)點(diǎn),在其余的兩個(gè)節(jié)點(diǎn)沒有訂單服務(wù)4這個(gè)實(shí)例信息。這種情況下,用戶服務(wù)獲取服務(wù)列表,可能獲取不到訂單實(shí)例4,這就是最簡(jiǎn)單的數(shù)據(jù)不一致情況。但是對(duì)于Eureka集群本身,各個(gè)節(jié)點(diǎn)都處于平等的地位,完全是為了冗余,這時(shí)有其中一個(gè)節(jié)點(diǎn)掛掉并不影響Eureka的使用,從這個(gè)角度來說保證了分布式的高可用。
分布式系統(tǒng)BASE理論
BASE 理論中的最終一致性,是指分布式系統(tǒng)中所有的數(shù)據(jù)副本在經(jīng)過一段時(shí)間的同步后,最終都會(huì)達(dá)到一個(gè)一致的狀態(tài)。也就是說,在數(shù)據(jù)副本在達(dá)到一致之前,會(huì)存在一斷延遲。
03
實(shí)戰(zhàn)案例
場(chǎng)景一:websocket實(shí)現(xiàn)分布式方案
問題描述:websocket相關(guān)簡(jiǎn)介
業(yè)務(wù)場(chǎng)景:前端發(fā)起流水線執(zhí)行的HTTP請(qǐng)求,點(diǎn)擊執(zhí)行日志與后端建立WS鏈接,后端能夠?qū)崟r(shí)的把日志主動(dòng)推送到前端頁面。
問題描述:websocket分布式會(huì)遇到的問題
當(dāng)后端服務(wù)器有多臺(tái)時(shí),用戶A發(fā)起流水線執(zhí)行的HTTP請(qǐng)求發(fā)送到B服務(wù)器,點(diǎn)擊查看實(shí)時(shí)日志與服務(wù)器A建立WS長鏈接,無法接收到實(shí)時(shí)日志的推送,而B用戶刷新當(dāng)前流水線可以看到實(shí)時(shí)日志。
期待效果:
用戶A發(fā)起流水線執(zhí)行的HTTP請(qǐng)求發(fā)送到B服務(wù)器。用戶A、用戶B點(diǎn)擊實(shí)時(shí)日志查看,均可以查看到流水線執(zhí)行的情況。
解決方案:
用戶A發(fā)起流水線執(zhí)行的HTTP請(qǐng)求發(fā)送到B服務(wù)器。B服務(wù)器把流水線執(zhí)行日志實(shí)時(shí)推送到Redis中,服務(wù)器A和B監(jiān)聽訂閱Redis的日志數(shù)據(jù),一旦監(jiān)聽到數(shù)據(jù),服務(wù)器A、B通過WS長鏈接把日志推送給與當(dāng)前服務(wù)器建立長鏈接的用戶A、用戶B。這樣就起到了解耦的作用,無論用戶的請(qǐng)求發(fā)送到哪一臺(tái)服務(wù)器上,無論用戶的長鏈接是與哪一臺(tái)服務(wù)器建立連接,都可以查看實(shí)時(shí)日志。
場(chǎng)景二:定時(shí)線程池分布式問題
問題描述
在項(xiàng)目中我們經(jīng)常會(huì)使用定時(shí)線程池來實(shí)現(xiàn)一個(gè)定時(shí)的異步任務(wù),線程池的創(chuàng)建和銷毀也是JVM層面的,如果分布式多機(jī)器部署的場(chǎng)景使用到定時(shí)線程池又會(huì)遇到什么問題,又要怎么去解決?
問題一:用戶在A服務(wù)器創(chuàng)建了一個(gè)任務(wù)加入到A服務(wù)器的線程池中,然后用戶B負(fù)載到B服務(wù)器上去取消這個(gè)定時(shí)任務(wù)?無法取消?
思考:是不是可以對(duì)創(chuàng)建的任務(wù)做一個(gè)Hash,有關(guān)某個(gè)任務(wù)的所有的操作都負(fù)載到同一個(gè)服務(wù)器上
結(jié)合問題及思考進(jìn)行如下改造:
問題二:如果突然A服務(wù)器掛了,這時(shí)候A服務(wù)器上面定時(shí)線程池中所存的任務(wù)就會(huì)消失?該怎么辦?
思考:這里和上面websocket場(chǎng)景還不一樣,上面實(shí)時(shí)查看日志,如果服務(wù)器掛掉用戶刷新一下頁面,用戶可以重新負(fù)載到其他服務(wù)器繼續(xù)查看日志。這里我創(chuàng)建完成一個(gè)任務(wù)之后,就完全交給JVM線程池了,服務(wù)器掛掉之后任務(wù)用戶自己創(chuàng)建的任務(wù)就消失了。怎么解決?
結(jié)合問題及思考進(jìn)行如下改造:
方案:我們把任務(wù)放在線程池中,并且緩存在Redis中做緩存?zhèn)浞?,借鑒微服務(wù)注冊(cè)中心的概念,我們把服務(wù)注冊(cè)到注冊(cè)中心,當(dāng)注冊(cè)中心檢測(cè)到B服務(wù)器掛掉了,就通知A服務(wù)器就把掛掉的B服務(wù)器線程池中的任務(wù)同步到A服務(wù)器中同步創(chuàng)建一份繼續(xù)執(zhí)行。
細(xì)節(jié)考慮:如果有一臺(tái)C服務(wù)器掛掉了,A和B同時(shí)監(jiān)聽到,會(huì)不會(huì)A和B分別在本地都創(chuàng)建了B的任務(wù),那同一時(shí)刻A和C分別會(huì)執(zhí)行B的任務(wù),同一時(shí)刻任務(wù)會(huì)執(zhí)行兩遍?
深度思考:分布式帶來了什么問題?系統(tǒng)的復(fù)雜度是不是大大的增加了。維護(hù)起來變得更復(fù)雜了。
案例總結(jié)
04
分布式常見方案
分布式事物
本地事物的ACID:
分布式事務(wù),就是指不是在單個(gè)服務(wù)或單個(gè)數(shù)據(jù)庫架構(gòu)下,產(chǎn)生的事務(wù),例如:在數(shù)據(jù)庫水平拆分、服務(wù)垂直拆分之后,一個(gè)業(yè)務(wù)操作通常要跨多個(gè)數(shù)據(jù)庫、服務(wù)才能完成。例如電商行業(yè)中比較常見的下單付款案例,包括下面幾個(gè)行為:
完成上面的操作需要訪問三個(gè)不同的微服務(wù)和三個(gè)不同的數(shù)據(jù)庫。訂單的創(chuàng)建、庫存的扣減、賬戶扣款在每一個(gè)服務(wù)和數(shù)據(jù)庫內(nèi)是一個(gè)本地事務(wù),可以保證ACID原則。但是當(dāng)我們把三件事情看做一個(gè)'業(yè)務(wù)',要滿足保證“業(yè)務(wù)”的原子性,要么所有操作全部成功,要么全部失敗,不允許出現(xiàn)部分成功部分失敗的現(xiàn)象,這就是分布式系統(tǒng)下的事務(wù)。
分布式事物解決方案:Seata
Seata事務(wù)管理中有三個(gè)重要的角色:
分布式鎖
什么情況下需要使用鎖?
在單機(jī)環(huán)境下,也就是單個(gè)JVM環(huán)境下多線程對(duì)共享資源的并發(fā)更新處理,我們可以簡(jiǎn)單地使用JDK提供的ReentrantLock。
如果是在微服務(wù)架構(gòu)多實(shí)例的環(huán)境下,每一個(gè)服務(wù)都有多個(gè)節(jié)點(diǎn),我們?nèi)绻€是按照之前的方式來做,就會(huì)出現(xiàn)如下圖這樣的情況:這個(gè)時(shí)候再用ReentrantLock就沒辦法控制了,因?yàn)檫@時(shí)候這些任務(wù)是跨JVM的,不再是簡(jiǎn)單的單體應(yīng)用了,需要協(xié)同多個(gè)節(jié)點(diǎn)信息,共同獲取鎖的競(jìng)爭(zhēng)情況。
分布式鎖解決方案:Redis
基于數(shù)據(jù)庫實(shí)現(xiàn)的分布式鎖。
實(shí)現(xiàn)邏輯:在數(shù)據(jù)庫中創(chuàng)建一個(gè)表,表中包含方法名、類名等字段,并在方法名字段上創(chuàng)建唯一索引,當(dāng)執(zhí)行某個(gè)方法時(shí),就使用這個(gè)方法名向表中插入數(shù)據(jù),插入成功就相當(dāng)于獲取了鎖,執(zhí)行完成后刪除對(duì)應(yīng)的行數(shù)據(jù)釋放鎖。
但是要注意以下幾點(diǎn)要求:
基于redis實(shí)現(xiàn)的分布式鎖。
實(shí)現(xiàn)邏輯:獲取鎖的時(shí)候,使用setnx加鎖,并使用expire命令為鎖添加一個(gè)超時(shí)時(shí)間,超過該時(shí)間則自動(dòng)釋放鎖,鎖的value值為一個(gè)隨機(jī)生成的UUID,通過此在釋放鎖的時(shí)候進(jìn)行判斷。獲取鎖的時(shí)候還設(shè)置一個(gè)獲取的超時(shí)時(shí)間,若超過這個(gè)時(shí)間則放棄獲取鎖。
Redission基于redis實(shí)現(xiàn)的分布式鎖RedLock。
Redission是基于redis實(shí)現(xiàn)的用于解決分布式問題的框架,其中RedLock是用來解決分布式鎖的常見方案。使用也比較簡(jiǎn)單。
分布式ID
分布式 ID 是分布式系統(tǒng)下的 ID。例如:?jiǎn)螜C(jī) MySQL 需要進(jìn)行分庫分表。在分庫之后, 數(shù)據(jù)遍布在不同服務(wù)器上的數(shù)據(jù)庫,數(shù)據(jù)庫的自增主鍵已經(jīng)沒辦法滿足生成的主鍵唯一了。分布式ID就是為不同的數(shù)據(jù)節(jié)點(diǎn)生成全局唯一主鍵ID。
分布式 ID 需要滿足哪些要求?
分布式 ID 常見的方案
方案一:Redis的Incr
NoSQL 方案使用 Redis 多一些。我們通過 Redis 的 incr 命令即可實(shí)現(xiàn)對(duì) id 原子順序遞增。
方案二:UUID
UUID 可以保證唯一性,因?yàn)槠渖梢?guī)則包括 MAC 地址、時(shí)間戳、名字空間(Namespace)、隨機(jī)或偽隨機(jī)數(shù)、時(shí)序等元素,計(jì)算機(jī)基于這些規(guī)則生成的 UUID 是肯定不會(huì)重復(fù)的。
方案三:雪花算法
Snowflake 是 Twitter 開源的分布式 ID 生成算法。Snowflake 由 64 bit 的二進(jìn)制數(shù)字組成,這 64bit 的二進(jìn)制被分成了幾部分,每一部分存儲(chǔ)的數(shù)據(jù)都有特定的含義:
分布式調(diào)度
分布式調(diào)度定義(分布式任務(wù)調(diào)度有兩層含義):
分布式方案一:Elastic-Job
Elastic_Job 是當(dāng)當(dāng)網(wǎng)開源的一個(gè)分布式調(diào)度解決方案,基于 Quartz 二次開發(fā)的,功能非常豐富強(qiáng)大,采用 zookeeper 實(shí)現(xiàn)分布式調(diào)度,實(shí)現(xiàn)任務(wù)分片以及高可用。目前由兩個(gè)相互獨(dú)立的子項(xiàng)目 Elatstic-Job-Lite 和 Elastic-Job-Cloud 組成。目前說的是 Elastic-Job-Lite 的輕量級(jí)解決方案,使用 jar 的形式提供分布式任務(wù)的調(diào)度服務(wù),而 Elastic-Job-Cloud 是結(jié)合 Mesos 以及 Docker 在云環(huán)境下使用。
分布式調(diào)度方案二:XXL-JOB
XXL-JOB是一個(gè)輕量級(jí)分布式任務(wù)調(diào)度平臺(tái),有以下特性:
最后,工具推薦Redisson,是架設(shè)在Redis基礎(chǔ)上的一個(gè)Java駐內(nèi)存數(shù)據(jù)網(wǎng)格(In-Memory Data Grid)。充分的利用了Redis鍵值數(shù)據(jù)庫提供的一系列優(yōu)勢(shì),基于Java實(shí)用工具包中常用接口,為使用者提供了一系列具有分布式特性的常用工具類。使得原本作為協(xié)調(diào)單機(jī)多線程并發(fā)程序的工具包獲得了協(xié)調(diào)分布式多機(jī)多線程并發(fā)系統(tǒng)的能力,大大降低了設(shè)計(jì)和研發(fā)大規(guī)模分布式系統(tǒng)的難度。同時(shí)結(jié)合各富特色的分布式服務(wù),更進(jìn)一步簡(jiǎn)化了分布式環(huán)境中程序相互之間的協(xié)作。
https://github.com/redisson/redisson/wiki/%E7%9B%AE%E5%BD%95
聯(lián)系客服