国产 无码 综合区,色欲AV无码国产永久播放,无码天堂亚洲国产AV,国产日韩欧美女同一区二区

【技術(shù)驛站】分布式基礎(chǔ)與常見面試問題

這篇具有很好參考價(jià)值的文章主要介紹了【技術(shù)驛站】分布式基礎(chǔ)與常見面試問題。希望對(duì)大家有所幫助。如果存在錯(cuò)誤或未考慮完全的地方,請(qǐng)大家不吝賜教,您也可以點(diǎn)擊"舉報(bào)違法"按鈕提交疑問。

??????歡迎來到我的博客,很高興能夠在這里和您見面!希望您在這里可以感受到一份輕松愉快的氛圍,不僅可以獲得有趣的內(nèi)容和知識(shí),也可以暢所欲言、分享您的想法和見解。
【技術(shù)驛站】分布式基礎(chǔ)與常見面試問題,s10 分布式,分布式

  • 推薦:kwan 的首頁,持續(xù)學(xué)習(xí),不斷總結(jié),共同進(jìn)步,活到老學(xué)到老
  • 導(dǎo)航
    • 檀越劍指大廠系列:全面總結(jié) java 核心技術(shù)點(diǎn),如集合,jvm,并發(fā)編程 redis,kafka,Spring,微服務(wù),Netty 等
    • 常用開發(fā)工具系列:羅列常用的開發(fā)工具,如 IDEA,Mac,Alfred,electerm,Git,typora,apifox 等
    • 數(shù)據(jù)庫系列:詳細(xì)總結(jié)了常用數(shù)據(jù)庫 mysql 技術(shù)點(diǎn),以及工作中遇到的 mysql 問題等
    • 懶人運(yùn)維系列:總結(jié)好用的命令,解放雙手不香嗎?能用一個(gè)命令完成絕不用兩個(gè)操作
    • 數(shù)據(jù)結(jié)構(gòu)與算法系列:總結(jié)數(shù)據(jù)結(jié)構(gòu)和算法,不同類型針對(duì)性訓(xùn)練,提升編程思維,劍指大廠

非常期待和您一起在這個(gè)小小的網(wǎng)絡(luò)世界里共同探索、學(xué)習(xí)和成長。?????? ?? 歡迎訂閱本專欄 ??

一.分布式理論

1.CAP 理論

CAP 理論,也被稱為 CAP 定理,是一個(gè)與分布式系統(tǒng)和數(shù)據(jù)庫系統(tǒng)相關(guān)的理論,由計(jì)算機(jī)科學(xué)家埃里克·布魯爾(Eric Brewer)在 2000 年提出。該理論描述了分布式計(jì)算系統(tǒng)中三個(gè)關(guān)鍵概念之間的權(quán)衡:

  1. 一致性(Consistency):在分布式系統(tǒng)中的所有節(jié)點(diǎn)看到的數(shù)據(jù)副本都是一致的。換句話說,如果一個(gè)節(jié)點(diǎn)在某次操作之后讀取了數(shù)據(jù),那么其他節(jié)點(diǎn)在同一時(shí)間也應(yīng)該能夠讀取到相同的數(shù)據(jù)。

  2. 可用性(Availability):系統(tǒng)在有限時(shí)間內(nèi)對(duì)于客戶端請(qǐng)求的響應(yīng)能力。即使系統(tǒng)中的某些節(jié)點(diǎn)出現(xiàn)故障,也應(yīng)該盡量保持對(duì)外提供服務(wù),而不是完全不可用。

  3. 分區(qū)容錯(cuò)性(Partition Tolerance):系統(tǒng)能夠繼續(xù)運(yùn)行,即使網(wǎng)絡(luò)中的某些部分無法通信,導(dǎo)致消息在節(jié)點(diǎn)之間可能會(huì)丟失或延遲。

CAP 理論指出,在分布式系統(tǒng)中,無法同時(shí)滿足一致性、可用性和分區(qū)容錯(cuò)性這三個(gè)要求。在任何分布式系統(tǒng)中,只能滿足其中的兩個(gè)。這是因?yàn)楫?dāng)分布式系統(tǒng)遇到網(wǎng)絡(luò)分區(qū)或故障時(shí),如果要保持一致性,可能會(huì)犧牲可用性,反之亦然。

2.分布式系統(tǒng)類型

根據(jù) CAP 理論的劃分,分布式系統(tǒng)可以分為以下三種類型:

  1. CP:保證一致性和分區(qū)容錯(cuò)性,可能犧牲可用性。這意味著系統(tǒng)在遇到網(wǎng)絡(luò)分區(qū)或節(jié)點(diǎn)故障時(shí),會(huì)停止對(duì)外提供服務(wù),以確保數(shù)據(jù)的一致性。

  2. CA:保證一致性和可用性,但可能會(huì)犧牲分區(qū)容錯(cuò)性。這種情況下,分布式系統(tǒng)在面臨網(wǎng)絡(luò)分區(qū)時(shí),會(huì)選擇保持一致性和可用性,但可能無法對(duì)所有請(qǐng)求都提供響應(yīng)。

  3. AP:保證可用性和分區(qū)容錯(cuò)性,但可能犧牲一致性。在這種情況下,系統(tǒng)會(huì)繼續(xù)對(duì)外提供服務(wù),即使數(shù)據(jù)在節(jié)點(diǎn)之間不一致。

需要注意的是,CAP 理論的描述是一個(gè)理論性的極端情況,實(shí)際系統(tǒng)往往會(huì)在這三個(gè)屬性之間進(jìn)行權(quán)衡,根據(jù)具體的需求和應(yīng)用場景做出合適的選擇。

3.Base 理論

BASE 理論是一個(gè)與分布式數(shù)據(jù)庫系統(tǒng)和分布式系統(tǒng)設(shè)計(jì)相關(guān)的概念,它是對(duì) CAP 理論的一種實(shí)際應(yīng)用和權(quán)衡。BASE 是一個(gè)縮寫,代表著三個(gè)關(guān)鍵屬性:

  1. 基本可用性(Basically Available):系統(tǒng)保證在有限時(shí)間內(nèi)對(duì)客戶端的請(qǐng)求做出響應(yīng),即使系統(tǒng)發(fā)生部分故障或者數(shù)據(jù)不一致。

  2. 軟狀態(tài)(Soft state):系統(tǒng)中的數(shù)據(jù)狀態(tài)可能會(huì)隨著時(shí)間的推移而發(fā)生變化,即時(shí)不同節(jié)點(diǎn)間的數(shù)據(jù)副本不一致。

  3. 最終一致性(Eventual Consistency):系統(tǒng)保證在一段時(shí)間內(nèi),如果不再發(fā)生更新,系統(tǒng)中的所有數(shù)據(jù)副本最終會(huì)達(dá)到一致的狀態(tài)。這與強(qiáng)一致性(如 ACID)不同,強(qiáng)一致性要求在任何時(shí)間點(diǎn)都保持?jǐn)?shù)據(jù)一致,而最終一致性允許在一段時(shí)間內(nèi)存在不一致的狀態(tài)。

BASE 理論的思想是,分布式系統(tǒng)可以放松強(qiáng)一致性的要求,從而獲得更高的可用性和性能。在分布式系統(tǒng)中,由于網(wǎng)絡(luò)延遲、節(jié)點(diǎn)故障等因素,實(shí)現(xiàn)強(qiáng)一致性可能會(huì)導(dǎo)致性能問題。相反,采用最終一致性的策略,允許數(shù)據(jù)在一段時(shí)間內(nèi)不同步,但會(huì)在后續(xù)的處理中逐漸達(dá)到一致狀態(tài),從而更好地滿足系統(tǒng)的可用性和性能需求。

需要注意的是,BASE 理論并不是絕對(duì)的,它適用于某些分布式系統(tǒng)和應(yīng)用場景,但不適用于所有情況。在設(shè)計(jì)分布式系統(tǒng)時(shí),根據(jù)具體的業(yè)務(wù)需求和性能要求,可以選擇適當(dāng)?shù)囊恢滦约?jí)別和數(shù)據(jù)管理策略。

4.集群對(duì)比

  • rebalance 數(shù)據(jù)重分配
  • 擴(kuò)容
  • 是否有副本
  • 是否有分片
  • 是否主從
  • 如何負(fù)載均衡
  • 集群節(jié)點(diǎn)發(fā)現(xiàn)
  • 宕機(jī)自修復(fù)
  • 是否節(jié)點(diǎn)對(duì)等

5.鏈?zhǔn)秸{(diào)用

  • spring 中的 FilterChain,鏈?zhǔn)秸{(diào)用 filter

  • netty 中的 ChannelHandler

  • sentinel 中的 slot chain

  • java8 中的 stream 處理

  • spring 中的狀態(tài)機(jī)模式定義

  • gatway 中的謂詞 router

6.框架中的 COW

框架中用到的寫時(shí)復(fù)制,copy on write

  • Redis 的 Aof 文件重寫
  • SpringCloud Alibaba 中的 Sentinel 中的 slotChain
  • ArrayList 擴(kuò)容時(shí)

7.時(shí)間輪算法

時(shí)間輪算法(Time Wheel Algorithm)是一種用于處理定時(shí)事件的數(shù)據(jù)結(jié)構(gòu)和算法。它主要用于操作系統(tǒng)、網(wǎng)絡(luò)通信和其他需要按時(shí)間調(diào)度任務(wù)的場景。時(shí)間輪算法基于一個(gè)循環(huán)的時(shí)間輪結(jié)構(gòu),類似于一個(gè)時(shí)鐘,其中每個(gè)刻度代表一個(gè)時(shí)間單位,例如毫秒或秒。

時(shí)間輪算法的主要思想是將時(shí)間分成若干個(gè)連續(xù)的間隔,每個(gè)間隔稱為一個(gè)刻度(tick)。每個(gè)刻度上都可以安排一個(gè)或多個(gè)任務(wù),這些任務(wù)都會(huì)在對(duì)應(yīng)的刻度觸發(fā)執(zhí)行。當(dāng)時(shí)間輪轉(zhuǎn)動(dòng)時(shí),當(dāng)前刻度會(huì)不斷變化,觸發(fā)相應(yīng)刻度上的任務(wù)執(zhí)行。

算法的基本步驟如下:

  1. 初始化時(shí)間輪:創(chuàng)建一個(gè)循環(huán)數(shù)組(或鏈表),每個(gè)元素代表一個(gè)刻度,每個(gè)刻度上存儲(chǔ)需要執(zhí)行的任務(wù)列表。

  2. 插入任務(wù):將需要定時(shí)執(zhí)行的任務(wù)插入到對(duì)應(yīng)的刻度上。如果任務(wù)需要在未來的幾個(gè)刻度后執(zhí)行,可以在相應(yīng)的刻度上存儲(chǔ)任務(wù)信息。

  3. 時(shí)間前進(jìn):隨著時(shí)間的推移,時(shí)間輪會(huì)不斷前進(jìn)。每次時(shí)間輪前進(jìn)一個(gè)刻度,當(dāng)前刻度會(huì)指向下一個(gè)刻度,這時(shí)候會(huì)觸發(fā)當(dāng)前刻度上存儲(chǔ)的所有任務(wù)執(zhí)行。

  4. 處理任務(wù):當(dāng)時(shí)間輪前進(jìn)時(shí),會(huì)觸發(fā)當(dāng)前刻度上存儲(chǔ)的任務(wù)執(zhí)行。這些任務(wù)可以是需要執(zhí)行的函數(shù)、事件通知等。

  5. 移除任務(wù):一旦任務(wù)被執(zhí)行,它可以從時(shí)間輪中移除。如果任務(wù)是重復(fù)性的,可以在執(zhí)行后重新插入到合適的刻度上,實(shí)現(xiàn)周期性執(zhí)行。

時(shí)間輪算法的優(yōu)勢在于它適用于需要精確時(shí)間控制的場景,同時(shí)具有較高的效率。然而,時(shí)間輪也有其局限性,例如在需要更高時(shí)間精度或動(dòng)態(tài)調(diào)整定時(shí)任務(wù)的情況下可能不太適用。

【技術(shù)驛站】分布式基礎(chǔ)與常見面試問題,s10 分布式,分布式

分層時(shí)間輪:

【技術(shù)驛站】分布式基礎(chǔ)與常見面試問題,s10 分布式,分布式

8.分布式痛點(diǎn)問題有哪些?

需要了解諸如秒殺搶購等場景的架構(gòu)實(shí)現(xiàn)方案,以及方案解決的痛點(diǎn)問題,這類問題往往需要先提煉痛點(diǎn)問題,再針對(duì)痛點(diǎn)問題做優(yōu)化。

分布式系統(tǒng)是由多臺(tái)計(jì)算機(jī)或服務(wù)器組成的系統(tǒng),它們通過網(wǎng)絡(luò)相互通信和協(xié)作,以完成復(fù)雜的任務(wù)。然而,分布式系統(tǒng)也面臨著許多痛點(diǎn)和挑戰(zhàn),以下是一些常見的問題:

  1. 通信延遲和帶寬限制: 分布式系統(tǒng)中的計(jì)算機(jī)之間需要進(jìn)行網(wǎng)絡(luò)通信,這會(huì)導(dǎo)致通信延遲和帶寬限制。這可能會(huì)影響系統(tǒng)的性能和響應(yīng)時(shí)間。

  2. 數(shù)據(jù)一致性: 在分布式系統(tǒng)中,多個(gè)節(jié)點(diǎn)可能并發(fā)地讀寫數(shù)據(jù),因此需要解決數(shù)據(jù)一致性的問題。維護(hù)一致性通常需要使用復(fù)雜的算法和協(xié)議,如分布式事務(wù)。

  3. 故障處理: 分布式系統(tǒng)中的節(jié)點(diǎn)可能會(huì)因?yàn)橛布收?、網(wǎng)絡(luò)問題或軟件錯(cuò)誤而失敗。因此,需要實(shí)現(xiàn)故障檢測和故障恢復(fù)機(jī)制,以確保系統(tǒng)的可用性和可靠性。

  4. 分布式存儲(chǔ): 數(shù)據(jù)的分布式存儲(chǔ)和管理是一個(gè)復(fù)雜的問題。需要考慮數(shù)據(jù)的分片、備份、恢復(fù)等方面,以確保數(shù)據(jù)的可靠性和可用性。

  5. 負(fù)載均衡: 在分布式系統(tǒng)中,需要確保各個(gè)節(jié)點(diǎn)之間的負(fù)載均衡,以避免某些節(jié)點(diǎn)過載,而其他節(jié)點(diǎn)空閑的情況。

  6. 安全性: 分布式系統(tǒng)需要保護(hù)數(shù)據(jù)的機(jī)密性和完整性,同時(shí)還需要考慮身份驗(yàn)證、授權(quán)和防止網(wǎng)絡(luò)攻擊等安全問題。

  7. 系統(tǒng)復(fù)雜性: 分布式系統(tǒng)往往比單一系統(tǒng)更加復(fù)雜,因?yàn)樯婕暗蕉鄠€(gè)節(jié)點(diǎn)、多個(gè)組件和多個(gè)網(wǎng)絡(luò)連接。這增加了開發(fā)、調(diào)試和維護(hù)的難度。

  8. 性能調(diào)優(yōu): 在分布式系統(tǒng)中,性能調(diào)優(yōu)是一個(gè)挑戰(zhàn),需要仔細(xì)設(shè)計(jì)和配置系統(tǒng),以確保在高負(fù)載時(shí)仍能提供良好的性能。

  9. 擴(kuò)展性: 隨著業(yè)務(wù)的增長,分布式系統(tǒng)可能需要擴(kuò)展,這要求系統(tǒng)具備良好的可擴(kuò)展性,能夠容易地添加新的節(jié)點(diǎn)或資源。

  10. 監(jiān)控和診斷: 對(duì)于分布式系統(tǒng),監(jiān)控和診斷問題變得更加復(fù)雜。需要使用專業(yè)的工具和技術(shù)來監(jiān)控系統(tǒng)的健康狀況,并快速定位和解決問題。

這些問題只是分布式系統(tǒng)中的一部分挑戰(zhàn),解決它們需要深入的技術(shù)知識(shí)和合適的工具和方法。因此,分布式系統(tǒng)的設(shè)計(jì)和管理是一項(xiàng)復(fù)雜而具有挑戰(zhàn)性的任務(wù)。

二.分布式事務(wù)

1.分布式事務(wù)解決方案

分布式事務(wù)是在分布式系統(tǒng)中處理跨多個(gè)資源、服務(wù)或數(shù)據(jù)庫的事務(wù)操作的問題。由于分布式系統(tǒng)的特性,確保一致性和可靠性變得更加復(fù)雜。為了解決這個(gè)問題,出現(xiàn)了多種分布式事務(wù)解決方案。以下是一些常見的分布式事務(wù)解決方案:

  1. 兩階段提交(Two-Phase Commit,2PC):在這種協(xié)議中,有一個(gè)協(xié)調(diào)者(Coordinator)和多個(gè)參與者(Participants)。在第一階段,協(xié)調(diào)者詢問所有參與者是否準(zhǔn)備好提交事務(wù)。如果所有參與者都準(zhǔn)備好了,協(xié)調(diào)者會(huì)發(fā)送一個(gè)提交請(qǐng)求,然后進(jìn)入第二階段。在第二階段,所有參與者會(huì)執(zhí)行實(shí)際的提交操作。如果有一個(gè)參與者出現(xiàn)問題,整個(gè)事務(wù)會(huì)回滾。

  2. 三階段提交(Three-Phase Commit,3PC):3PC 在 2PC 的基礎(chǔ)上增加了一個(gè)準(zhǔn)備階段,以減少某些情況下的阻塞和超時(shí)問題。在第一階段,協(xié)調(diào)者詢問所有參與者是否準(zhǔn)備好提交事務(wù)。在第二階段,協(xié)調(diào)者會(huì)告知所有參與者是否要提交或中止事務(wù)。在第三階段,所有參與者執(zhí)行實(shí)際的提交或回滾操作。

  3. XA 協(xié)議(Two-Phase Commit): XA 協(xié)議是一種傳統(tǒng)的分布式事務(wù)協(xié)議,它通過協(xié)調(diào)器(Coordinator)來確保分布式事務(wù)的一致性。在這個(gè)協(xié)議中,參與事務(wù)的各個(gè)資源管理器(Resource Manager)會(huì)向協(xié)調(diào)器報(bào)告事務(wù)的執(zhí)行狀態(tài),并在協(xié)調(diào)器的協(xié)調(diào)下執(zhí)行事務(wù)的提交或回滾。XA 協(xié)議具有強(qiáng)一致性,但也有較高的性能開銷和單點(diǎn)故障的風(fēng)險(xiǎn)。

  4. TCC(Try-Confirm-Cancel):TCC 是一種補(bǔ)償型的分布式事務(wù)解決方案,它將事務(wù)分為三個(gè)階段:嘗試(Try)、確認(rèn)(Confirm)和取消(Cancel)。在嘗試階段,參與者會(huì)嘗試執(zhí)行事務(wù)操作,但不會(huì)真正提交。在確認(rèn)階段,參與者確認(rèn)操作,并提交更改。如果有問題,可以在取消階段進(jìn)行回滾操作。

  5. Saga 模式:Saga 是一種長時(shí)間運(yùn)行的事務(wù),通過將大事務(wù)分解為一系列較小的子事務(wù)來處理。每個(gè)子事務(wù)負(fù)責(zé)自己的提交和回滾邏輯。如果一個(gè)子事務(wù)失敗,相應(yīng)的補(bǔ)償操作將會(huì)觸發(fā)。Saga 模式依賴于應(yīng)用程序的邏輯來處理一致性和回滾。

  6. 消息隊(duì)列:使用消息隊(duì)列作為分布式事務(wù)的一部分,將事務(wù)操作封裝為消息,并將其發(fā)送到隊(duì)列中。接收者在處理消息時(shí)執(zhí)行相應(yīng)的操作。如果操作失敗,可以通過消息重試機(jī)制或者定制的補(bǔ)償操作來處理。

  7. 基于時(shí)間戳的一致性:通過記錄操作的時(shí)間戳和狀態(tài)信息來實(shí)現(xiàn)一致性。參與者可以根據(jù)時(shí)間戳來判斷是否可以執(zhí)行操作,從而避免沖突。

  8. GTM(Global Transaction Manager): GTM 是一種集中式的全局事務(wù)管理器,它負(fù)責(zé)協(xié)調(diào)分布式事務(wù)的提交和回滾。不同于 Seata,GTM 通常將事務(wù)數(shù)據(jù)存儲(chǔ)在單獨(dú)的數(shù)據(jù)庫中,而不是嵌入到應(yīng)用的業(yè)務(wù)數(shù)據(jù)庫中。

每種解決方案都有其優(yōu)缺點(diǎn),具體的選擇要取決于應(yīng)用程序的需求、性能要求以及容忍的一致性級(jí)別。在實(shí)際應(yīng)用中,根據(jù)業(yè)務(wù)場景的不同,可能需要結(jié)合多種解決方案來實(shí)現(xiàn)分布式事務(wù)的一致性和可靠性。

2.如何解決 TCC 中懸掛問題?

TCC(Try-Confirm-Cancel)是一種用于分布式事務(wù)處理的模式,它將一個(gè)大的事務(wù)拆分為三個(gè)階段:嘗試(Try)、確認(rèn)(Confirm)和取消(Cancel)。每個(gè)階段對(duì)應(yīng)一個(gè)操作,而整個(gè)事務(wù)的一致性由這三個(gè)操作共同保證。TCC 模式能夠在分布式環(huán)境中實(shí)現(xiàn)較高的事務(wù)一致性和可靠性。

懸掛問題是 TCC 模式中的一個(gè)關(guān)鍵挑戰(zhàn),指的是在執(zhí)行過程中,一個(gè)分支事務(wù)的確認(rèn)操作成功,但由于某些原因?qū)е缕浜罄m(xù)的分支事務(wù)的確認(rèn)或取消操作無法繼續(xù)執(zhí)行。這可能會(huì)導(dǎo)致部分事務(wù)的狀態(tài)不一致,影響整個(gè)分布式事務(wù)的一致性。

以下是解決 TCC 中懸掛問題的一些方法:

  1. 超時(shí)機(jī)制: 在 TCC 模式中,可以為每個(gè)分支事務(wù)設(shè)置一個(gè)超時(shí)時(shí)間。如果一個(gè)分支事務(wù)的確認(rèn)或取消操作在超時(shí)時(shí)間內(nèi)未完成,系統(tǒng)可以自動(dòng)執(zhí)行一些補(bǔ)救操作,如執(zhí)行重試、回滾等,以確保事務(wù)的完整性。

  2. 事務(wù)狀態(tài)標(biāo)記: 在 TCC 模式中,可以為每個(gè)分支事務(wù)引入狀態(tài)標(biāo)記,表示事務(wù)的當(dāng)前狀態(tài)(嘗試、確認(rèn)、取消)。當(dāng)一個(gè)分支事務(wù)的確認(rèn)操作成功時(shí),將狀態(tài)標(biāo)記為“已確認(rèn)”;當(dāng)取消操作成功時(shí),將狀態(tài)標(biāo)記為“已取消”。系統(tǒng)可以定期掃描未完成的事務(wù),檢查狀態(tài)標(biāo)記,然后采取相應(yīng)的補(bǔ)救措施。

  3. 異步通知與補(bǔ)償: 當(dāng)一個(gè)分支事務(wù)的確認(rèn)操作成功時(shí),可以異步地通知其他相關(guān)的分支事務(wù)進(jìn)行確認(rèn)或取消操作。這樣即使一個(gè)分支事務(wù)的確認(rèn)操作懸掛,其他事務(wù)可以繼續(xù)執(zhí)行,然后系統(tǒng)可以通過補(bǔ)償操作來糾正狀態(tài)不一致的情況。

  4. 定時(shí)任務(wù): 引入定時(shí)任務(wù)來檢查未完成的事務(wù),發(fā)現(xiàn)懸掛事務(wù)并進(jìn)行處理。這可以是一個(gè)周期性的任務(wù),負(fù)責(zé)掃描系統(tǒng)中的事務(wù)狀態(tài)并執(zhí)行相應(yīng)的補(bǔ)救操作。

  5. 日志和審計(jì): 在 TCC 模式中,記錄詳細(xì)的事務(wù)日志和審計(jì)信息。這些信息可以幫助系統(tǒng)管理員或開發(fā)人員識(shí)別和解決懸掛問題,以及執(zhí)行必要的恢復(fù)操作。

解決 TCC 中懸掛問題需要結(jié)合超時(shí)機(jī)制、事務(wù)狀態(tài)標(biāo)記、異步通知與補(bǔ)償、定時(shí)任務(wù)以及日志審計(jì)等方法,以確保在分布式環(huán)境中維護(hù)事務(wù)的一致性和可靠性。

3.如何回滾 MQ 和 Redis?

在分布式事務(wù)場景中,涉及到 Redis 和消息隊(duì)列(MQ)的回滾是一個(gè)復(fù)雜的問題,因?yàn)檫@涉及到多個(gè)不同的存儲(chǔ)系統(tǒng)之間的一致性?;貪L策略可以根據(jù)具體的業(yè)務(wù)需求和技術(shù)選型來確定。以下是關(guān)于如何處理分布式事務(wù)中的 Redis 和 MQ 回滾的一些一般策略:

Redis 回滾:

Redis 本身不支持傳統(tǒng)的分布式事務(wù),但您可以通過以下方法來實(shí)現(xiàn)一定程度的回滾:

  1. 操作日志: 在進(jìn)行分布式事務(wù)操作時(shí),將涉及到的 Redis 操作記錄到一個(gè)日志中。如果事務(wù)的后續(xù)步驟發(fā)生錯(cuò)誤,您可以根據(jù)日志信息進(jìn)行回滾,即撤銷已執(zhí)行的操作。

  2. 備份和恢復(fù): 在事務(wù)開始之前,可以將涉及到的 Redis 數(shù)據(jù)進(jìn)行備份。如果事務(wù)失敗,您可以使用備份數(shù)據(jù)來恢復(fù)到事務(wù)之前的狀態(tài)。

  3. 使用 MULTI/EXEC 命令: Redis 提供了MULTIEXEC命令來創(chuàng)建事務(wù)塊。您可以在事務(wù)塊中執(zhí)行多個(gè)命令,并在執(zhí)行失敗時(shí)使用DISCARD命令來取消事務(wù),或者使用EXEC命令提交事務(wù)。如果事務(wù)失敗,您可以使用DISCARD來回滾之前的操作。

消息隊(duì)列(MQ)回滾:

在消息隊(duì)列中處理回滾通常需要考慮以下策略:

  1. 事務(wù)消息: 一些消息隊(duì)列系統(tǒng)(如 Apache Kafka)支持事務(wù)消息。您可以將數(shù)據(jù)庫操作和消息發(fā)送放在同一個(gè)事務(wù)中,然后在提交事務(wù)時(shí)同時(shí)提交消息和數(shù)據(jù)庫操作,或者在回滾時(shí)同時(shí)進(jìn)行回滾。

  2. 消息狀態(tài)管理: 如果消息隊(duì)列不支持事務(wù)消息,您可以在數(shù)據(jù)庫中維護(hù)消息的狀態(tài)。當(dāng)事務(wù)開始時(shí),將消息狀態(tài)設(shè)置為“待處理”。如果事務(wù)成功,則將消息狀態(tài)設(shè)置為“已處理”。如果事務(wù)失敗,則將消息狀態(tài)設(shè)置為“待處理”,以便稍后重新處理。

  3. 死信隊(duì)列: 一些消息隊(duì)列支持死信隊(duì)列,用于處理處理失敗的消息。您可以將處理失敗的消息移動(dòng)到死信隊(duì)列,并在稍后處理。

需要注意的是,處理分布式事務(wù)回滾是一個(gè)復(fù)雜的問題,需要根據(jù)具體的業(yè)務(wù)需求和技術(shù)選型來選擇合適的回滾策略。在實(shí)際應(yīng)用中,您可能需要結(jié)合數(shù)據(jù)庫事務(wù)和消息隊(duì)列的特性,設(shè)計(jì)適合自己應(yīng)用場景的回滾機(jī)制。

4.最終一致性

分布式最終一致性是指在分布式系統(tǒng)中,盡管在一段時(shí)間內(nèi)數(shù)據(jù)副本之間可能存在不一致的狀態(tài),但經(jīng)過一段時(shí)間后,系統(tǒng)最終會(huì)達(dá)到一致的狀態(tài)。這種一致性模型允許在分布式系統(tǒng)中的各個(gè)節(jié)點(diǎn)之間存在一段時(shí)間的數(shù)據(jù)不一致,但最終數(shù)據(jù)會(huì)收斂到一致狀態(tài),從而保證系統(tǒng)的可用性和可靠性。

分布式最終一致性通常涉及以下幾種策略和模式:

  1. 異步復(fù)制: 分布式系統(tǒng)中的數(shù)據(jù)副本可能會(huì)異步地復(fù)制到其他節(jié)點(diǎn),導(dǎo)致在某個(gè)時(shí)間點(diǎn)上節(jié)點(diǎn)之間的數(shù)據(jù)狀態(tài)不一致。但隨著時(shí)間的推移,副本會(huì)趨于一致。

  2. 事件驅(qū)動(dòng): 將系統(tǒng)中的數(shù)據(jù)變更表示為事件,并通過消息隊(duì)列將這些事件傳播到其他節(jié)點(diǎn)。每個(gè)節(jié)點(diǎn)按照接收到的事件按序處理數(shù)據(jù)更新,最終達(dá)到一致狀態(tài)。

  3. 版本控制: 為每個(gè)數(shù)據(jù)副本引入版本控制,記錄每次數(shù)據(jù)變更,然后通過協(xié)調(diào)機(jī)制在副本之間傳播這些變更,確保數(shù)據(jù)最終一致。

  4. 定時(shí)同步: 定期在各個(gè)節(jié)點(diǎn)之間進(jìn)行數(shù)據(jù)同步,通過定時(shí)任務(wù)將數(shù)據(jù)從一個(gè)節(jié)點(diǎn)同步到其他節(jié)點(diǎn),逐步實(shí)現(xiàn)數(shù)據(jù)的一致性。

  5. 基于時(shí)間戳: 在數(shù)據(jù)副本上附加時(shí)間戳,然后使用時(shí)間戳來解決數(shù)據(jù)沖突和合并問題,使數(shù)據(jù)逐漸趨于一致。

  6. 基于版本號(hào): 為每個(gè)數(shù)據(jù)副本引入版本號(hào),允許數(shù)據(jù)更新時(shí)指定版本號(hào),協(xié)調(diào)機(jī)制根據(jù)版本號(hào)解決并發(fā)寫入的問題。

分布式最終一致性是一種折衷方案,權(quán)衡了分布式系統(tǒng)中的性能、可用性和數(shù)據(jù)一致性。這種一致性模型適用于一些弱一致性要求,例如社交網(wǎng)絡(luò)的點(diǎn)贊數(shù)、閱讀數(shù)等數(shù)據(jù),在短時(shí)間內(nèi)的不一致并不會(huì)嚴(yán)重影響用戶體驗(yàn)。然而,對(duì)于某些領(lǐng)域,如金融、電子商務(wù)等,強(qiáng)一致性可能更為重要,因此需要根據(jù)具體的業(yè)務(wù)需求選擇合適的一致性模型。

5.分布式 session 共享問題

分布式 Session 共享是指在一個(gè)分布式系統(tǒng)中,多個(gè)服務(wù)或節(jié)點(diǎn)需要共享用戶會(huì)話(Session)數(shù)據(jù),以保持用戶在不同請(qǐng)求之間的狀態(tài)和登錄信息。這種情況常見于使用負(fù)載均衡、微服務(wù)架構(gòu)或多節(jié)點(diǎn)的系統(tǒng)中。然而,實(shí)現(xiàn)分布式 Session 共享可能涉及一些挑戰(zhàn)和考慮事項(xiàng)。

以下是與分布式 Session 共享相關(guān)的問題和解決方案:

問題:

  1. 一致性問題: 各節(jié)點(diǎn)之間的數(shù)據(jù)一致性可能受到網(wǎng)絡(luò)延遲和故障的影響,可能會(huì)導(dǎo)致不同節(jié)點(diǎn)上的 Session 數(shù)據(jù)不一致。

  2. 性能問題: 分布式 Session 共享可能涉及跨網(wǎng)絡(luò)的數(shù)據(jù)傳輸,可能會(huì)對(duì)性能產(chǎn)生一定影響。

  3. Session 管理問題: 如何有效管理 Session 的過期和清理是一個(gè)挑戰(zhàn),特別是在分布式環(huán)境中。

解決方案:

  1. 共享存儲(chǔ): 使用共享存儲(chǔ)(如數(shù)據(jù)庫、緩存系統(tǒng))作為 Session 存儲(chǔ)的后端,以確保各節(jié)點(diǎn)可以訪問和更新相同的 Session 數(shù)據(jù)。這種方式需要考慮一致性問題,如使用分布式鎖來保護(hù) Session 數(shù)據(jù)的更新。

  2. 無狀態(tài)服務(wù): 將 Session 數(shù)據(jù)從應(yīng)用層移至客戶端或無狀態(tài)的服務(wù),以避免 Session 共享的復(fù)雜性。使用 Token 驗(yàn)證可以在每個(gè)請(qǐng)求中傳遞用戶身份和狀態(tài)信息,而不依賴于服務(wù)器端的 Session 數(shù)據(jù)。

  3. 分布式緩存: 使用分布式緩存系統(tǒng),如 Redis,來存儲(chǔ) Session 數(shù)據(jù)。這可以提高讀寫性能并提供一致性保證。同時(shí),要關(guān)注緩存的失效策略。

  4. 一致性算法: 對(duì)于需要強(qiáng)一致性的場景,可以使用一致性算法(如 Paxos、Raft)來確保各節(jié)點(diǎn)上的 Session 數(shù)據(jù)保持一致。

  5. 版本控制: 在 Session 數(shù)據(jù)中引入版本控制,以便在更新時(shí)檢測到?jīng)_突并解決。

  6. 定時(shí)清理: 使用定時(shí)任務(wù)來清理過期的 Session 數(shù)據(jù),以避免存儲(chǔ)占用過多空間。

  7. 分布式事務(wù): 在需要的情況下,使用分布式事務(wù)來確保 Session 數(shù)據(jù)的一致性。

  8. 負(fù)載均衡策略: 在負(fù)載均衡時(shí),盡量確保同一用戶的請(qǐng)求被路由到相同的節(jié)點(diǎn),以減少 Session 數(shù)據(jù)的遷移。

無論選擇哪種解決方案,都需要根據(jù)具體場景和需求權(quán)衡不同的權(quán)衡,綜合考慮一致性、性能、可靠性等因素。

6.注冊(cè)中心腦裂問題?

從腦裂問題剖析 Nacos&Eureka&Zookeeper 架構(gòu)

Nacos、Eureka 和 Zookeeper 都是用于服務(wù)注冊(cè)與發(fā)現(xiàn)、配置管理等分布式系統(tǒng)領(lǐng)域的重要工具或框架。下面將分析這三者在面臨腦裂問題時(shí)的架構(gòu)特點(diǎn)和應(yīng)對(duì)策略。

Nacos:
Nacos 是一個(gè)全面的服務(wù)發(fā)現(xiàn)、配置管理和服務(wù)管理平臺(tái)。它的架構(gòu)和應(yīng)對(duì)腦裂問題的方式如下:

  • 架構(gòu): Nacos 采用集群架構(gòu),由多個(gè)節(jié)點(diǎn)組成。其中,有一個(gè)或多個(gè)節(jié)點(diǎn)被選為主節(jié)點(diǎn),負(fù)責(zé)協(xié)調(diào)服務(wù)注冊(cè)、配置管理等任務(wù)。

  • 應(yīng)對(duì)策略: Nacos 使用心跳機(jī)制來監(jiān)測節(jié)點(diǎn)的健康狀態(tài)。如果一個(gè)節(jié)點(diǎn)被認(rèn)為不健康,主節(jié)點(diǎn)可以將其標(biāo)記為不可用,從而避免不一致性。Nacos 也支持使用多數(shù)投票(Quorum)來進(jìn)行決策,確保操作的可靠性。

Eureka:
Eureka 是由 Netflix 開發(fā)的服務(wù)注冊(cè)與發(fā)現(xiàn)系統(tǒng)。其架構(gòu)和應(yīng)對(duì)腦裂問題的方式如下:

  • 架構(gòu): Eureka 由多個(gè)相互獨(dú)立的節(jié)點(diǎn)組成,每個(gè)節(jié)點(diǎn)都可以扮演服務(wù)器和客戶端角色。每個(gè)節(jié)點(diǎn)都會(huì)維護(hù)一個(gè)服務(wù)注冊(cè)表,記錄可用的服務(wù)實(shí)例。

  • 應(yīng)對(duì)策略: Eureka 使用心跳和定時(shí)任務(wù)來監(jiān)測節(jié)點(diǎn)和服務(wù)實(shí)例的狀態(tài)。當(dāng)一個(gè)節(jié)點(diǎn)或?qū)嵗徽J(rèn)為不可用時(shí),其他節(jié)點(diǎn)會(huì)進(jìn)行剔除或標(biāo)記。Eureka 在節(jié)點(diǎn)之間的通信中引入了"自我保護(hù)模式",在網(wǎng)絡(luò)分區(qū)等情況下,可以避免錯(cuò)誤地移除健康實(shí)例。

Zookeeper:
Zookeeper 是一個(gè)分布式協(xié)調(diào)服務(wù),可用于服務(wù)發(fā)現(xiàn)、配置管理等。其架構(gòu)和應(yīng)對(duì)腦裂問題的方式如下:

  • 架構(gòu): Zookeeper 使用集群架構(gòu),每個(gè)節(jié)點(diǎn)都知道其他節(jié)點(diǎn)的狀態(tài)。集群中的節(jié)點(diǎn)會(huì)選舉一個(gè)主節(jié)點(diǎn),其他節(jié)點(diǎn)作為從節(jié)點(diǎn)。

  • 應(yīng)對(duì)策略: Zookeeper 使用心跳機(jī)制和選主算法來監(jiān)測節(jié)點(diǎn)的狀態(tài)和選舉主節(jié)點(diǎn)。在腦裂問題中,如果網(wǎng)絡(luò)分區(qū)導(dǎo)致主節(jié)點(diǎn)和從節(jié)點(diǎn)無法通信,Zookeeper 的主從選舉算法可以確保只有一個(gè)主節(jié)點(diǎn)被選出,從而避免數(shù)據(jù)的不一致性。

總體來說,Nacos、Eureka 和 Zookeeper 都考慮了腦裂問題,并采用了不同的機(jī)制來應(yīng)對(duì),如心跳監(jiān)測、選主算法、多數(shù)投票等。無論選擇哪個(gè)工具或框架,都需要根據(jù)實(shí)際需求、性能要求和可靠性等因素來進(jìn)行選擇和配置。

三.分布式鎖

1.分布式鎖與單機(jī)鎖的區(qū)別?

分布式鎖和單機(jī)鎖之間有幾個(gè)關(guān)鍵區(qū)別,這些區(qū)別主要涉及到鎖的范圍、性能、可用性和實(shí)現(xiàn)復(fù)雜性等方面:

  1. 鎖的范圍

    • 單機(jī)鎖:單機(jī)鎖是在單個(gè)進(jìn)程或單個(gè)計(jì)算機(jī)上的線程之間使用的鎖。它們適用于單個(gè)應(yīng)用程序或單個(gè)進(jìn)程內(nèi)的并發(fā)控制。

    • 分布式鎖:分布式鎖用于在分布式系統(tǒng)中的多個(gè)進(jìn)程或計(jì)算機(jī)之間進(jìn)行并發(fā)控制。它們用于協(xié)調(diào)不同計(jì)算機(jī)上的線程,以確保在整個(gè)分布式系統(tǒng)中的互斥操作。

  2. 性能

    • 單機(jī)鎖:單機(jī)鎖通常比分布式鎖性能更高,因?yàn)樗鼈儾簧婕熬W(wǎng)絡(luò)通信和跨進(jìn)程的同步。單機(jī)鎖只需要在單個(gè)進(jìn)程內(nèi)部完成,開銷相對(duì)較低。

    • 分布式鎖:分布式鎖涉及到網(wǎng)絡(luò)通信,因此通常比單機(jī)鎖更消耗資源和時(shí)間。分布式鎖的性能通常不如單機(jī)鎖,但在分布式環(huán)境中是必要的。

  3. 可用性

    • 單機(jī)鎖:單機(jī)鎖的可用性受限于單個(gè)進(jìn)程或計(jì)算機(jī)的可用性。如果進(jìn)程或計(jì)算機(jī)崩潰或不可用,那么與之關(guān)聯(lián)的單機(jī)鎖也將不可用。

    • 分布式鎖:分布式鎖可以提高可用性,因?yàn)樗鼈兛梢栽诙鄠€(gè)計(jì)算機(jī)上復(fù)制。即使某個(gè)計(jì)算機(jī)或進(jìn)程不可用,其他計(jì)算機(jī)上的鎖仍然可以正常運(yùn)行。

  4. 實(shí)現(xiàn)復(fù)雜性

    • 單機(jī)鎖:單機(jī)鎖的實(shí)現(xiàn)通常較簡單,可以使用內(nèi)置的 Java 鎖(如synchronized)或標(biāo)準(zhǔn)庫中的鎖來輕松實(shí)現(xiàn)。

    • 分布式鎖:分布式鎖的實(shí)現(xiàn)較復(fù)雜,需要考慮網(wǎng)絡(luò)通信、故障恢復(fù)、一致性等因素。常見的分布式鎖實(shí)現(xiàn)包括基于數(shù)據(jù)庫、緩存、ZooKeeper 等技術(shù),這些實(shí)現(xiàn)通常需要更多的工程和設(shè)計(jì)考慮。

分布式鎖和單機(jī)鎖之間的主要區(qū)別在于它們的范圍、性能、可用性和實(shí)現(xiàn)復(fù)雜性。選擇哪種類型的鎖取決于您的應(yīng)用程序需求和環(huán)境。在分布式系統(tǒng)中,使用分布式鎖可以確保多個(gè)進(jìn)程或計(jì)算機(jī)之間的數(shù)據(jù)一致性和互斥操作,但需要額外的開銷和復(fù)雜性。在單機(jī)環(huán)境中,通常使用單機(jī)鎖足以滿足并發(fā)控制的需求。

2.分布式鎖的特征?

  • 高性能
  • 可重入
  • 線程互斥

分布式鎖是用于在分布式系統(tǒng)中實(shí)現(xiàn)同步和互斥操作的一種機(jī)制。它具有以下特征和屬性:

  1. 互斥性(Mutual Exclusion):分布式鎖確保在任何給定時(shí)間點(diǎn)只有一個(gè)客戶端或進(jìn)程可以持有鎖。這意味著一旦一個(gè)客戶端成功獲取鎖,其他客戶端將被阻止或排隊(duì),直到鎖被釋放。

  2. 可重入性(Reentrancy):分布式鎖通常支持可重入性,允許同一個(gè)客戶端多次獲取同一個(gè)鎖,而不會(huì)發(fā)生死鎖。每次獲取鎖后,鎖計(jì)數(shù)器會(huì)遞增,而每次釋放鎖后,鎖計(jì)數(shù)器會(huì)遞減,直到為零才能完全釋放鎖。

  3. 超時(shí)機(jī)制(Timeout):分布式鎖通常支持超時(shí)機(jī)制,允許客戶端設(shè)置獲取鎖的超時(shí)時(shí)間。如果在指定的超時(shí)時(shí)間內(nèi)無法獲取鎖,客戶端可以選擇放棄或重試。

  4. 阻塞和非阻塞模式:分布式鎖通常支持阻塞和非阻塞兩種模式。在阻塞模式下,如果無法獲取鎖,客戶端將被阻塞,直到鎖可用。在非阻塞模式下,如果無法獲取鎖,客戶端會(huì)立即返回,而不會(huì)阻塞。

  5. 高可用性和容錯(cuò)性:分布式鎖通常具有高可用性和容錯(cuò)性。它們可以在多個(gè)節(jié)點(diǎn)上部署,以確保即使某些節(jié)點(diǎn)或資源不可用,鎖仍然可以正常工作。

  6. 自動(dòng)釋放(Automatic Release):分布式鎖通常支持自動(dòng)釋放機(jī)制,以防止鎖被長時(shí)間持有。客戶端通常需要在一段時(shí)間后自動(dòng)釋放鎖,以防止鎖被意外持有。

  7. 實(shí)現(xiàn)方式多樣:有多種方式可以實(shí)現(xiàn)分布式鎖,包括基于數(shù)據(jù)庫、緩存、ZooKeeper、Redis 等技術(shù)。每種實(shí)現(xiàn)方式都有其自身的特點(diǎn)和適用場景。

  8. 一致性保證:分布式鎖通常會(huì)考慮數(shù)據(jù)一致性的問題,以確保在多個(gè)節(jié)點(diǎn)上的鎖操作是一致的。這包括使用分布式事務(wù)或一致性協(xié)議來保證鎖的正確性。

分布式鎖是一種用于分布式系統(tǒng)中的并發(fā)控制的重要機(jī)制,具有互斥性、可重入性、超時(shí)機(jī)制、阻塞和非阻塞模式、高可用性、自動(dòng)釋放等特征。選擇合適的分布式鎖實(shí)現(xiàn)方式和配置參數(shù)取決于具體的應(yīng)用場景和需求。

3.分布式鎖的種類

分布式鎖是用于在分布式系統(tǒng)中實(shí)現(xiàn)同步和互斥操作的一種機(jī)制。有多種分布式鎖的實(shí)現(xiàn)方式,以下是一些常見的分布式鎖種類:

  1. 基于數(shù)據(jù)庫的分布式鎖:可以使用數(shù)據(jù)庫的事務(wù)來實(shí)現(xiàn)分布式鎖。在這種方式下,數(shù)據(jù)庫表中的某一行記錄代表鎖,通過數(shù)據(jù)庫的事務(wù)來對(duì)這行記錄進(jìn)行加鎖和釋放鎖操作。通常使用數(shù)據(jù)庫的行級(jí)鎖來確保互斥性。

  2. 基于緩存的分布式鎖:使用分布式緩存系統(tǒng)如 Redis 或 Memcached 來實(shí)現(xiàn)分布式鎖。在這種方式下,鎖的狀態(tài)通常是存儲(chǔ)在緩存中的,可以使用緩存的原子操作來實(shí)現(xiàn)鎖的獲取和釋放。

  3. 基于 ZooKeeper 的分布式鎖:ZooKeeper 是一個(gè)分布式協(xié)調(diào)服務(wù),可以用于實(shí)現(xiàn)分布式鎖。通過在 ZooKeeper 中創(chuàng)建臨時(shí)節(jié)點(diǎn)來表示鎖的狀態(tài),可以確保在同一時(shí)刻只有一個(gè)客戶端能夠擁有這個(gè)鎖。

  4. 基于文件系統(tǒng)的分布式鎖:使用共享文件系統(tǒng)(如 NFS)來實(shí)現(xiàn)分布式鎖。在這種方式下,鎖的狀態(tài)通常是存儲(chǔ)在共享文件系統(tǒng)上的文件或目錄,通過文件系統(tǒng)的鎖機(jī)制來實(shí)現(xiàn)互斥操作。

  5. 基于分布式算法的分布式鎖:一些分布式算法(如基于 Paxos 或 Raft 的一致性算法)也可以用于實(shí)現(xiàn)分布式鎖。這些算法可以確保多個(gè)節(jié)點(diǎn)之間的數(shù)據(jù)一致性,從而實(shí)現(xiàn)分布式鎖。

  6. 基于數(shù)據(jù)庫樂觀鎖的分布式鎖:使用數(shù)據(jù)庫中的樂觀鎖機(jī)制(例如,版本號(hào)或時(shí)間戳)來實(shí)現(xiàn)分布式鎖??蛻舳藝L試通過更新數(shù)據(jù)庫記錄來獲取鎖,并檢查是否成功。這種方式下,需要處理沖突和重試。

  7. 基于消息隊(duì)列的分布式鎖:使用分布式消息隊(duì)列來實(shí)現(xiàn)分布式鎖。鎖的狀態(tài)可以作為消息隊(duì)列中的消息來表示,通過消息隊(duì)列的原子性操作來實(shí)現(xiàn)鎖的獲取和釋放。

每種分布式鎖實(shí)現(xiàn)方式都有其優(yōu)點(diǎn)和局限性,選擇適合您應(yīng)用需求的方式取決于多個(gè)因素,包括性能、復(fù)雜性、可用性和一致性要求等。不同的應(yīng)用場景可能需要不同類型的分布式鎖。

4.分布式鎖的實(shí)現(xiàn)?

使用 ReentrantLock 的 tryLock 方法進(jìn)行加鎖,ReentrantLock 可以解決重入鎖的問題,進(jìn)而解決續(xù)期的問題。

@Target({ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Component
public @interface RedisLock {
    String value() default "";

    @AliasFor("key")
    String name() default "";

    @AliasFor("name")
    String key() default "";

    long expire() default 30000; // 默認(rèn)鎖的過期時(shí)間,單位毫秒

    long timeout() default 0; // 默認(rèn)獲取鎖的超時(shí)時(shí)間,單位毫秒

    boolean fair() default false; // 是否使用公平鎖
}
@Aspect
@Component
@Order(1)  // 設(shè)置切面優(yōu)先級(jí)
public class RedisLockAspect {

    private final StringRedisTemplate redisTemplate;

    public RedisLockAspect(StringRedisTemplate redisTemplate) {
        this.redisTemplate = redisTemplate;
    }

    @Around("@annotation(redisLock)")
    public Object doWithLock(ProceedingJoinPoint joinPoint, RedisLock redisLock) throws Throwable {
        String lockKey = getLockKey(redisLock);
        Lock lock = new ReentrantLock(redisLock.fair());
        boolean locked = false;

        try {
            locked = lock.tryLock(redisLock.timeout(), TimeUnit.MILLISECONDS);
            if (locked) {
                if (StringUtils.hasText(lockKey)) {
                    redisTemplate.opsForValue().set(lockKey, "locked", redisLock.expire(), TimeUnit.MILLISECONDS);
                }
                return joinPoint.proceed();
            } else {
                throw new RuntimeException("Failed to acquire lock.");
            }
        } finally {
            if (locked) {
                lock.unlock();
                if (StringUtils.hasText(lockKey)) {
                    redisTemplate.delete(lockKey);
                }
            }
        }
    }

    private String getLockKey(RedisLock redisLock) {
        String lockKey = redisLock.key();
        if (!StringUtils.hasText(lockKey)) {
            lockKey = redisLock.name();
        }
        return lockKey;
    }
}

5.分布式鎖的續(xù)期?

分布式鎖的續(xù)期是指在某個(gè)持有分布式鎖的客戶端需要延長鎖的持有時(shí)間,以防止在執(zhí)行長時(shí)間操作時(shí)鎖過期導(dǎo)致其他客戶端獲取鎖并干擾正在進(jìn)行的操作。續(xù)期分布式鎖通常涉及以下步驟:

  1. 獲取鎖: 客戶端通過某種機(jī)制(如 Redis 的SETNX命令)嘗試獲取分布式鎖。如果成功獲取鎖,那么鎖的持有者就是該客戶端。

  2. 設(shè)置鎖的過期時(shí)間: 在獲取鎖成功后,客戶端可以通過設(shè)置鎖的過期時(shí)間來確保即使在某些情況下鎖沒有被顯式釋放,也能在一定時(shí)間后自動(dòng)釋放。這通常使用 Redis 的EXPIRE命令或類似的機(jī)制來完成。

  3. 續(xù)期: 在客戶端持有鎖的過程中,如果該客戶端需要繼續(xù)執(zhí)行一些耗時(shí)的操作,可以選擇續(xù)期鎖的持有時(shí)間。續(xù)期通常涉及以下步驟:

    • 客戶端在執(zhí)行操作時(shí),可以定期檢查自己是否還持有鎖,以避免過期。
    • 在續(xù)期時(shí),客戶端可以通過更新鎖的過期時(shí)間,將鎖的持有時(shí)間延長。

    在 Redis 中,可以使用類似于EXPIRE命令的PEXPIRE(以毫秒為單位的過期時(shí)間)來更新鍵的過期時(shí)間,從而實(shí)現(xiàn)鎖的續(xù)期。

  4. 釋放鎖: 當(dāng)客戶端操作完成后,它可以通過刪除鎖的方式來釋放鎖,或者讓鎖在過期時(shí)自動(dòng)釋放。

需要注意的是,分布式鎖的續(xù)期需要小心處理,以避免潛在的問題,如死鎖或者某個(gè)客戶端長時(shí)間持有鎖而不釋放。續(xù)期應(yīng)該在合理的時(shí)間間隔內(nèi)進(jìn)行,以確保其他等待獲取鎖的客戶端有機(jī)會(huì)獲得鎖資源。同時(shí),續(xù)期操作可能會(huì)在某些極端情況下失敗,如網(wǎng)絡(luò)問題或鎖服務(wù)的故障,因此需要考慮異常情況的處理。

6.Redisson 分布式鎖公平鎖

在代碼和 Lua 中怎么做到加鎖、解鎖、鎖續(xù)期

Redisson 是一個(gè)基于 Redis 的分布式鎖框架,它提供了多種類型的分布式鎖,包括公平鎖。在 Redisson 中,公平鎖的實(shí)現(xiàn)會(huì)涉及到以下幾個(gè)關(guān)鍵概念:鎖的持有者、鎖的等待隊(duì)列、鎖的釋放與續(xù)期。下面是使用 Redisson 實(shí)現(xiàn)公平鎖的一些關(guān)鍵細(xì)節(jié):

1. 加鎖

使用 Redisson 實(shí)現(xiàn)公平鎖時(shí),可以通過以下方式加鎖:

RLock fairLock = redisson.getFairLock("myFairLock");
fairLock.lock();
try {
    // 在這里執(zhí)行被鎖保護(hù)的代碼
} finally {
    fairLock.unlock();
}

這段代碼首先獲取一個(gè)公平鎖對(duì)象fairLock,然后調(diào)用lock()方法來獲取鎖。如果鎖當(dāng)前不可用,線程將被阻塞,直到鎖可用為止。一旦鎖可用,線程就可以執(zhí)行被鎖保護(hù)的代碼。請(qǐng)注意,使用try-finally塊確保鎖一定會(huì)被釋放,以防止死鎖。

2. 解鎖

解鎖操作是自動(dòng)的,當(dāng)調(diào)用unlock()方法時(shí),Redisson 會(huì)自動(dòng)釋放鎖。在上面的示例中,try-finally塊確保了在退出鎖保護(hù)的代碼塊后一定會(huì)釋放鎖。

3. 鎖續(xù)期

Redisson 會(huì)自動(dòng)續(xù)期鎖的持有時(shí)間。如果一個(gè)線程在持有鎖時(shí)出現(xiàn)了長時(shí)間的阻塞(例如,由于執(zhí)行耗時(shí)的操作),Redisson 會(huì)定期為該鎖續(xù)期,以確保其他等待鎖的線程不會(huì)因?yàn)殒i的過期而獲取到鎖。

4. 實(shí)現(xiàn)細(xì)節(jié)

Redisson 在底層使用了 Redis 的原子操作來實(shí)現(xiàn)鎖。它會(huì)使用 Lua 腳本來確保鎖的原子性操作。以下是一些細(xì)節(jié):

  • 當(dāng)一個(gè)線程嘗試獲取鎖時(shí),Redisson 會(huì)生成一個(gè)唯一的標(biāo)識(shí)符(例如,UUID),然后將該標(biāo)識(shí)符與鎖關(guān)聯(lián),并將其設(shè)置為鎖的值。
  • 如果鎖已經(jīng)被其他線程持有,嘗試獲取鎖的線程會(huì)將自己加入到鎖的等待隊(duì)列中。
  • 當(dāng)一個(gè)線程釋放鎖時(shí),Redisson 會(huì)檢查鎖的值是否與其標(biāo)識(shí)符匹配,如果匹配,則釋放鎖;否則,鎖將保持不變,等待持有鎖的線程繼續(xù)執(zhí)行。
  • 續(xù)期操作是通過定期更新鎖的過期時(shí)間來實(shí)現(xiàn)的。

使用 Redisson 可以很容易地實(shí)現(xiàn)分布式公平鎖,Redisson 會(huì)處理鎖的加鎖、解鎖和鎖的續(xù)期等細(xì)節(jié),使得在分布式環(huán)境中實(shí)現(xiàn)鎖變得相對(duì)簡單。在底層,Redisson 利用了 Redis 強(qiáng)大的原子操作能力來確保鎖的正確性和一致性。

7.Redisson 分布式鎖非公平鎖

Redisson 支持非公平鎖(Unfair Lock),與公平鎖相比,非公平鎖允許多個(gè)線程爭奪鎖的時(shí)候,不一定按照先來后到的順序獲取鎖,而是按照競爭的速度獲取鎖。以下是使用 Redisson 實(shí)現(xiàn)非公平鎖的一些關(guān)鍵細(xì)節(jié):

1. 加鎖

使用 Redisson 實(shí)現(xiàn)非公平鎖時(shí),可以通過以下方式加鎖:

RLock unfairLock = redisson.getLock("myUnfairLock");
unfairLock.lock();
try {
    // 在這里執(zhí)行被鎖保護(hù)的代碼
} finally {
    unfairLock.unlock();
}

這段代碼首先獲取一個(gè)非公平鎖對(duì)象unfairLock,然后調(diào)用lock()方法來獲取鎖。與公平鎖不同,非公平鎖不會(huì)保證等待鎖的線程按照先后順序獲取鎖,而是允許競爭速度更快的線程更早地獲取到鎖。

2. 解鎖

解鎖操作與公平鎖一樣,也是自動(dòng)的。當(dāng)調(diào)用unlock()方法時(shí),Redisson 會(huì)自動(dòng)釋放鎖。

3. 實(shí)現(xiàn)細(xì)節(jié)

Redisson 在底層使用了 Redis 的原子操作來實(shí)現(xiàn)非公平鎖。以下是一些細(xì)節(jié):

  • 當(dāng)一個(gè)線程嘗試獲取鎖時(shí),Redisson 會(huì)使用SET命令來嘗試將鎖的值設(shè)置為一個(gè)唯一的標(biāo)識(shí)符(例如,UUID)。
  • 如果鎖已經(jīng)被其他線程持有,嘗試獲取鎖的線程將無法成功,但它不會(huì)進(jìn)入等待隊(duì)列,而是會(huì)立即返回。
  • 當(dāng)一個(gè)線程釋放鎖時(shí),Redisson 會(huì)檢查鎖的值是否與其標(biāo)識(shí)符匹配,如果匹配,則釋放鎖;否則,鎖將保持不變。

Redisson 可以輕松實(shí)現(xiàn)分布式非公平鎖,它使用 Redis 的原子操作來確保鎖的正確性和一致性。與公平鎖不同,非公平鎖允許多個(gè)線程競爭鎖,并且允許競爭速度更快的線程更早地獲取鎖,適用于某些場景下對(duì)性能更高要求的情況。

8.Redisson 實(shí)現(xiàn)分布式鎖讀寫鎖

Redisson 是一個(gè)基于 Redis 的分布式 Java 對(duì)象服務(wù)框架,它提供了分布式鎖的實(shí)現(xiàn),包括讀寫鎖。下面是使用 Redisson 實(shí)現(xiàn)分布式讀寫鎖的一些關(guān)鍵細(xì)節(jié):

1. 讀寫鎖的加鎖

Redisson 提供了RLock接口用于表示分布式鎖,其中包括讀寫鎖。要使用讀寫鎖,可以通過以下方式加鎖:

RReadWriteLock rwLock = redisson.getReadWriteLock("myReadWriteLock");
RLock readLock = rwLock.readLock();
RLock writeLock = rwLock.writeLock();

// 獲取讀鎖
readLock.lock();
try {
    // 執(zhí)行讀操作
} finally {
    readLock.unlock();
}

// 獲取寫鎖
writeLock.lock();
try {
    // 執(zhí)行寫操作
} finally {
    writeLock.unlock();
}

這段代碼首先獲取一個(gè)讀寫鎖對(duì)象rwLock,然后從中分別獲取讀鎖readLock和寫鎖writeLock。讀鎖允許多個(gè)線程同時(shí)持有,而寫鎖是互斥的,只允許一個(gè)線程持有。

2. 讀寫鎖的釋放

與通常的 Java 鎖一樣,Redisson 的讀寫鎖也要求在使用完畢后顯式釋放。在上面的示例中,使用了try-finally塊確保鎖一定會(huì)被釋放。

3. 實(shí)現(xiàn)細(xì)節(jié)

Redisson 使用 Redis 的原子操作來實(shí)現(xiàn)讀寫鎖。以下是一些細(xì)節(jié):

  • 當(dāng)一個(gè)線程獲取讀鎖時(shí),Redisson 會(huì)在 Redis 中記錄當(dāng)前持有讀鎖的線程數(shù)量,并且在鎖的值中增加一個(gè)標(biāo)識(shí)符,表示這個(gè)線程持有讀鎖。
  • 當(dāng)一個(gè)線程獲取寫鎖時(shí),Redisson 會(huì)將鎖的值設(shè)置為一個(gè)唯一的標(biāo)識(shí)符,表示這個(gè)線程持有寫鎖。此時(shí),其他線程無法獲取讀鎖或?qū)戞i。
  • 讀鎖和寫鎖在釋放時(shí)會(huì)根據(jù)鎖的類型(讀鎖或?qū)戞i)來更新鎖的值,并在不再持有鎖的情況下將其清除。

Redisson 還提供了一些其他功能,如讀鎖的重入性和鎖的自動(dòng)續(xù)期等,以增強(qiáng)鎖的功能??傊琑edisson 使得在分布式環(huán)境中實(shí)現(xiàn)讀寫鎖變得相對(duì)簡單,使用了 Redis 的原子操作來確保鎖的正確性和一致性。

四.高并發(fā)高可用

1.什么是接口冪等性?

接口冪等性是指無論調(diào)用接口的次數(shù)是一次還是多次,對(duì)系統(tǒng)的影響都是相同的。換句話說,無論接口被調(diào)用多少次,最終的結(jié)果應(yīng)該是一致的。這個(gè)概念通常在分布式系統(tǒng)、Web 服務(wù)、API 設(shè)計(jì)等領(lǐng)域中討論,旨在確保系統(tǒng)的穩(wěn)定性、一致性和可靠性。

冪等性的一個(gè)常見用例是在進(jìn)行寫操作時(shí),如創(chuàng)建、更新或刪除數(shù)據(jù)。在這些情況下,如果多次請(qǐng)求接口,結(jié)果都應(yīng)該是相同的,不會(huì)產(chǎn)生意外的重復(fù)操作或錯(cuò)誤。

舉個(gè)例子,考慮一個(gè)銀行轉(zhuǎn)賬的接口。無論你調(diào)用這個(gè)接口一次還是多次,最終賬戶的余額變化應(yīng)該是一樣的。如果接口在多次調(diào)用時(shí)引發(fā)了多次轉(zhuǎn)賬,可能會(huì)導(dǎo)致賬戶余額錯(cuò)誤或不一致。

確保接口冪等性通常需要以下一些方法:

  1. 唯一標(biāo)識(shí)符: 在每次請(qǐng)求中使用唯一標(biāo)識(shí)符(如請(qǐng)求 ID、事務(wù) ID)來標(biāo)識(shí)每個(gè)請(qǐng)求。服務(wù)器端可以使用這些標(biāo)識(shí)符來檢測是否已經(jīng)處理過相同的請(qǐng)求。

  2. 冪等操作設(shè)計(jì): 設(shè)計(jì)接口的操作,使得多次執(zhí)行相同的操作不會(huì)產(chǎn)生額外的影響。例如,如果已經(jīng)創(chuàng)建了某個(gè)資源,再次創(chuàng)建同一個(gè)資源應(yīng)該返回相同的結(jié)果,而不會(huì)重復(fù)創(chuàng)建。

  3. 事務(wù)和狀態(tài)管理: 對(duì)于需要修改系統(tǒng)狀態(tài)的操作,使用事務(wù)來確保一致性。事務(wù)可以在操作失敗時(shí)回滾之前的狀態(tài),從而避免重復(fù)修改。

  4. 冪等性檢測: 在服務(wù)器端檢測已經(jīng)處理過的請(qǐng)求,如果檢測到重復(fù)請(qǐng)求,則返回相同的結(jié)果而不會(huì)產(chǎn)生副作用。

  5. 冪等性標(biāo)記: 為接口添加冪等性標(biāo)記,允許客戶端指定請(qǐng)求為冪等操作,從而通知服務(wù)器端在處理請(qǐng)求時(shí)保持冪等性。

  6. 響應(yīng)和狀態(tài)碼: 使用適當(dāng)?shù)?HTTP 狀態(tài)碼和響應(yīng)來表示操作的結(jié)果。例如,對(duì)于重復(fù)的冪等請(qǐng)求,可以使用 200 OK 表示之前已經(jīng)執(zhí)行過相同的操作。

確保接口的冪等性對(duì)于分布式系統(tǒng)和 API 設(shè)計(jì)至關(guān)重要,它可以減少因網(wǎng)絡(luò)問題、重試或其他因素導(dǎo)致的數(shù)據(jù)不一致性和錯(cuò)誤。

2.冪等和防重提交的區(qū)別

冪等性(Idempotence)和防重提交(Prevent Duplicate Submissions)是兩個(gè)不同概念,它們通常用于描述不同方面的操作和處理,特別是在網(wǎng)絡(luò)和分布式系統(tǒng)中。下面我會(huì)為你解釋它們的區(qū)別:

  1. 冪等性(Idempotence):

    • 冪等性是指無論對(duì)同一個(gè)操作執(zhí)行多少次,結(jié)果都是一致的,也就是說,重復(fù)執(zhí)行相同的操作不會(huì)產(chǎn)生不同的效果。這對(duì)于網(wǎng)絡(luò)請(qǐng)求和分布式系統(tǒng)中的操作非常重要,因?yàn)樗梢源_保在網(wǎng)絡(luò)中的各種情況下,操作不會(huì)重復(fù)執(zhí)行或?qū)е乱馔獾母弊饔谩?/li>
    • 舉例:HTTP 請(qǐng)求方法中的 GET 和 DELETE 通常是冪等的,因?yàn)樗鼈儾粫?huì)更改服務(wù)器的狀態(tài),并且多次執(zhí)行相同的請(qǐng)求不會(huì)產(chǎn)生不同的結(jié)果。
  2. 防重提交(Prevent Duplicate Submissions):

    • 防重提交是一種策略,用于防止用戶或系統(tǒng)在同一操作上重復(fù)提交數(shù)據(jù)。這通常與表單提交和數(shù)據(jù)更新有關(guān)。如果不采取防重提交措施,用戶可能會(huì)多次提交相同的表單或數(shù)據(jù),導(dǎo)致不必要的重復(fù)操作。
    • 舉例:當(dāng)用戶提交一個(gè)在線表單時(shí),服務(wù)器通常會(huì)生成一個(gè)唯一的標(biāo)識(shí)符(例如,令牌或會(huì)話 ID)并將其與提交的數(shù)據(jù)關(guān)聯(lián)起來。如果用戶再次提交相同的表單,服務(wù)器會(huì)檢查該標(biāo)識(shí)符,如果已經(jīng)處理過相同的請(qǐng)求,則拒絕重復(fù)提交。

總結(jié)
冪等性強(qiáng)調(diào)相同操作的重復(fù)執(zhí)行不會(huì)改變結(jié)果,而防重提交則強(qiáng)調(diào)防止相同數(shù)據(jù)或請(qǐng)求的重復(fù)提交。在某些情況下,可以同時(shí)使用這兩個(gè)概念,以確保在網(wǎng)絡(luò)和分布式系統(tǒng)中的操作具有一致性并避免重復(fù)處理。

3.冪等在項(xiàng)目中的應(yīng)用?

冪等性在不同的應(yīng)用場景中具有重要作用:

冪等性是指對(duì)同一操作的多次執(zhí)行具有相同的效果,無論執(zhí)行多少次,結(jié)果都是一致的。這是在分布式系統(tǒng)中非常重要的概念,因?yàn)樵诰W(wǎng)絡(luò)通信中,消息可能會(huì)重復(fù)發(fā)送或處理。以下是一些應(yīng)用場景:

  • 支付系統(tǒng): 在金融領(lǐng)域,冪等性非常重要。例如,如果用戶多次點(diǎn)擊支付按鈕,系統(tǒng)應(yīng)該只執(zhí)行一次支付操作,而不會(huì)多次扣款。

  • 消息隊(duì)列處理: 當(dāng)消息隊(duì)列用于分發(fā)任務(wù)或消息時(shí),消費(fèi)者應(yīng)該能夠多次接收相同的消息,但只處理一次,以確保任務(wù)的冪等性。

  • REST API 設(shè)計(jì): 在設(shè)計(jì) RESTful API 時(shí),應(yīng)該使 API 具有冪等性,以便客戶端可以多次調(diào)用相同的請(qǐng)求而不會(huì)產(chǎn)生不一致的結(jié)果。

在項(xiàng)目中,應(yīng)用冪等性具體方式取決于項(xiàng)目的需求和架構(gòu)。通常,使用冪等性可以確保系統(tǒng)在面對(duì)網(wǎng)絡(luò)中斷、消息重復(fù)或其他異常情況時(shí)仍然能夠保持一致性。

4.一致性 hash 在項(xiàng)目中的應(yīng)用?

一致性哈希是一種用于分布式數(shù)據(jù)存儲(chǔ)和負(fù)載均衡的技術(shù),它允許數(shù)據(jù)在不同節(jié)點(diǎn)之間進(jìn)行分布,同時(shí)保持節(jié)點(diǎn)的動(dòng)態(tài)可擴(kuò)展性。以下是一些應(yīng)用場景:

  • 分布式緩存: 一致性哈希可用于將緩存數(shù)據(jù)分布到多個(gè)緩存節(jié)點(diǎn),以實(shí)現(xiàn)負(fù)載均衡和水平擴(kuò)展。

  • 分布式數(shù)據(jù)庫: 在分布式數(shù)據(jù)庫中,一致性哈??梢杂糜跊Q定將數(shù)據(jù)存儲(chǔ)在哪個(gè)節(jié)點(diǎn)上,同時(shí)支持節(jié)點(diǎn)的動(dòng)態(tài)添加和移除。

  • 負(fù)載均衡: 一致性哈??捎糜谠诜植际较到y(tǒng)中均勻地分布請(qǐng)求,以避免某些節(jié)點(diǎn)過載。

  • 分布式文件系統(tǒng): 一致性哈??捎糜趯⑽募K分布到不同的存儲(chǔ)節(jié)點(diǎn)上,以實(shí)現(xiàn)高可用性和容錯(cuò)性。

一致性哈希則有助于解決負(fù)載均衡和分布式數(shù)據(jù)存儲(chǔ)的問題,提高系統(tǒng)的性能和可擴(kuò)展性。

5.什么是布隆過濾器?

布隆過濾器(Bloom Filter)是一種空間效率很高的概率數(shù)據(jù)結(jié)構(gòu),用于判斷一個(gè)元素是否存在于一個(gè)集合中。它基于哈希函數(shù),可以快速地進(jìn)行成員查詢,但是在查詢結(jié)果方面存在一定的概率性。

布隆過濾器的主要特點(diǎn)是:

  1. 快速成員查詢: 布隆過濾器能夠在常數(shù)時(shí)間內(nèi)(與集合大小無關(guān))進(jìn)行成員查詢,無需遍歷整個(gè)集合,因此在處理大量數(shù)據(jù)時(shí)速度非???。

  2. 空間效率高: 布隆過濾器只需要使用很少的額外內(nèi)存空間來存儲(chǔ)元素信息,這使得它在存儲(chǔ)大量數(shù)據(jù)時(shí)占用的空間非常小。

  3. 概率性判斷: 布隆過濾器在判斷元素存在時(shí)是概率性的,即可能存在誤判(判斷某個(gè)元素存在,但實(shí)際上不存在)和漏判(判斷某個(gè)元素不存在,但實(shí)際上存在)。

  4. 不支持元素刪除: 布隆過濾器一般不支持從集合中刪除元素,因?yàn)閯h除操作可能影響到其他元素的判斷。

布隆過濾器的實(shí)現(xiàn)原理是通過一系列的哈希函數(shù)將每個(gè)元素映射到一個(gè)位數(shù)組(bit array)中的多個(gè)位置上,將這些位置置為 1。查詢時(shí),將待查詢?cè)亟?jīng)過相同的哈希函數(shù)映射到位數(shù)組上,如果所有對(duì)應(yīng)位置都為 1,則認(rèn)為元素可能存在于集合中;如果有任何一個(gè)位置不為 1,則元素肯定不存在于集合中。

6.布隆過濾器作用?

由于布隆過濾器存在概率性誤判的問題,因此適用于對(duì)于判斷是否存在的問題,并且可以容忍一定的誤判率。常見的應(yīng)用包括:

  • 緩存系統(tǒng)中判斷某個(gè)數(shù)據(jù)是否在緩存中,從而避免無效的數(shù)據(jù)庫查詢。
  • 網(wǎng)絡(luò)爬蟲中避免重復(fù)爬取相同的 URL。
  • 分布式系統(tǒng)中的去重操作,避免重復(fù)處理相同的數(shù)據(jù)。

需要注意的是,布隆過濾器不能提供精確的查詢結(jié)果,但在很多場景下,其高效的查詢速度和較小的內(nèi)存占用使其成為一個(gè)有價(jià)值的工具。

7.常見的分布式日志方案?

分布式日志方案是用于在分布式系統(tǒng)中管理、收集、存儲(chǔ)和分析日志數(shù)據(jù)的解決方案。這些方案旨在處理大量的日志數(shù)據(jù),以便識(shí)別問題、監(jiān)控系統(tǒng)健康狀況和進(jìn)行數(shù)據(jù)分析。以下是一些常見的分布式日志方案:

  1. ELK Stack(Elasticsearch, Logstash, Kibana): ELK Stack 是一個(gè)開源的分布式日志處理方案,由 Elasticsearch 用于存儲(chǔ)和索引日志數(shù)據(jù),Logstash 用于采集、處理和轉(zhuǎn)發(fā)日志,Kibana 用于展示和可視化日志數(shù)據(jù)。它們的組合可以提供強(qiáng)大的日志分析和查詢功能。

  2. Graylog: Graylog 是一個(gè)開源的日志管理和分析平臺(tái),提供收集、存儲(chǔ)、搜索和分析日志的功能。它支持高可用部署、靈活的數(shù)據(jù)處理流水線以及實(shí)時(shí)搜索和可視化。

  3. Fluentd: Fluentd 是一個(gè)開源的日志收集和傳輸工具,用于將日志從不同的源傳輸?shù)讲煌哪繕?biāo)。它支持多種數(shù)據(jù)源和數(shù)據(jù)目標(biāo),可以將日志聚合到中央存儲(chǔ)或分發(fā)到多個(gè)位置。

  4. Splunk: Splunk 是一個(gè)商業(yè)的日志管理和分析平臺(tái),可以用于收集、索引、搜索和分析大量的日志數(shù)據(jù)。它提供實(shí)時(shí)搜索、可視化、報(bào)表和警報(bào)功能,適用于大規(guī)模的日志分析需求。

  5. Apache Kafka: 雖然 Kafka 主要是用于分布式消息傳遞,但它也可以用作日志收集工具。通過將日志數(shù)據(jù)發(fā)送到 Kafka 主題中,然后由訂閱者消費(fèi)和處理日志數(shù)據(jù),可以實(shí)現(xiàn)分布式日志收集。

  6. Prometheus: 雖然主要是一個(gè)監(jiān)控和報(bào)警系統(tǒng),Prometheus 也可以用于日志收集。它提供強(qiáng)大的數(shù)據(jù)存儲(chǔ)和查詢能力,可用于存儲(chǔ)和查詢?nèi)罩緮?shù)據(jù)。

  7. Apache Hadoop: Hadoop 生態(tài)系統(tǒng)中的組件,如 HDFS 和 HBase,可以用于存儲(chǔ)和管理大規(guī)模的日志數(shù)據(jù)。這對(duì)于需要進(jìn)行大數(shù)據(jù)分析的場景特別有用。

這些分布式日志方案都有其優(yōu)勢和適用場景,選擇適合自己需求的方案時(shí),需要考慮數(shù)據(jù)量、性能需求、可用性、可視化和分析功能等因素。

8.簡介 prometheus

Prometheus 是一個(gè)開源的監(jiān)控和警報(bào)系統(tǒng),旨在幫助用戶收集、存儲(chǔ)、查詢和可視化系統(tǒng)和服務(wù)的時(shí)間序列數(shù)據(jù)。它由 SoundCloud 開發(fā)并于 2012 年開源發(fā)布。Prometheus 是一個(gè)獨(dú)立的項(xiàng)目,但也與云原生生態(tài)系統(tǒng)和容器技術(shù)(如 Kubernetes)緊密集成。

以下是 Prometheus 的一些主要特點(diǎn)和功能:

  1. 多維數(shù)據(jù)模型: Prometheus 使用具有標(biāo)簽(Label)的時(shí)間序列數(shù)據(jù)模型,這意味著每個(gè)樣本數(shù)據(jù)都帶有鍵-值對(duì)標(biāo)簽。這樣的模型支持多個(gè)維度的靈活查詢和聚合。

  2. 數(shù)據(jù)采集和存儲(chǔ): Prometheus 支持多種方式的數(shù)據(jù)采集,可以從各種應(yīng)用、服務(wù)、操作系統(tǒng)和中間件中收集指標(biāo)數(shù)據(jù)。收集的數(shù)據(jù)會(huì)存儲(chǔ)在本地的時(shí)間序列數(shù)據(jù)庫中。

  3. 查詢語言: Prometheus 提供了 PromQL 查詢語言,可以用于從存儲(chǔ)的數(shù)據(jù)中進(jìn)行查詢、聚合和計(jì)算。PromQL 支持范圍查詢、聚合操作、數(shù)學(xué)計(jì)算等功能。

  4. 數(shù)據(jù)可視化: Prometheus 可以與多種數(shù)據(jù)可視化工具集成,如 Grafana,用于創(chuàng)建儀表盤和圖表,以便對(duì)監(jiān)控?cái)?shù)據(jù)進(jìn)行直觀的可視化。

  5. 告警和警報(bào): Prometheus 具有強(qiáng)大的告警機(jī)制,可以根據(jù)指標(biāo)數(shù)據(jù)的閾值設(shè)置警報(bào)規(guī)則,并在條件滿足時(shí)觸發(fā)警報(bào)通知。

  6. 自動(dòng)發(fā)現(xiàn): Prometheus 支持自動(dòng)發(fā)現(xiàn)和目標(biāo)服務(wù)的配置,通過服務(wù)發(fā)現(xiàn)機(jī)制可以動(dòng)態(tài)地添加、刪除或更新監(jiān)控目標(biāo)。

  7. 高度可擴(kuò)展: Prometheus 支持水平擴(kuò)展,可以通過添加更多的實(shí)例來處理更大規(guī)模的監(jiān)控工作負(fù)載。

  8. 云原生集成: Prometheus 與云原生技術(shù)(如 Kubernetes)集成緊密,可以自動(dòng)發(fā)現(xiàn)和監(jiān)控容器、Pod 等資源。

Prometheus 在云原生生態(tài)系統(tǒng)中得到廣泛應(yīng)用,適用于監(jiān)控分布式系統(tǒng)、微服務(wù)架構(gòu)、容器化應(yīng)用等場景。它的靈活的數(shù)據(jù)模型、強(qiáng)大的查詢語言和可擴(kuò)展性使其成為了一個(gè)流行的監(jiān)控和告警解決方案。

9.簡介 Grafana

Grafana 是一個(gè)開源的數(shù)據(jù)可視化和監(jiān)控平臺(tái),用于創(chuàng)建儀表盤、圖表和報(bào)表,幫助用戶將復(fù)雜的數(shù)據(jù)轉(zhuǎn)化為可視化的圖形形式,從而更直觀地理解數(shù)據(jù)。Grafana 最初是為 Graphite 設(shè)計(jì)的,但現(xiàn)在已經(jīng)成為一個(gè)通用的多數(shù)據(jù)源監(jiān)控和可視化平臺(tái)。

以下是 Grafana 的一些主要特點(diǎn)和功能:

  1. 多數(shù)據(jù)源支持: Grafana 支持連接多種不同的數(shù)據(jù)源,包括時(shí)間序列數(shù)據(jù)庫(如 Prometheus、InfluxDB)、日志數(shù)據(jù)庫(如 Elasticsearch)、關(guān)系型數(shù)據(jù)庫(如 MySQL、PostgreSQL)等。

  2. 豐富的可視化選項(xiàng): Grafana 提供各種可視化選項(xiàng),包括折線圖、柱狀圖、餅圖、儀表盤、地圖等,用戶可以根據(jù)需要選擇最適合的圖表類型。

  3. 高度可定制: 用戶可以自定義圖表的樣式、顏色、標(biāo)簽等,以及儀表盤的布局和外觀,從而創(chuàng)建符合自己需求的數(shù)據(jù)可視化界面。

  4. 告警和警報(bào): Grafana 支持設(shè)置告警規(guī)則,當(dāng)指標(biāo)數(shù)據(jù)滿足預(yù)定義條件時(shí),可以觸發(fā)警報(bào)通知,幫助用戶及時(shí)發(fā)現(xiàn)和解決問題。

  5. 插件生態(tài)系統(tǒng): Grafana 有豐富的插件生態(tài)系統(tǒng),用戶可以通過插件擴(kuò)展功能,集成不同的數(shù)據(jù)源、圖表類型等。

  6. 團(tuán)隊(duì)協(xié)作: Grafana 支持多用戶和團(tuán)隊(duì)協(xié)作,可以為不同用戶設(shè)置權(quán)限,共享儀表盤和報(bào)表。

  7. 云原生支持: Grafana 可以與云原生技術(shù)(如 Kubernetes)緊密集成,通過自動(dòng)發(fā)現(xiàn)和監(jiān)控容器、Pod 等資源。

Grafana 的易用性和靈活性使其成為了一個(gè)受歡迎的數(shù)據(jù)可視化和監(jiān)控平臺(tái)。無論是在開發(fā)者、運(yùn)維人員,還是數(shù)據(jù)分析師等角色中,Grafana 都能夠幫助用戶更好地理解和利用數(shù)據(jù)。

五.分布式協(xié)議與算法

1.選舉算法

在分布式系統(tǒng)中采用的是主從架構(gòu),也就是有一個(gè)主節(jié)點(diǎn),若干個(gè)從節(jié)點(diǎn)。主節(jié)點(diǎn)可以執(zhí)行讀寫操作,從節(jié)點(diǎn)只執(zhí)行讀操作。采用這種一主多從的方案,可以有效保證了數(shù)據(jù)的一致性(哪怕不是強(qiáng)一致性,也能有效保證最終一致性)。

在開源的 Redis,MongoDB,Zookeeper 等軟件中也都采用了的選舉算法,常用的選舉算法有以下三種:Bully 算法,Raft 算法,ZAB 算法。

2.Bully 算法

Bully 算法是一種簡單直接的算法,因?yàn)樗倪x舉原則是取 ID 的最大值作為主節(jié)點(diǎn)。在 Bully 算法中,有兩種節(jié)點(diǎn)角色:普通節(jié)點(diǎn)和主節(jié)點(diǎn)。初始化的時(shí)候,所有節(jié)點(diǎn)都是普通節(jié)點(diǎn)。當(dāng)主節(jié)點(diǎn)掛掉了,才會(huì)發(fā)起下一輪選舉。

在選舉中,有三種消息,分別是Election消息,用于發(fā)起選舉。Alive消息,對(duì)Elelction消息的回復(fù)。Victory消息是選主成功后向其他節(jié)點(diǎn)發(fā)送的通知消息。選舉過程如下:

  1. 集群中每個(gè)節(jié)點(diǎn)判斷一下自己的節(jié)點(diǎn) ID 是否為存活節(jié)點(diǎn)中最大,如果是則直接發(fā)送 Victory 消息,宣布自己為主節(jié)點(diǎn)。

  2. 如果自己不是最大的 ID 節(jié)點(diǎn),則向比自己大的節(jié)點(diǎn)發(fā)送 Election 消息,等待其他節(jié)點(diǎn)回復(fù)。

  3. 在指定時(shí)間內(nèi)沒有收到其他節(jié)點(diǎn) Alive 信息,則認(rèn)為自己是主節(jié)點(diǎn),然后向其他節(jié)點(diǎn)發(fā)送 Victory 消息,宣布自己是主節(jié)點(diǎn)。如果收到比自己 ID 大的節(jié)點(diǎn) Alive 消息,則等待其他節(jié)點(diǎn)發(fā)送 Victory 消息。

  4. 如果收到比自己節(jié)點(diǎn) ID 小的消息,則回復(fù) Alive 消息,告訴對(duì)方重新選舉。

該算法簡單易于實(shí)現(xiàn),但是當(dāng)頻繁有節(jié)點(diǎn)加入或退出,會(huì)造成頻繁主節(jié)點(diǎn)切換。當(dāng)應(yīng)用在節(jié)點(diǎn)數(shù)量穩(wěn)定的系統(tǒng)中,該算法的通用性可能還不錯(cuò)。此外,在 MongoDB 中故障遷移也采用了這個(gè)算法,它是采用最后操作的時(shí)間戳作為節(jié)點(diǎn) ID,在實(shí)際生產(chǎn)中,我們還可以使用啟動(dòng)的時(shí)間作為 ID。

3.Raft 算法

提起分布式系統(tǒng),Raft 算法是一個(gè)基礎(chǔ)的算法。這種算法開創(chuàng)了投票選舉的先例。核心思想就是“少數(shù)服從多數(shù)”,獲得投票最多的節(jié)點(diǎn)就是主節(jié)點(diǎn)。

在 Raft 算法中,節(jié)點(diǎn)有三種角色:

  • Leader:主節(jié)點(diǎn),同一時(shí)刻只有一個(gè)主節(jié)點(diǎn)。

  • Candidate:候選節(jié)點(diǎn),節(jié)點(diǎn)處于該狀態(tài)才能選舉為 Leader 節(jié)點(diǎn)。

  • Follower:Leader 節(jié)點(diǎn)的跟隨者,不能發(fā)起選舉。

Raft 選舉流程如下:

  1. 初始化階段,每個(gè)節(jié)點(diǎn)都是 Follower 狀態(tài)。

  2. 進(jìn)入選舉階段時(shí),每個(gè)節(jié)點(diǎn)進(jìn)入 Candidate 狀態(tài),并向其他節(jié)點(diǎn)發(fā)送選舉 Vote 請(qǐng)求。

  3. 其他節(jié)點(diǎn)根據(jù)收到選舉請(qǐng)求的順序回復(fù)是否同意成為主節(jié)點(diǎn),在每一輪選舉中每個(gè)候選人只有一張選票。

  4. 若發(fā)起選舉的節(jié)點(diǎn)超過一半選票,則成為主節(jié)點(diǎn),狀態(tài)轉(zhuǎn)換為 Leader,其他節(jié)點(diǎn)狀態(tài)有 Candidate 轉(zhuǎn)換為 Follower,leader 和 follower 之間有周期性的心跳包,用于檢測主節(jié)點(diǎn)存活。

  5. Leader 節(jié)點(diǎn)的周期到了,會(huì)自動(dòng)降為 Follower,進(jìn)入下一輪選主。當(dāng)然了,當(dāng)前 Leader 節(jié)點(diǎn)掛掉了也會(huì)由 Follower 發(fā)起新一輪選舉。

目前集群管理容器 Kubernetes 采用的 etcd 組件就是采用 Raft 選舉算法進(jìn)行的??傊琑aft 算法選舉速度快,缺點(diǎn)是要求系統(tǒng)內(nèi)每個(gè)節(jié)點(diǎn)都可以通信,因此通信容量大。

Nacos 中集群保證一致性算法采用 Raft 協(xié)議模式,采用心跳機(jī)制實(shí)現(xiàn)選舉的。

Raft 協(xié)議是分布式系統(tǒng)領(lǐng)域中的一種一致性算法,用于實(shí)現(xiàn)分布式系統(tǒng)中多個(gè)節(jié)點(diǎn)之間的一致性和協(xié)調(diào)。與 Paxos 等其他一致性算法相比,Raft 協(xié)議的設(shè)計(jì)更加直觀和易于理解,因此在教學(xué)和實(shí)際應(yīng)用中都得到了廣泛的關(guān)注和應(yīng)用。Raft 協(xié)議由 Diego Ongaro 和 John Ousterhout 在 2013 年提出。

Raft 協(xié)議的核心思想是將分布式一致性問題分解為三個(gè)關(guān)鍵的角色:領(lǐng)導(dǎo)者(Leader)、跟隨者(Follower)和候選人(Candidate)。協(xié)議的運(yùn)作分為以下幾個(gè)階段:

  1. 領(lǐng)導(dǎo)者選舉(Leader Election): 初始狀態(tài)下,所有節(jié)點(diǎn)都是候選人。候選人在一輪選舉中請(qǐng)求其他節(jié)點(diǎn)的選票,獲得大多數(shù)選票的候選人成為領(lǐng)導(dǎo)者。領(lǐng)導(dǎo)者負(fù)責(zé)提出日志條目和復(fù)制日志。

  2. 日志復(fù)制(Log Replication): 領(lǐng)導(dǎo)者負(fù)責(zé)提出日志條目,并將其廣播給其他節(jié)點(diǎn)。其他節(jié)點(diǎn)將接收到的日志條目存儲(chǔ)在本地日志中,并向領(lǐng)導(dǎo)者發(fā)送確認(rèn)。一旦領(lǐng)導(dǎo)者確認(rèn)大多數(shù)節(jié)點(diǎn)已經(jīng)存儲(chǔ)了特定的日志條目,該條目被視為“已提交”。

  3. 安全性保障: Raft 協(xié)議通過約束性質(zhì)來確保安全性。具體來說,一個(gè)已經(jīng)提交的日志條目在以后的日志中不會(huì)被覆蓋或更改。

Raft 協(xié)議的主要目標(biāo)是確保以下性質(zhì):

  • 一致性: 在任何時(shí)間,所有節(jié)點(diǎn)的日志內(nèi)容是一致的。
  • 可用性: 只要多數(shù)節(jié)點(diǎn)可用,系統(tǒng)就能夠接受讀取和寫入請(qǐng)求。
  • 安全性: 只有領(lǐng)導(dǎo)者可以提出新的日志條目,而其他節(jié)點(diǎn)會(huì)遵循領(lǐng)導(dǎo)者的日志來保持一致。

Raft 協(xié)議的設(shè)計(jì)使得它相對(duì)于一些復(fù)雜的算法(如 Paxos)來說更容易理解和實(shí)現(xiàn),因此在分布式系統(tǒng)領(lǐng)域得到了廣泛的應(yīng)用。許多開源項(xiàng)目和數(shù)據(jù)庫,如 etcd、CockroachDB 等,都使用了 Raft 協(xié)議作為底層的一致性算法。

4.ZAB 算法

ZAB算法英文叫 Zookeeper Atomic Broadcast,是改進(jìn)了 Raft 算法而用于zookeeper分布式協(xié)調(diào)服務(wù)的一種算法。這種算法引入了節(jié)點(diǎn) ID 和數(shù)據(jù) ID 作為參考進(jìn)行選擇主節(jié)點(diǎn)。節(jié)點(diǎn) ID 或數(shù)據(jù) ID 越大,表示數(shù)據(jù)越新。所以相比 Raft 算法,ZAB 算法盡可能保證了數(shù)據(jù)的最新。

ZAB 算法有三種角色:

  • Laeder 節(jié)點(diǎn)。即主節(jié)點(diǎn)
  • Follower 節(jié)點(diǎn),也就是從節(jié)點(diǎn)。
  • Obserer 節(jié)點(diǎn),觀察節(jié)點(diǎn),無投票權(quán)利。

選舉過程中,節(jié)點(diǎn)擁有四種狀態(tài)。分別是:

  • Looking 狀態(tài):即選舉狀態(tài),當(dāng)進(jìn)入這種狀態(tài),節(jié)點(diǎn)會(huì)認(rèn)為集群中沒有主節(jié)點(diǎn),自己會(huì)進(jìn)入選舉狀態(tài)。
  • Leading 狀態(tài):表示已經(jīng)選出了主節(jié)點(diǎn),當(dāng)前節(jié)點(diǎn)為 Leader。
  • Following 狀態(tài):表示集群中已經(jīng)選出了主節(jié)點(diǎn),其他非主節(jié)點(diǎn)更新自己的狀態(tài)為 Following。
  • Observer 狀態(tài):當(dāng)前節(jié)點(diǎn)處于該狀態(tài)沒有投票和選舉權(quán)。

投票過程中,每個(gè)節(jié)點(diǎn)有一個(gè)唯一的三元組(server_id,server_zxid,epoch),其中 Server_id 代表當(dāng)前節(jié)點(diǎn)的 ID,server_zxid代表數(shù)據(jù)(事務(wù) id),該值越大表示數(shù)據(jù)越新。epoch 為當(dāng)前選舉輪數(shù)。選舉原則為 server_zxid 最大為主節(jié)點(diǎn),若相同則 server_id 最大為主節(jié)點(diǎn)。

該算法性能很好,對(duì)系統(tǒng)無特殊要求,一般采用廣播的方式發(fā)送消息。但是當(dāng)集群內(nèi)集群數(shù)據(jù)龐大,也容易出現(xiàn)網(wǎng)絡(luò)風(fēng)暴。此外,選舉過程需要節(jié)點(diǎn) ID 和數(shù)據(jù)事務(wù) ID,因此選舉周期可能稍長。不過有新節(jié)點(diǎn)的加入也不一定觸發(fā)新的選舉。

4.Paxos 協(xié)議

Paxos 協(xié)議是分布式系統(tǒng)領(lǐng)域中的一種一致性算法,用于在分布式系統(tǒng)中實(shí)現(xiàn)多個(gè)節(jié)點(diǎn)之間的一致性和協(xié)調(diào)。Paxos 協(xié)議由 Leslie Lamport 于 1989 年首次提出,它解決了分布式系統(tǒng)中的狀態(tài)機(jī)復(fù)制問題,即如何在不同節(jié)點(diǎn)之間復(fù)制和保持相同的狀態(tài)。

Paxos 協(xié)議的核心思想是通過投票和協(xié)商來達(dá)成共識(shí),使得多個(gè)節(jié)點(diǎn)能夠就某個(gè)值達(dá)成一致意見。Paxos 協(xié)議分為多個(gè)階段,每個(gè)階段都有不同的角色和任務(wù)。主要包括以下幾個(gè)階段:

  1. 提議階段(Prepare Phase): 在這個(gè)階段,一個(gè)節(jié)點(diǎn)(稱為提議者)向其他節(jié)點(diǎn)發(fā)送一個(gè)提案,請(qǐng)求其他節(jié)點(diǎn)在不同條件下對(duì)提案進(jìn)行批準(zhǔn)或拒絕。提案包括提案編號(hào)和提議的值。

  2. 批準(zhǔn)階段(Promise Phase): 在收到提案后,其他節(jié)點(diǎn)(稱為接收者)會(huì)比較提案編號(hào),如果提案編號(hào)更高且沒有承諾過其他提案,則接收者會(huì)向提議者發(fā)送一個(gè)承諾,表示愿意接受提案。

  3. 提交階段(Accept Phase): 如果提議者收到足夠的承諾,表示提案可以提交。提議者向所有節(jié)點(diǎn)發(fā)送提案,并要求節(jié)點(diǎn)接受該提案作為共識(shí)值。

Paxos 協(xié)議的主要目標(biāo)是保證以下性質(zhì):

  • 一致性: 所有正確的節(jié)點(diǎn)最終都會(huì)達(dá)成相同的共識(shí)值。
  • 安全性: 如果一個(gè)值被選定為共識(shí)值,那么它一定是一個(gè)被提議者提出過的值。
  • 活性: 只要大多數(shù)節(jié)點(diǎn)是正確的,提案最終會(huì)被選定為共識(shí)值。

盡管 Paxos 協(xié)議是一個(gè)經(jīng)典的一致性算法,但其復(fù)雜性使得理解和實(shí)現(xiàn)都具有一定難度。因此,許多分布式系統(tǒng)和數(shù)據(jù)庫在實(shí)際中使用了基于 Paxos 的變種或改進(jìn),如 Multi-Paxos、Fast Paxos、Zookeeper 中的 Zab 協(xié)議等,來滿足分布式一致性需求。

5.選舉算法對(duì)比

1.zookeeper 主從選舉

zookeeper 作為一個(gè)分布式應(yīng)用程序協(xié)調(diào)服務(wù),在大型網(wǎng)站中,其本身也是集群部署的,安裝 zookeeper 的時(shí)候最好是單數(shù)節(jié)點(diǎn),因?yàn)橐x舉。Zookeeper 的 leader 節(jié)點(diǎn)是集群工作的核心,用來更新并保證 leader 和 server 具有相同的系統(tǒng)狀態(tài),F(xiàn)ollower 服務(wù)器是 leader 的跟隨者,用于接收客戶端的請(qǐng)求并向客戶端返回結(jié)果,在選舉過程中參與投票。對(duì)于客戶端而言,每個(gè) zookeeper 都是一樣的。

zookeeper 提供了三種選舉策略:

  • LeaderElection
  • AuthFastLeaderElection
  • FastLeaderElection

下面主要講解 FastLeaderElection 的選舉原理
1.myId
每個(gè) ZooKeeper 服務(wù)器,都需要在數(shù)據(jù)文件夾下創(chuàng)建一個(gè)名為 myid 的文件,該文件包含整個(gè) ZooKeeper 集群唯一的 ID(整數(shù))。例如,某 ZooKeeper 集群包含三臺(tái)服務(wù)器,hostname 分別為 zoo1、zoo2zoo3,其 myid 分別為 1、2 和 3,則在配置文件中其 ID 與 hostname 必須一一對(duì)應(yīng),如下所示。在該配置文件中,server. 后面的數(shù)據(jù)即為 myid

server.1=zoo1:2888:3888
server.2=zoo2:2888:3888
server.3=zoo3:2888:3888

2.zxid
服務(wù)器的事務(wù) id,數(shù)據(jù)越新,zxid 越大;

3.epoch
邏輯時(shí)鐘,在服務(wù)端是一個(gè)自增序列,每次進(jìn)入下一輪投票后,就會(huì)加 1;

4.服務(wù)器狀態(tài)

  • LOOKING: 不確定 Leader 狀態(tài)。該狀態(tài)下的服務(wù)器認(rèn)為當(dāng)前集群中沒有 Leader,會(huì)發(fā)起 Leader 選舉。
  • FOLLOWING: 跟隨者狀態(tài)。表明當(dāng)前服務(wù)器角色是 Follower,并且知道 Leader 是誰。
  • LEADING: 領(lǐng)導(dǎo)者狀態(tài)。表明當(dāng)前服務(wù)器角色為 Leader 角色,它會(huì)維護(hù)與 Follower 間的心跳。
  • OBSERVING: 觀察者狀態(tài)。表明當(dāng)前服務(wù)器角色是 Observer,與 Follower 的唯一不同在于不參與選舉,也不參與集群寫操作時(shí)的投票。

5.選票的數(shù)據(jù)結(jié)構(gòu)
每個(gè)服務(wù)器在進(jìn)行領(lǐng)導(dǎo)選舉時(shí),會(huì)發(fā)送如下關(guān)鍵信息:

  • logicClock 每個(gè)服務(wù)器會(huì)維護(hù)一個(gè)自增的整數(shù),名為 logicClock,它表示這是該服務(wù)器發(fā)起的第多少輪投票。
  • state 當(dāng)前服務(wù)器的狀態(tài)。
  • self_id 當(dāng)前服務(wù)器的 myid。
  • self_zxid 當(dāng)前服務(wù)器上所保存的數(shù)據(jù)的最大 zxid。
  • vote_id 被推舉的服務(wù)器的 myid。
  • vote_zxid 被推舉的服務(wù)器上所保存的數(shù)據(jù)的最大 zxid。

6.投票流程
當(dāng)系統(tǒng)啟動(dòng)或者 leader 崩潰后,就會(huì)開始 leader 的選舉。

  1. 狀態(tài)變更。服務(wù)器啟動(dòng)的時(shí)候每個(gè) server 的狀態(tài)時(shí) Looking,如果是 leader 掛掉后進(jìn)入選舉,那么余下的非 Observer 的 Server 就會(huì)將自己的服務(wù)器狀態(tài)變更為 Looking,然后開始進(jìn)入 Leader 的選舉狀態(tài);

  2. 發(fā)起投票。每個(gè) server 會(huì)產(chǎn)生一個(gè)(myid,zxid)的投票,系統(tǒng)初始化的時(shí)候 zxid 都是 0,如果是運(yùn)行期間,每個(gè) server 的 zxid 可能都不同,這取決于最后一次更新的數(shù)據(jù)。將投票發(fā)送給集群中的所有機(jī)器;

  3. 接收并檢查投票。server 收到投票后,會(huì)先檢查是否是本輪投票,是否來自 looking 狀態(tài)的 server;

  4. 處理投票。對(duì)自己的投票和接收到的投票進(jìn)行 PK:

  5. 先檢查zxid,較大的優(yōu)先為leader
    如果zxid一樣,myid較大的為leader
    根據(jù)PK結(jié)果更新自己的投票,在次發(fā)送自己的投票
    
  6. 統(tǒng)計(jì)投票。每次投票后,服務(wù)器統(tǒng)計(jì)投票信息,如果有過半機(jī)器接收到相同的投票,那么 leader 產(chǎn)生,如果否,那么進(jìn)行下一輪投票;

  7. 改變 server 狀態(tài),一旦確定了 Leader,server 會(huì)更新自己的狀態(tài)為 Following,選舉結(jié)束。

補(bǔ)充說明:

  1. 在步驟 2 發(fā)送投票的時(shí)候,投票的信息除了 myid 和 zxid,還有:
    • electionEpoch:邏輯時(shí)鐘,用來判斷多個(gè)投票是否在同一輪選舉周期中,該值在服務(wù)端是一個(gè)自增序列,每次進(jìn)入新一輪的投票后,都會(huì)對(duì)該值進(jìn)行加 1 操作。
    • peerEpoch:被推舉的 Leader 的 epoch。
    • state:當(dāng)前服務(wù)器的狀態(tài)。
  2. 為了能夠相互投票,每兩臺(tái)服務(wù)器之間都會(huì)建立網(wǎng)絡(luò)連接,為避免重復(fù)建立 TCP 連接,zk 的 server 只允許 myid 大于自己的服務(wù)器與自己建立連接,否則斷開當(dāng)前連接,并主動(dòng)和對(duì)方建立連接。
2.ES master 選舉

選舉時(shí)間點(diǎn)
Elasticsearch 在滿足如下時(shí)間點(diǎn)的時(shí)候會(huì)觸發(fā)選舉

  1. 集群啟動(dòng)初始化。
  2. 集群 Master 崩潰的時(shí)候。
  3. 任何一個(gè)節(jié)點(diǎn)發(fā)現(xiàn)當(dāng)前集群中 Master 節(jié)點(diǎn)沒有得到 n/2+1 個(gè)節(jié)點(diǎn)的認(rèn)可的時(shí)候,觸發(fā)選舉。

選舉流程
1.篩選 activeMaster 列表
Es 的 master 就是從 activeMasters 列表或者 masterCandidates(master 候選節(jié)點(diǎn))列表選舉出來,所以選舉之前 es 首先需要得到這兩個(gè)列表。Elasticsearch 節(jié)點(diǎn)成員首先向集群中的所有成員發(fā)送 Ping 請(qǐng)求,elasticsearch 默認(rèn)等待 discovery.zen.ping_timeout 時(shí)間,然后 elasticsearch 針對(duì)獲取的全部 response 進(jìn)行過濾,篩選出其中 activeMasters 列表,activeMaster 列表是其它節(jié)點(diǎn)認(rèn)為的當(dāng)前集群的 Master 節(jié)點(diǎn)。

源碼如下:

List<DiscoveryNode> activeMasters = new ArrayList<>();
for (ZenPing.PingResponse pingResponse : pingResponses) {
    //不允許將自己放在activeMasters列表中
    if (pingResponse.master() != null && !localNode.equals(pingResponse.master())) {
        activeMasters.add(pingResponse.master());
    }
}

可以看到 elasticsearch 在獲取 activeMasters 列表的時(shí)候會(huì)排除本地節(jié)點(diǎn),目的是為了避免腦裂,假設(shè)這樣一個(gè)場景,當(dāng)前最小編號(hào)的節(jié)點(diǎn) P0 認(rèn)為自己就是 master 并且 P0 和其它節(jié)點(diǎn)發(fā)生網(wǎng)絡(luò)分區(qū),同時(shí) es 允許將自己放在 activeMaster 中,因?yàn)?P0 編號(hào)最小,那么 P0 永遠(yuǎn)會(huì)選擇自己作為 master 節(jié)點(diǎn),那么就會(huì)出現(xiàn)腦裂的情況。

2.篩選 masterCandidates 列表
masterCandidates 列表是當(dāng)前集群有資格成為 Master 的節(jié)點(diǎn),如果我們?cè)?elasticsearch.yml 中配置了如下參數(shù),那么這個(gè)節(jié)點(diǎn)就沒有資格成為 Master 節(jié)點(diǎn),也就不會(huì)被篩選進(jìn)入 masterCandidates 列表。

# 配置某個(gè)節(jié)點(diǎn)沒有成為master資格
node.master:false

源代碼如下所示:

List<ElectMasterService.MasterCandidate> masterCandidates = new ArrayList<>();
for (ZenPing.PingResponse pingResponse : pingResponses) {
    if (pingResponse.node().isMasterNode()) {
        masterCandidates.add(new ElectMasterService.MasterCandidate(pingResponse.node(), 		    pingResponse.getClusterStateVersion()));
    }
}

3.從 activeMasters 列表選舉 Master 節(jié)點(diǎn)
activeMaster 列表是其它節(jié)點(diǎn)認(rèn)為的當(dāng)前集群的 Master 節(jié)點(diǎn)列表,如果 activeMasters 列表不為空,elasticsearch 會(huì)優(yōu)先從 activeMasters 列表中選舉,選舉的算法是 Bully 算法,Bully 算法會(huì)涉及到優(yōu)先級(jí)比較, 在 activeMasters 列表優(yōu)先級(jí)比較的時(shí)候,如果節(jié)點(diǎn)有成為 master 的資格,那么優(yōu)先級(jí)比較高,如果 activeMaster 列表有多個(gè)節(jié)點(diǎn)具有 master 資格,那么選擇 id 最小的節(jié)點(diǎn)。

代碼如下:

private static int compareNodes(DiscoveryNode o1, DiscoveryNode o2) {
    if (o1.isMasterNode() && !o2.isMasterNode()) {
        return -1;
    }
    if (!o1.isMasterNode() && o2.isMasterNode()) {
        return 1;
    }
    return o1.getId().compareTo(o2.getId());
}

//取ID最小的
public DiscoveryNode tieBreakActiveMasters(Collection<DiscoveryNode> activeMasters) {
    return activeMasters.stream().min(ElectMasterService::compareNodes).get();
}

4.從 masterCandidates 列表選舉 Master 節(jié)點(diǎn)
如果 activeMaster 列表為空,那么會(huì)在 masterCandidates 中選舉,masterCandidates 選舉也會(huì)涉及到優(yōu)先級(jí)比較,masterCandidates 選舉的優(yōu)先級(jí)比較和 activemasters 選舉的優(yōu)先級(jí)比較不同。它首先會(huì)判斷 masterCandidates 列表成員數(shù)目是否達(dá)到了最小數(shù)目 discovery.zen.minimum_master_nodes。如果達(dá)到的情況下比較優(yōu)先級(jí),優(yōu)先級(jí)比較的時(shí)候首先比較節(jié)點(diǎn)擁有的集群狀態(tài)版本編號(hào),然后再比較 id,這一流程的目的是讓擁有最新集群狀態(tài)的節(jié)點(diǎn)成為 master。

代碼如下:

public static int compare(MasterCandidate c1, MasterCandidate c2) {
    //比較集群狀態(tài)版本編碼,取版本大的,使擁有集群最新狀態(tài)的節(jié)點(diǎn)成為master
    int ret = Long.compare(c2.clusterStateVersion, c1.clusterStateVersion);
    //如果版本號(hào)一樣,則比較ID
    if (ret == 0) {
        ret = compareNodes(c1.getNode(), c2.getNode());
    }
    return ret;
}

5.本地節(jié)點(diǎn)是 master
經(jīng)過上述選舉之后,會(huì)選舉出一個(gè)準(zhǔn) master 節(jié)點(diǎn), 準(zhǔn) master 節(jié)點(diǎn)會(huì)等待其它節(jié)點(diǎn)的投票,如果有 discovery.zen.minimum_master_nodes-1 個(gè)節(jié)點(diǎn)投票認(rèn)為當(dāng)前節(jié)點(diǎn)是 master,那么選舉就成功,準(zhǔn) master 會(huì)等待 discovery.zen.master_election.wait_for_joins_timeout 時(shí)間,如果超時(shí),那么就失敗。在代碼實(shí)現(xiàn)上準(zhǔn) master 通過注冊(cè)一個(gè)回調(diào)來實(shí)現(xiàn),同時(shí)借助了 AtomicReferenceCountDownLatch 等并發(fā)構(gòu)建實(shí)現(xiàn)

if (clusterService.localNode().equals(masterNode)) {
final int requiredJoins = Math.max(0, electMaster.minimumMasterNodes() - 1);
nodeJoinController.waitToBeElectedAsMaster(requiredJoins, masterElectionWaitForJoinsTimeout,
        new NodeJoinController.ElectionCallback() {
            @Override
            public void onElectedAsMaster(ClusterState state) {
                joinThreadControl.markThreadAsDone(currentThread);
                nodesFD.updateNodesAndPing(state); // start the nodes FD
            }
            @Override
            public void onFailure(Throwable t) {
                logger.trace("failed while waiting for nodes to join, rejoining", t);
                joinThreadControl.markThreadAsDoneAndStartNew(currentThread);
            }
        }
);

本地節(jié)點(diǎn)是 Master 的時(shí)候,Master 節(jié)點(diǎn)會(huì)開啟錯(cuò)誤檢測 (NodeFaultDetection 機(jī)制),它節(jié)點(diǎn)會(huì)定期掃描集群所有的成員,將失活的成員移除集群,同時(shí)將最新的集群狀態(tài)發(fā)布到集群中,集群成員收到最新的集群狀態(tài)后會(huì)進(jìn)行相應(yīng)的調(diào)整,比如重新選擇主分片,進(jìn)行數(shù)據(jù)復(fù)制等操作。

6.本地節(jié)點(diǎn)不是 master
當(dāng)前節(jié)點(diǎn)判定在集群當(dāng)前狀態(tài)下如果自己不可能是 master 節(jié)點(diǎn),首先會(huì)禁止其他節(jié)點(diǎn)加入自己,然后投票選舉出準(zhǔn) Master 節(jié)點(diǎn)。同時(shí)監(jiān)聽 master 發(fā)布的集群狀態(tài) (MasterFaultDetection 機(jī)制),如果集群狀態(tài)顯示的 master 節(jié)點(diǎn)和當(dāng)前節(jié)點(diǎn)認(rèn)為的 master 節(jié)點(diǎn)不是同一個(gè)節(jié)點(diǎn),那么當(dāng)前節(jié)點(diǎn)就重新發(fā)起選舉。

Master 節(jié)點(diǎn)也會(huì)監(jiān)聽 Master 節(jié)點(diǎn)進(jìn)行錯(cuò)誤檢測,如果成員節(jié)點(diǎn)發(fā)現(xiàn) master 連接不上,重新加入新的 Master 節(jié)點(diǎn),如果發(fā)現(xiàn)當(dāng)前集群中有很多節(jié)點(diǎn)都連不上 master 節(jié)點(diǎn),那么會(huì)重新發(fā)起選舉。

3.Redis 哨兵選舉

哨兵的工作方式:

  1. 每個(gè) Sentinel(哨兵)進(jìn)程以每秒一次的頻率向整個(gè)集群中的 Master 主服務(wù)器、Slave 從服務(wù)器以及其他哨兵進(jìn)程發(fā)送一個(gè) PING 命令。

  2. 如果一個(gè)實(shí)例距離最后一次有效回復(fù) PING 命令的時(shí)間超過 down-after-milliseconds 選型所指定的值,則這個(gè)實(shí)例會(huì)被哨兵進(jìn)程標(biāo)記為主觀下線。

  3. 如果一個(gè) Master 主服務(wù)器被標(biāo)記為主觀下線,則正在監(jiān)視這個(gè) Master 主服務(wù)器的所有哨兵進(jìn)程要以每秒一次的頻率來確認(rèn) Master 主服務(wù)器是否的確進(jìn)入了主觀下線狀態(tài)。

  4. 如果有足夠數(shù)量(quorum 數(shù)量)的哨兵進(jìn)程認(rèn)為 Master 主服務(wù)器下線,則 Master 主服務(wù)器會(huì)被標(biāo)記成客觀下線。

  5. 在一般情況下,每個(gè)哨兵進(jìn)程以每秒一次的頻率向集群中的所有 master 主服務(wù)器、Slave 從服務(wù)器發(fā)送 Info 命令。當(dāng) Master 主服務(wù)器被標(biāo)記為客觀下線后,Info 命令的頻率從 10 秒一次改成 1 秒一次。

  6. 若沒有足夠數(shù)量的 Sentinel(哨兵)進(jìn)程同意 Master 主服務(wù)器下線, Master 主服務(wù)器的客觀下線狀態(tài)就會(huì)被移除。若 Master 主服務(wù)器重新向 Sentinel(哨兵)進(jìn)程發(fā)送 PING 命令返回有效回復(fù),Master 主服務(wù)器的主觀下線狀態(tài)就會(huì)被移除。

選舉方式:
如果一個(gè) Master 主服務(wù)器被標(biāo)記為客觀下線,并且 Majority 數(shù)量的哨兵都允許進(jìn)行主備切換(如果沒有 Majority 數(shù)量的哨兵同一主備切換時(shí),是不允許進(jìn)行主備切換的),選舉一個(gè)領(lǐng)頭的哨兵進(jìn)行主備切換。

選舉領(lǐng)頭的哨兵
選舉領(lǐng)頭 sentinel 遵循以下規(guī)則:

  1. 所有的 sentinel 都有公平被選擇成領(lǐng)頭的資格。

  2. 所有的 sentinel 都只有一次將某個(gè) sentinel 選舉成領(lǐng)頭的機(jī)會(huì)(在一輪選舉中),一旦選舉,則不能修改。

  3. 先到先得,一旦當(dāng)前 sentinel 設(shè)置了領(lǐng)頭 sentinel,以后要求設(shè)置 sentinel 都會(huì)被拒絕。

  4. 每個(gè)發(fā)現(xiàn)服務(wù)器客觀下線的哨兵,都會(huì)要求其他哨兵將自己設(shè)置成領(lǐng)頭哨兵。

  5. 當(dāng)一個(gè)哨兵(源哨兵)向另一個(gè)哨兵(目標(biāo)哨兵)發(fā)送is-master-down-by-addr ip port current_epoch runid命令的時(shí)候,runid 參數(shù)不是*,而是sentinel運(yùn)行 id,就表示源sentinel要求目標(biāo)sentinel選舉其為領(lǐng)頭。

  6. 源哨兵會(huì)檢查目標(biāo)哨兵對(duì)其設(shè)置成領(lǐng)頭的回復(fù),如果回復(fù)的leader_runidleader_epoch為源哨兵,表示目標(biāo)哨兵同意將源哨兵設(shè)置成領(lǐng)頭。

  7. 如果某個(gè)哨兵被半數(shù)以上的哨兵設(shè)置成領(lǐng)頭哨兵,則該哨兵就成為領(lǐng)頭哨兵。

  8. 如果在限定時(shí)間內(nèi),沒有選舉出領(lǐng)頭哨兵,暫停一段時(shí)間,再選舉。

Master 主服務(wù)器選舉

領(lǐng)頭哨兵會(huì)對(duì)已客觀宕機(jī)的 Master 主服務(wù)器和 Slave 從服務(wù)器進(jìn)行主備切換。
選舉 Slave 需要考慮以下的信息:

  1. 如果一個(gè)slavemaster斷開連接的時(shí)間已經(jīng)超過了down-after-milliseconds的 10 倍,外加 master 宕機(jī)的時(shí)長,那么認(rèn)為該slave不適合選舉為master。

  2. 按照slave的優(yōu)先級(jí)進(jìn)行排序,slave priority越低,優(yōu)先級(jí)越高。

  3. 如果slave的優(yōu)先級(jí)一致,那么看replica offset,哪個(gè) slave 復(fù)制了越多的數(shù)據(jù),offset越靠后,優(yōu)先級(jí)就越高。

  4. 如果優(yōu)先級(jí)和offset都一樣,那么選擇一個(gè)run ID比較小的那個(gè)slave。

4.Redis cluster 選舉

判斷節(jié)點(diǎn)宕機(jī)

如果一個(gè)節(jié)點(diǎn)認(rèn)為另外一個(gè)節(jié)點(diǎn)宕機(jī),那么就是 pfail ,主觀宕機(jī)。如果多個(gè)節(jié)點(diǎn)都認(rèn)為另外一個(gè)節(jié)點(diǎn)宕機(jī)了,那么就是 fail ,客觀宕機(jī),跟哨兵的原理幾乎一樣,sdown,odown

在 cluster-node-timeout 內(nèi),某個(gè)節(jié)點(diǎn)一直沒有返回 pong ,那么就被認(rèn)為 pfail 。

如果一個(gè)節(jié)點(diǎn)認(rèn)為某個(gè)節(jié)點(diǎn) pfail 了,那么會(huì)在 gossip ping 消息中, ping 給其他節(jié)點(diǎn),如果超過半數(shù)的節(jié)點(diǎn)都認(rèn)為 pfail 了,那么就會(huì)變成 fail 。

Redis Cluster Master 主服務(wù)器選舉:
對(duì)宕機(jī)的 master node,從其所有 slave node 中,選擇一個(gè)切換成 master node。

  1. 檢查每個(gè)slave node與 master node 斷開連接的時(shí)間,如果超過了 cluster-node-timeout * cluster-slave-validity-factor,那么沒有資格切換成 master。

  2. 每個(gè)從節(jié)點(diǎn),根據(jù)自己對(duì) master 復(fù)制數(shù)據(jù)的offset,來設(shè)置一個(gè)選舉時(shí)間,offset越大(復(fù)制的數(shù)據(jù)越多)的從節(jié)點(diǎn),選舉時(shí)間越靠前,優(yōu)先進(jìn)行選舉。

  3. 所有的master node開始slave選舉投票,給要進(jìn)行選舉的slave node進(jìn)行投票,如果大部分master(N/2+1)都投票給某個(gè)從節(jié)點(diǎn),那么選舉通過,那個(gè)從節(jié)點(diǎn)可以切換成master。

  4. 從節(jié)點(diǎn)進(jìn)行主備切換,從節(jié)點(diǎn)切換成主節(jié)點(diǎn)。

5.kafka parition 選舉

Kafka 的 Leader 選舉是通過在 zookeeper 上創(chuàng)建 /controller 臨時(shí)節(jié)點(diǎn)來實(shí)現(xiàn) leader 選舉,并在該節(jié)點(diǎn)中寫入當(dāng)前 broker 的信息 {“version”:1,”brokerid”:1,”timestamp”:”1512018424988”}。利用 Zookeeper強(qiáng)一致性特性,一個(gè)節(jié)點(diǎn)只能被一個(gè)客戶端創(chuàng)建成功,創(chuàng)建成功的 broker 即為 leader,即先到先得原則,leader 也就是集群中的 controller,負(fù)責(zé)集群中所有大小事務(wù)。當(dāng) leaderzookeeper 失去連接時(shí),臨時(shí)節(jié)點(diǎn)會(huì)刪除,而其他 broker 會(huì)監(jiān)聽該節(jié)點(diǎn)的變化,當(dāng)節(jié)點(diǎn)刪除時(shí),其他 broker 會(huì)收到事件通知,重新發(fā)起 leader 選舉。

kafka 在所有 broker 中選出一個(gè) controller,所有 Partition 的 Leader 選舉都由 controller 決定。controller 會(huì)將 Leader 的改變直接通過 RPC 的方式(比 zookeeper queue 的方式有效)通知給需要為此作出響應(yīng)的 Broker。同時(shí) controller 也負(fù)責(zé)增刪 Topic 以及 Replica 的分配。

六.Raft 算法

1.Raft 算法基本原理

共識(shí)算法就是保證一個(gè)集群的多臺(tái)機(jī)器協(xié)同工作,在遇到請(qǐng)求時(shí),數(shù)據(jù)能夠保持一致。即使遇到機(jī)器宕機(jī),整個(gè)系統(tǒng)仍然能夠?qū)ν獗3址?wù)的可用性。

Raft 協(xié)議強(qiáng)依賴 Leader 節(jié)點(diǎn)來確保集群數(shù)據(jù)一致性。即 client 發(fā)送過來的數(shù)據(jù)均先到達(dá) Leader 節(jié)點(diǎn),Leader 接收到數(shù)據(jù)后,先將數(shù)據(jù)標(biāo)記為 uncommitted 狀態(tài),隨后 Leader 開始向所有 Follower 復(fù)制數(shù)據(jù)并等待響應(yīng),在獲得集群中大于 N/2 個(gè) Follower 的已成功接收數(shù)據(jù)完畢的響應(yīng)后,Leader 將數(shù)據(jù)的狀態(tài)標(biāo)記為 committed,隨后向 client 發(fā)送數(shù)據(jù)已接收確認(rèn),在向 client 發(fā)送出已數(shù)據(jù)接收后,再向所有 Follower 節(jié)點(diǎn)發(fā)送通知表明該數(shù)據(jù)狀態(tài)為 committed。

2.Raft 三個(gè)子問題

Raft 將共識(shí)問題分解三個(gè)子問題:

  • Leader election 領(lǐng)導(dǎo)選舉:有且僅有一個(gè) leader 節(jié)點(diǎn),如果 leader 宕機(jī),通過選舉機(jī)制選出新的 leader;
  • Log replication 日志復(fù)制:leader 從客戶端接收數(shù)據(jù)更新/刪除請(qǐng)求,然后日志復(fù)制到 follower 節(jié)點(diǎn),從而保證集群數(shù)據(jù)的一致性;
  • Safety 安全性:通過安全性原則來處理一些特殊 case,保證 Raft 算法的完備性;

3.Raft 算法核心流程

Raft 算法核心流程可以歸納為:

  • 首先選出 leader,leader 節(jié)點(diǎn)負(fù)責(zé)接收外部的數(shù)據(jù)更新/刪除請(qǐng)求;
  • 然后日志復(fù)制到其他 follower 節(jié)點(diǎn),同時(shí)通過安全性的準(zhǔn)則來保證整個(gè)日志復(fù)制的一致性;
  • 如果遇到 leader 故障,followers 會(huì)重新發(fā)起選舉出新的 leader;

【技術(shù)驛站】分布式基礎(chǔ)與常見面試問題,s10 分布式,分布式

4.raft 協(xié)議角色

  • Leader: 處理所有客戶端交互,日志復(fù)制等,一般一次只有一個(gè) Leader.
  • Follower: 類似選民,完全被動(dòng)
  • Candidate(候選人): 可以被選為一個(gè)新的領(lǐng)導(dǎo)人。

Leader 全權(quán)負(fù)責(zé)所有客戶端的請(qǐng)求,以及將數(shù)據(jù)同步到 Follower 中(同一時(shí)刻系統(tǒng)中只存在一個(gè) Leader)。

Follower 被動(dòng)響應(yīng)請(qǐng)求 RPC,從不主動(dòng)發(fā)起請(qǐng)求 RPC。

Candidate 由 Follower 向 Leader 轉(zhuǎn)換的中間狀態(tài)

【技術(shù)驛站】分布式基礎(chǔ)與常見面試問題,s10 分布式,分布式

【技術(shù)驛站】分布式基礎(chǔ)與常見面試問題,s10 分布式,分布式

【技術(shù)驛站】分布式基礎(chǔ)與常見面試問題,s10 分布式,分布式

5.領(lǐng)導(dǎo)選舉

集群中每個(gè)節(jié)點(diǎn)只能處于 Leader、Follower 和 Candidate 三種狀態(tài)的一種:

(1)follower 從節(jié)點(diǎn):

  • 節(jié)點(diǎn)默認(rèn)是 follower;
  • 如果剛剛開始或和 leader 通信超時(shí),follower 會(huì)發(fā)起選舉,變成 candidate,然后去競選 leader;
  • 如果收到其他 candidate 的競選投票請(qǐng)求,按照先來先得 & 每個(gè)任期只能投票一次的投票原則投票;

(2)candidate 候選者:

  • follower 發(fā)起選舉后就變?yōu)?candidate,會(huì)向其他節(jié)點(diǎn)拉選票。candidate 的票會(huì)投給自己,所以不會(huì)向其他節(jié)點(diǎn)投票;
  • 如果獲得超過半數(shù)的投票,candidate 變成 leader,然后馬上和其他節(jié)點(diǎn)通信,表明自己的 leader 的地位;
  • 如果選舉超時(shí),重新發(fā)起選舉;
  • 如果遇到更高任期 Term 的 leader 的通信請(qǐng)求,轉(zhuǎn)化為 follower;

(3)leader 主節(jié)點(diǎn):

  • 成為 leader 節(jié)點(diǎn)后,此時(shí)可以接受客戶端的數(shù)據(jù)請(qǐng)求,負(fù)責(zé)日志同步;
  • 如果遇到更高任期 Term 的 candidate 的通信請(qǐng)求,這說明 candidate 正在競選 leader,此時(shí)之前任期的 leader 轉(zhuǎn)化為 follower,且完成投票;
  • 如果遇到更高任期 Term 的 leader 的通信請(qǐng)求,這說明已經(jīng)選舉成功新的 leader,此時(shí)之前任期的 leader 轉(zhuǎn)化為 follower;

【技術(shù)驛站】分布式基礎(chǔ)與常見面試問題,s10 分布式,分布式

Raft 算法把時(shí)間軸劃分為不同任期 Term。每個(gè)任期 Term 都有自己的編號(hào) TermId,該編號(hào)全局唯一且單調(diào)遞增。如下圖,每個(gè)任務(wù)的開始都 Leader Election 領(lǐng)導(dǎo)選舉。如果選舉成功,則進(jìn)入維持任務(wù) Term 階段,此時(shí) leader 負(fù)責(zé)接收客戶端請(qǐng)求并,負(fù)責(zé)復(fù)制日志。Leader 和所有 follower 都保持通信,如果 follower 發(fā)現(xiàn)通信超時(shí),TermId 遞增并發(fā)起新的選舉。如果選舉成功,則進(jìn)入新的任期。如果選舉失敗,TermId 遞增,然后重新發(fā)起選舉直到成功。

舉個(gè)例子,參考下圖,Term N 選舉成功,Term N+1 和 Term N+2 選舉失敗,Term N+3 重新選舉成功。

【技術(shù)驛站】分布式基礎(chǔ)與常見面試問題,s10 分布式,分布式

具體的說,Leader 在任期內(nèi)會(huì)周期性向其他 follower 節(jié)點(diǎn)發(fā)送心跳來維持地位。follower 如果發(fā)現(xiàn)心跳超時(shí),就認(rèn)為 leader 節(jié)點(diǎn)宕機(jī)或不存在。隨機(jī)等待一定時(shí)間后,follower 會(huì)發(fā)起選舉,變成 candidate,然后去競選 leader。選舉結(jié)果有三種情況:

(1)獲取超過半數(shù)投票,贏得選舉:

  • 當(dāng) Candidate 獲得超過半數(shù)的投票時(shí),代表自己贏得了選舉,且轉(zhuǎn)化為 leader。此時(shí),它會(huì)馬上向其他節(jié)點(diǎn)發(fā)送請(qǐng)求,從而確認(rèn)自己的 leader 地位,從而阻止新一輪的選舉;
  • 投票原則**:當(dāng)多個(gè) Candidate 競選 Leader 時(shí)**
    • 一個(gè)任期內(nèi),follower 只會(huì)投票一次票,且投票先來先得;
    • Candidate 存儲(chǔ)的日志至少要和 follower 一樣新(安全性準(zhǔn)則),否則拒絕投票;

(2)投票未超過半數(shù),選舉失?。?/strong>

  • 當(dāng) Candidate 沒有獲得超過半數(shù)的投票時(shí),說明多個(gè) Candidate 競爭投票導(dǎo)致過于分散,或者出現(xiàn)了丟包現(xiàn)象。此時(shí),認(rèn)為當(dāng)期任期選舉失敗,任期 TermId+1,然后發(fā)起新一輪選舉;
  • 上述機(jī)制可能出現(xiàn)多個(gè) Candidate 競爭投票,導(dǎo)致每個(gè) Candidate 一直得不到超過半數(shù)的票,最終導(dǎo)致無限選舉投票循環(huán);
  • 投票分散問題解決:Raft 會(huì)給每個(gè) Candidate 在固定時(shí)間內(nèi)隨機(jī)確認(rèn)一個(gè)超時(shí)時(shí)間(一般為 150-300ms)。這么做可以盡量避免新的一次選舉出現(xiàn)多個(gè) Candidate 競爭投票的現(xiàn)象;

(3)收到其他 Leader 通信請(qǐng)求:

  • 如果 Candidate 收到其他聲稱自己是 Leader 的請(qǐng)求的時(shí)候,通過任期 TermId 來判斷是否處理;
  • 如果請(qǐng)求的任期 TermId 不小于 Candidate 當(dāng)前任期 TermId,那么 Candidate 會(huì)承認(rèn)該 Leader 的合法地位并轉(zhuǎn)化為 Follower;
  • 否則,拒絕這次請(qǐng)求,并繼續(xù)保持 Candidate;

簡單地說,Leader Election 領(lǐng)導(dǎo)選舉通過若干的投票原則,保證一次選舉有且僅可能最多選出一個(gè) leader,從而解決了腦裂問題。

6.Raft 整個(gè)底層實(shí)現(xiàn)原理:

默認(rèn)情況下選舉的過程:

  1. 默認(rèn)情況下每個(gè)節(jié)點(diǎn)都是跟隨者角色;
  2. 每個(gè)節(jié)點(diǎn)隨機(jī)生成一個(gè)選舉的超時(shí)時(shí)間,大概為 100-300ms,在這個(gè)超時(shí)時(shí)間內(nèi)必須要等待。
  3. 超時(shí)時(shí)間過后,當(dāng)前節(jié)點(diǎn)的狀態(tài)由跟隨者變?yōu)楦傔x者狀態(tài),會(huì)給其他節(jié)點(diǎn)發(fā)出選舉的投票通知,只要該競選者有超過半數(shù)以上即可選為領(lǐng)導(dǎo)角色。

核心的設(shè)計(jì)原理就是誰超時(shí)時(shí)間最短誰成為領(lǐng)導(dǎo)角色的概率就最大。

故障的重新實(shí)現(xiàn)選舉:
如果跟隨者節(jié)點(diǎn)不能夠及時(shí)的收到領(lǐng)導(dǎo)角色消息,那么這時(shí)候跟隨者就會(huì)將當(dāng)前自己的狀態(tài)由跟隨者變?yōu)榫┻x擇角色,會(huì)給其他節(jié)點(diǎn)發(fā)出選舉的投票通知,只要該競選者有超過半數(shù)以上即可選為領(lǐng)導(dǎo)角色。

疑問:是否可能會(huì)產(chǎn)生兩個(gè)同事的競選者呢?

  • 如果所有的節(jié)點(diǎn)的超市隨機(jī)數(shù)都一樣的情況下,當(dāng)前投票全部作廢,重新進(jìn)入隨機(jī)生成超時(shí)時(shí)間;
  • 如果有多個(gè)節(jié)點(diǎn)生成的隨機(jī)數(shù)都是一樣的情況下,比較誰的票數(shù)最多,誰就是領(lǐng)導(dǎo)。如果票數(shù)完全一樣的情況下,直接作廢,重新進(jìn)入隨機(jī)生成超時(shí)時(shí)間。

注意:建議集群節(jié)點(diǎn)為奇數(shù),偶數(shù)節(jié)點(diǎn)容易造成死循環(huán)。

如何實(shí)現(xiàn)日志的復(fù)制:

  • 所有的寫的請(qǐng)求都是統(tǒng)一交給領(lǐng)導(dǎo)角色完成,寫入該對(duì)應(yīng)的日志,標(biāo)記該日志為被提交狀態(tài);
  • 為了提交該日志,領(lǐng)導(dǎo)角色就會(huì)將該日志以心跳的形式發(fā)送給其他的跟隨者節(jié)點(diǎn),只要超過半數(shù)的跟隨者節(jié)點(diǎn)寫入該日志,則直接通知其它跟隨者節(jié)點(diǎn)同步該數(shù)據(jù),這個(gè)過程稱為日志復(fù)制的過程。

七.限流算法

1.限流算法

限流算法是一種用于控制流量的技術(shù),通常用于防止系統(tǒng)過載、保護(hù)系統(tǒng)資源和提高系統(tǒng)的穩(wěn)定性。在網(wǎng)絡(luò)服務(wù)、分布式系統(tǒng)以及 API 接口中廣泛應(yīng)用。以下是一些常見的限流算法:

  1. 令牌桶算法(Token Bucket): 令牌桶算法基于令牌桶的概念,系統(tǒng)以恒定的速率往桶里放入令牌,每個(gè)請(qǐng)求需要一個(gè)令牌才能被處理。如果桶中沒有足夠的令牌,請(qǐng)求將被拒絕。這種算法可以平滑地處理突發(fā)流量。

  2. 漏桶算法(Leaky Bucket): 漏桶算法以恒定的速率處理請(qǐng)求,無論流量的突發(fā)性如何。如果流量超出了系統(tǒng)能夠處理的速率,多余的請(qǐng)求會(huì)被緩存在漏桶中,然后按照恒定速率被處理。

  3. 計(jì)數(shù)器算法: 這是一種簡單的限流方法,基于時(shí)間窗口內(nèi)請(qǐng)求的數(shù)量。例如,在一分鐘內(nèi)只允許處理 1000 個(gè)請(qǐng)求。一旦達(dá)到限制,后續(xù)的請(qǐng)求將被拒絕或排隊(duì)等待。

  4. 平滑窗口算法: 這種算法將時(shí)間分割為多個(gè)窗口,每個(gè)窗口內(nèi)有一個(gè)請(qǐng)求計(jì)數(shù)器。舊的計(jì)數(shù)器會(huì)在時(shí)間窗口滑動(dòng)時(shí)被清除,新的請(qǐng)求將增加計(jì)數(shù)器。這可以平滑處理突發(fā)流量。

  5. 基于演化的算法: 一些算法會(huì)根據(jù)系統(tǒng)的實(shí)際情況進(jìn)行動(dòng)態(tài)調(diào)整,例如根據(jù)系統(tǒng)的負(fù)載、響應(yīng)時(shí)間等動(dòng)態(tài)地調(diào)整限流策略。

  6. 優(yōu)先級(jí)隊(duì)列算法: 在資源有限的情況下,可以使用優(yōu)先級(jí)隊(duì)列來確保高優(yōu)先級(jí)的請(qǐng)求能夠優(yōu)先被處理。

  7. 混合算法: 有時(shí)候會(huì)將多種限流算法結(jié)合起來,以在不同情況下都能夠有效地控制流量。

選擇適合的限流算法取決于系統(tǒng)的需求、特點(diǎn)以及性能要求。限流算法有助于保護(hù)系統(tǒng)免受惡意請(qǐng)求、過載和資源耗盡的影響,從而提高系統(tǒng)的可用性和穩(wěn)定性。

【技術(shù)驛站】分布式基礎(chǔ)與常見面試問題,s10 分布式,分布式

2.固定窗口限流算法

固定窗口限流算法(Fixed Window Rate Limiting Algorithm)是一種最簡單的限流算法,其原理是在固定時(shí)間窗口(單位時(shí)間)內(nèi)限制請(qǐng)求的數(shù)量。該算法將時(shí)間分成固定的窗口,并在每個(gè)窗口內(nèi)限制請(qǐng)求的數(shù)量。具體來說,算法將請(qǐng)求按照時(shí)間順序放入時(shí)間窗口中,并計(jì)算該時(shí)間窗口內(nèi)的請(qǐng)求數(shù)量,如果請(qǐng)求數(shù)量超出了限制,則拒絕該請(qǐng)求。

假設(shè)單位時(shí)間(固定時(shí)間窗口)是1秒,限流閥值為3。在單位時(shí)間1秒內(nèi),每來一個(gè)請(qǐng)求,計(jì)數(shù)器就加1,如果計(jì)數(shù)器累加的次數(shù)超過限流閥值3,后續(xù)的請(qǐng)求全部拒絕。等到1s結(jié)束后,計(jì)數(shù)器清0,重新開始計(jì)數(shù)。

固定窗口算法的優(yōu)缺點(diǎn):

  • 優(yōu)點(diǎn):固定窗口算法非常簡單,易于實(shí)現(xiàn)和理解。
  • 缺點(diǎn):存在明顯的臨界問題,比如: 假設(shè)限流閥值為5個(gè)請(qǐng)求,單位時(shí)間窗口是1s,如果我們?cè)趩挝粫r(shí)間內(nèi)的前0.8-1s1-1.2s,分別并發(fā) 5 個(gè)請(qǐng)求。雖然都沒有超過閥值,但是如果算 0.8-1.2s,則并發(fā)數(shù)高達(dá) 10,已經(jīng)超過單位時(shí)間 1s 不超過 5 閥值的定義啦。

【技術(shù)驛站】分布式基礎(chǔ)與常見面試問題,s10 分布式,分布式

3.滑動(dòng)窗口算法?

滑動(dòng)窗口限流算法是一種常用的限流算法,用于控制系統(tǒng)對(duì)外提供服務(wù)的速率,防止系統(tǒng)被過多的請(qǐng)求壓垮。它將單位時(shí)間周期分為n個(gè)小周期,分別記錄每個(gè)小周期內(nèi)接口的訪問次數(shù),并且根據(jù)時(shí)間滑動(dòng)刪除過期的小周期。它可以解決固定窗口臨界值的問題

【技術(shù)驛站】分布式基礎(chǔ)與常見面試問題,s10 分布式,分布式

為了解決計(jì)數(shù)器算法的臨界值的問題,發(fā)明了滑動(dòng)窗口算法。在 TCP 網(wǎng)絡(luò)通信協(xié)議中,就采用滑動(dòng)時(shí)間窗口算
法來解決網(wǎng)絡(luò)擁堵問題。

滑動(dòng)時(shí)間窗口是將計(jì)數(shù)器算法中的實(shí)際周期切分成多個(gè)小的時(shí)間窗口,分別在每個(gè)小的時(shí)間窗口中記錄訪問次數(shù),然后根據(jù)時(shí)間將窗口往前滑動(dòng)并刪除過期的小時(shí)間窗口。最終只需要統(tǒng)計(jì)滑動(dòng)窗口范圍內(nèi)的小時(shí)間窗口的總
的請(qǐng)求數(shù)即可。

【技術(shù)驛站】分布式基礎(chǔ)與常見面試問題,s10 分布式,分布式

在上圖中,假設(shè)我們?cè)O(shè)置一分鐘的請(qǐng)求閾值是 100,我們將一分鐘拆分成 4 個(gè)小時(shí)間窗口,這樣,每個(gè)小的時(shí)間窗口只能處理 25 個(gè)請(qǐng)求,我們用虛線方框表示滑動(dòng)時(shí)間窗口,當(dāng)前窗口的大小是 2,也就是在窗口內(nèi)最多能處理
50 個(gè)請(qǐng)求。隨著時(shí)間的推移,滑動(dòng)窗口也隨著時(shí)間往前移動(dòng),比如上圖開始時(shí),窗口是 0:00 到 0:30 的這個(gè)范圍,過了 15 秒后,窗口是 0:15 到 0:45 的這個(gè)范圍,窗口中的請(qǐng)求重新清零,這樣就很好的解決了計(jì)數(shù)器算法的臨界值問題。
在滑動(dòng)時(shí)間窗口算法中,我們的小窗口劃分的越多,滑動(dòng)窗口的滾動(dòng)就越平滑,限流的統(tǒng)計(jì)就會(huì)越精確。

滑動(dòng)窗口限流算法的優(yōu)缺點(diǎn):

優(yōu)點(diǎn)

  • 簡單易懂
  • 精度高(通過調(diào)整時(shí)間窗口的大小來實(shí)現(xiàn)不同的限流效果)
  • 可擴(kuò)展性強(qiáng)(可以非常容易地與其他限流算法結(jié)合使用)

缺點(diǎn)

  • 突發(fā)流量無法處理(無法應(yīng)對(duì)短時(shí)間內(nèi)的大量請(qǐng)求,但是一旦到達(dá)限流后,請(qǐng)求都會(huì)直接暴力被拒絕。醬紫我們會(huì)損失一部分請(qǐng)求,這其實(shí)對(duì)于產(chǎn)品來說,并不太友好),需要合理調(diào)整時(shí)間窗口大小。

4.漏桶算法

漏桶限流算法(Leaky Bucket Algorithm)是一種流量控制算法,用于控制流入網(wǎng)絡(luò)的數(shù)據(jù)速率,以防止網(wǎng)絡(luò)擁塞。它的思想是將數(shù)據(jù)包看作是水滴,漏桶看作是一個(gè)固定容量的水桶,數(shù)據(jù)包像水滴一樣從桶的頂部流入桶中,并通過桶底的一個(gè)小孔以一定的速度流出,從而限制了數(shù)據(jù)包的流量。

漏桶限流算法的基本工作原理是:對(duì)于每個(gè)到來的數(shù)據(jù)包,都將其加入到漏桶中,并檢查漏桶中當(dāng)前的水量是否超過了漏桶的容量。如果超過了容量,就將多余的數(shù)據(jù)包丟棄。如果漏桶中還有水,就以一定的速率從桶底輸出數(shù)據(jù)包,保證輸出的速率不超過預(yù)設(shè)的速率,從而達(dá)到限流的目的。

  • 流入的水滴,可以看作是訪問系統(tǒng)的請(qǐng)求,這個(gè)流入速率是不確定的。
  • 桶的容量一般表示系統(tǒng)所能處理的請(qǐng)求數(shù)。
  • 如果桶的容量滿了,就達(dá)到限流的閥值,就會(huì)丟棄水滴(拒絕請(qǐng)求)
  • 流出的水滴,是恒定過濾的,對(duì)應(yīng)服務(wù)按照固定的速率處理請(qǐng)求。

【技術(shù)驛站】分布式基礎(chǔ)與常見面試問題,s10 分布式,分布式

漏桶限流算法的優(yōu)缺點(diǎn):

優(yōu)點(diǎn)

  • 可以平滑限制請(qǐng)求的處理速度,避免瞬間請(qǐng)求過多導(dǎo)致系統(tǒng)崩潰或者雪崩。
  • 可以控制請(qǐng)求的處理速度,使得系統(tǒng)可以適應(yīng)不同的流量需求,避免過載或者過度閑置。
  • 可以通過調(diào)整桶的大小和漏出速率來滿足不同的限流需求,可以靈活地適應(yīng)不同的場景。

缺點(diǎn)

  • 需要對(duì)請(qǐng)求進(jìn)行緩存,會(huì)增加服務(wù)器的內(nèi)存消耗。
  • 對(duì)于流量波動(dòng)比較大的場景,需要較為靈活的參數(shù)配置才能達(dá)到較好的效果。
  • 但是面對(duì)突發(fā)流量的時(shí)候,漏桶算法還是循規(guī)蹈矩地處理請(qǐng)求,這不是我們想看到的啦。流量變突發(fā)時(shí),我們肯定希望系統(tǒng)盡量快點(diǎn)處理請(qǐng)求,提升用戶體驗(yàn)嘛。

5.令牌桶算法

令牌桶算法是一種常用的限流算法,可以用于限制單位時(shí)間內(nèi)請(qǐng)求的數(shù)量。該算法維護(hù)一個(gè)固定容量的令牌桶,每秒鐘會(huì)向令牌桶中放入一定數(shù)量的令牌。當(dāng)有請(qǐng)求到來時(shí),如果令牌桶中有足夠的令牌,則請(qǐng)求被允許通過并從令牌桶中消耗一個(gè)令牌,否則請(qǐng)求被拒絕。

【技術(shù)驛站】分布式基礎(chǔ)與常見面試問題,s10 分布式,分布式

漏桶算法和令牌桶算法都是用于實(shí)現(xiàn)流量控制和限流的算法,但它們的原理和適用場景略有不同。

漏桶算法: 漏桶算法模擬了一個(gè)漏桶,請(qǐng)求被認(rèn)為是水滴,以固定的速率流出(漏出)系統(tǒng)。如果流入的請(qǐng)求速率超過了漏桶的容量,多余的請(qǐng)求將會(huì)被丟棄或者等待下一個(gè)時(shí)間段。漏桶算法適用于平滑流量,防止系統(tǒng)被大量突發(fā)流量壓垮。但是,對(duì)于短時(shí)間內(nèi)的突發(fā)流量,漏桶算法并不是特別有效,因?yàn)樗鼰o法快速處理瞬時(shí)大量的請(qǐng)求,而只能以固定的速率處理。

令牌桶算法: 令牌桶算法也是一種流量控制算法,它基于一個(gè)令牌桶的概念。令牌以一定的速率被添加到桶中,每個(gè)請(qǐng)求在處理之前需要獲取一個(gè)令牌,如果沒有可用的令牌,則請(qǐng)求將被暫時(shí)阻塞或丟棄。令牌桶算法可以適應(yīng)瞬時(shí)的突發(fā)流量,因?yàn)楫?dāng)桶中積累足夠多的令牌時(shí),可以快速處理突發(fā)請(qǐng)求,而不會(huì)受限于固定的速率。這使得令牌桶算法更適合處理突發(fā)流量的情況。

令牌桶算法的優(yōu)缺點(diǎn):

優(yōu)點(diǎn):

  • 穩(wěn)定性高:令牌桶算法可以控制請(qǐng)求的處理速度,可以使系統(tǒng)的負(fù)載變得穩(wěn)定。
  • 精度高:令牌桶算法可以根據(jù)實(shí)際情況動(dòng)態(tài)調(diào)整生成令牌的速率,可以實(shí)現(xiàn)較高精度的限流。
  • 彈性好:令牌桶算法可以處理突發(fā)流量,可以在短時(shí)間內(nèi)提供更多的處理能力,以處理突發(fā)流量。

GuavaRateLimiter限流組件,就是基于令牌桶算法實(shí)現(xiàn)的。

缺點(diǎn):

  • 實(shí)現(xiàn)復(fù)雜:相對(duì)于固定窗口算法等其他限流算法,令牌桶算法的實(shí)現(xiàn)較為復(fù)雜。 對(duì)短時(shí)請(qǐng)求難以處理:在短時(shí)間內(nèi)有大量請(qǐng)求到來時(shí),可能會(huì)導(dǎo)致令牌桶中的令牌被快速消耗完,從而限流。這種情況下,可以考慮使用漏桶算法。
  • 時(shí)間精度要求高:令牌桶算法需要在固定的時(shí)間間隔內(nèi)生成令牌,因此要求時(shí)間精度較高,如果系統(tǒng)時(shí)間不準(zhǔn)確,可能會(huì)導(dǎo)致限流效果不理想。

6.令牌桶算法種類

令牌桶允許一定程度的突發(fā)調(diào)用,那么關(guān)于令牌桶處理數(shù)據(jù)報(bào)文的方式,RFC 中定義了兩種令牌桶算法:

單速率三色標(biāo)記(single rate three color marker,srTCM,RFC2697 定義,或稱為單速雙桶算法)算法,主要關(guān)注報(bào)文尺寸的突發(fā)。

雙速率三色標(biāo)記(two rate three color marker,trTCM,RFC2698 定義,或稱為雙速雙桶算法)算法,主要關(guān)注速率的突發(fā)。

兩種算法都是為報(bào)文打上紅、黃、綠三種顏色的標(biāo)記,所以稱為“三色標(biāo)記”。 根據(jù)報(bào)文的顏色,設(shè)置報(bào)文的丟棄優(yōu)先級(jí),兩種算法都可工作于色盲模式和非色盲模式。

7.什么是計(jì)數(shù)器算法?

計(jì)數(shù)器算法,是指在指定的時(shí)間周期內(nèi)累加訪問次數(shù),達(dá)到設(shè)定的閾值時(shí),觸發(fā)限流策略。下一個(gè)時(shí)間周期
進(jìn)行訪問時(shí),訪問次數(shù)清零。此算法無論在單機(jī)還是分布式環(huán)境下實(shí)現(xiàn)都非常簡單,使用 redis 的 incr 原子自增
性,再結(jié)合 key 的過期時(shí)間,即可輕松實(shí)現(xiàn)。

從上圖我們來看,我們?cè)O(shè)置一分鐘的閾值是 100,在 0:00 到 1:00 內(nèi)請(qǐng)求數(shù)是 60,當(dāng)?shù)?1:00 時(shí),請(qǐng)求數(shù)清零,從 0 開始計(jì)算,這時(shí)在 1:00 到 2:00 之間我們能處理的最大的請(qǐng)求為 100,超過 100 個(gè)的請(qǐng)求,系統(tǒng)都拒絕。

這個(gè)算法有一個(gè)臨界問題,比如在上圖中,在 0:00 到 1:00 內(nèi),只在 0:50 有 60 個(gè)請(qǐng)求,而在 1:00 到 2:00 之間,只在 1:10 有 60 個(gè)請(qǐng)求,雖然在兩個(gè)一分鐘的時(shí)間內(nèi),都沒有超過 100 個(gè)請(qǐng)求,但是在 0:50 到 1:10 這 20 秒內(nèi),確有 120 個(gè)請(qǐng)求,雖然在每個(gè)周期內(nèi),都沒超過閾值,但是在這 20 秒內(nèi),已經(jīng)遠(yuǎn)遠(yuǎn)超過了我們?cè)瓉碓O(shè)置的 1 分鐘內(nèi) 100 個(gè)請(qǐng)求的閾值。

【技術(shù)驛站】分布式基礎(chǔ)與常見面試問題,s10 分布式,分布式

8.令牌桶和漏桶算法區(qū)別

  • 內(nèi)容上:令牌桶算法是按固定速率生成令牌,請(qǐng)求能從桶中拿到令牌就執(zhí)行,否則執(zhí)行失敗。漏桶算法是任務(wù)進(jìn)桶速率不限,但是出桶的速率是固定的,超出桶大小的的任務(wù)丟棄,也就是執(zhí)行失敗。
  • 突發(fā)流量適應(yīng)性上:令牌桶限制的是流入的速率且靈活,允許一次取出多個(gè) token,只要有令牌就可以處理任務(wù),在桶容量上允許處理突發(fā)流量。而漏桶算法出桶的速率固定,有突發(fā)流量也只能按流出的速率處理任務(wù),所以漏桶算法是平滑流入的速率,限制流出速率。

八.架構(gòu)設(shè)計(jì)

1.設(shè)計(jì)一個(gè) IM 系統(tǒng)

設(shè)計(jì)一個(gè)即時(shí)消息(IM)系統(tǒng)涉及許多方面,從基本的架構(gòu)到具體的技術(shù)組件。以下是一個(gè)詳細(xì)的設(shè)計(jì)示例:

1. 架構(gòu)選擇:
分布式架構(gòu)是構(gòu)建高性能、可擴(kuò)展的 IM 系統(tǒng)的關(guān)鍵。采用微服務(wù)架構(gòu),將不同的功能模塊拆分成獨(dú)立的服務(wù),可以提高系統(tǒng)的可維護(hù)性和擴(kuò)展性。以下是架構(gòu)的主要組成部分:

2. 組件設(shè)計(jì):

用戶認(rèn)證和授權(quán)服務(wù): 負(fù)責(zé)處理用戶的注冊(cè)、登錄、認(rèn)證和授權(quán)??梢允褂?OAuth 2.0 或 JWT 等技術(shù)來實(shí)現(xiàn)安全的用戶身份驗(yàn)證。

消息傳遞服務(wù): 用于處理消息的實(shí)時(shí)傳遞??梢圆捎?WebSocket 協(xié)議來實(shí)現(xiàn)雙向通信,也可以結(jié)合輪詢或長輪詢等技術(shù)來確保消息的及時(shí)性。

好友管理服務(wù): 處理用戶之間的好友關(guān)系。存儲(chǔ)用戶的好友列表,并提供添加、刪除好友的功能。

群組管理服務(wù): 用于創(chuàng)建和管理群組聊天??梢詫?shí)現(xiàn)群組的創(chuàng)建、成員管理、群主權(quán)限等功能。

消息存儲(chǔ)服務(wù): 負(fù)責(zé)將消息持久化存儲(chǔ)。可以使用數(shù)據(jù)庫(如 MySQL、PostgreSQL)或 NoSQL 數(shù)據(jù)庫(如 MongoDB、Redis)來存儲(chǔ)消息。

在線狀態(tài)服務(wù): 用于跟蹤用戶的在線狀態(tài)??梢允褂?Redis 等內(nèi)存數(shù)據(jù)庫來存儲(chǔ)用戶的在線信息。

推送通知服務(wù): 用于將消息通知推送給離線用戶??梢允褂?APNs(Apple Push Notification Service)、FCM(Firebase Cloud Messaging)等平臺(tái)來實(shí)現(xiàn)推送通知。

文件傳輸服務(wù): 處理用戶之間的文件傳輸,可以將文件存儲(chǔ)在分布式存儲(chǔ)系統(tǒng)(如 Amazon S3、阿里云 OSS)中,并生成訪問鏈接。

日志和監(jiān)控: 集成日志記錄和監(jiān)控系統(tǒng),用于追蹤系統(tǒng)運(yùn)行狀況、性能指標(biāo)和錯(cuò)誤情況。常見的工具有 ELK(Elasticsearch、Logstash、Kibana)和 Prometheus 等。

3. 技術(shù)選擇:

編程語言: 使用適合性能和開發(fā)效率的語言,如 Java、Kotlin、Go 等。

數(shù)據(jù)庫: 根據(jù)數(shù)據(jù)的特點(diǎn)選擇適當(dāng)?shù)臄?shù)據(jù)庫,如關(guān)系型數(shù)據(jù)庫(MySQL、PostgreSQL)用于用戶信息存儲(chǔ),NoSQL 數(shù)據(jù)庫(MongoDB、Redis)用于消息存儲(chǔ)和緩存。

通信協(xié)議: 使用 WebSocket 協(xié)議實(shí)現(xiàn)實(shí)時(shí)消息傳遞,HTTP 或其他適當(dāng)?shù)膮f(xié)議用于服務(wù)間通信。

容器和編排: 使用 Docker 容器化應(yīng)用程序,并使用 Kubernetes 或 Docker Compose 進(jìn)行容器編排和管理。

安全: 使用 HTTPS 保護(hù)數(shù)據(jù)傳輸,實(shí)現(xiàn)用戶認(rèn)證和授權(quán),防止跨站腳本攻擊(XSS)等。

可擴(kuò)展性: 使用水平擴(kuò)展策略,根據(jù)負(fù)載自動(dòng)添加新的服務(wù)器實(shí)例。

高可用性: 使用負(fù)載均衡、故障轉(zhuǎn)移和備份策略來保證系統(tǒng)的高可用性。

緩存: 使用緩存技術(shù)(如 Redis)提高系統(tǒng)性能,減輕數(shù)據(jù)庫壓力。

消息隊(duì)列: 使用消息隊(duì)列(如 Kafka、RabbitMQ)處理異步任務(wù)和消息通知。

實(shí)時(shí)性能優(yōu)化: 使用 CDN(內(nèi)容分發(fā)網(wǎng)絡(luò))加速消息傳遞,減少網(wǎng)絡(luò)延遲。

4. 擴(kuò)展和優(yōu)化:

不斷監(jiān)控和優(yōu)化系統(tǒng)性能,采用水平擴(kuò)展和負(fù)載均衡策略,及時(shí)處理熱點(diǎn)問題,定期進(jìn)行數(shù)據(jù)庫性能優(yōu)化,使用緩存技術(shù)提高系統(tǒng)響應(yīng)速度。

2.項(xiàng)目優(yōu)化

3.大數(shù)據(jù)量壓測調(diào)優(yōu)

一億用戶量,平均每人每天 10 次的業(yè)務(wù)量,要求并發(fā)數(shù)在 5000 以上,峰值在 5w 到 10w 之間,QPS 在 25w 以上, 如何進(jìn)行壓測?如何進(jìn)行調(diào)優(yōu)?

4.查詢時(shí)間幾十秒?

1.問題描述

在某個(gè)大型科技公司用戶中臺(tái)中,組織機(jī)構(gòu)接口的 RT 時(shí)間為 70s。

一般系統(tǒng) RT 要求在 500ms 內(nèi),并且越短越好。

2.問題模擬

由于接口查詢耗時(shí)太長,當(dāng)前僅采用單機(jī) jmeter 進(jìn)行壓測,模擬 100QPS 的執(zhí)行情況,

【技術(shù)驛站】分布式基礎(chǔ)與常見面試問題,s10 分布式,分布式

3.優(yōu)化手段

通過優(yōu)化數(shù)據(jù)庫表結(jié)構(gòu)、使用緩存技術(shù)、合理配置網(wǎng)關(guān)等手段,我們可以顯著改善組織機(jī)構(gòu)樹查詢的效率和用戶體驗(yàn)。常見的優(yōu)化思路如下:

  1. 統(tǒng)一數(shù)據(jù)源:建立一個(gè)中心化的組織機(jī)構(gòu)數(shù)據(jù)源,將所有部門和團(tuán)隊(duì)的組織機(jī)構(gòu)數(shù)據(jù)集中存儲(chǔ),確保數(shù)據(jù)的一致性和準(zhǔn)確性。
  2. 數(shù)據(jù)庫優(yōu)化:設(shè)計(jì)合適的數(shù)據(jù)庫表結(jié)構(gòu)和索引,以支持高效的組織機(jī)構(gòu)查詢操作。使用合適的數(shù)據(jù)庫技術(shù)和優(yōu)化方法,如垂直拆分或水平拆分等,來提高查詢性能。
  3. 緩存機(jī)制:使用緩存來存儲(chǔ)組織機(jī)構(gòu)數(shù)據(jù),如 Redis 等。通過合理的緩存策略,如設(shè)置適當(dāng)?shù)倪^期時(shí)間、使用 LRU 算法等,減少對(duì)數(shù)據(jù)庫的頻繁查詢,提高查詢效率。
  4. 分布式架構(gòu):采用分布式架構(gòu),將查詢負(fù)載分散到多個(gè)節(jié)點(diǎn)上,通過水平擴(kuò)展來提高查詢性能和并發(fā)處理能力。
  5. 異步處理:對(duì)于復(fù)雜的組織機(jī)構(gòu)查詢,可以將其放入異步任務(wù)隊(duì)列中處理,減少前端請(qǐng)求的等待時(shí)間,提高系統(tǒng)的并發(fā)處理能力。
  6. 數(shù)據(jù)同步機(jī)制:確保組織機(jī)構(gòu)數(shù)據(jù)的同步和更新的及時(shí)性,使用合適的數(shù)據(jù)同步技術(shù)和機(jī)制,如定時(shí)任務(wù)、事件驅(qū)動(dòng)等,保證數(shù)據(jù)的一致性。
  7. 前端優(yōu)化:采用前端技術(shù)和界面設(shè)計(jì)優(yōu)化,如數(shù)據(jù)分頁加載、懶加載等方式,減少一次性加載大量組織機(jī)構(gòu)數(shù)據(jù)所帶來的性能壓力。
4.數(shù)據(jù)庫層面優(yōu)化

數(shù)據(jù)庫優(yōu)化是解決大型科技公司組織機(jī)構(gòu)查詢問題的重要一環(huán)。下面是一些詳細(xì)的數(shù)據(jù)庫優(yōu)化策略和技術(shù):

  1. 合適的表結(jié)構(gòu)設(shè)計(jì):
  • 根據(jù)組織機(jī)構(gòu)的特點(diǎn),設(shè)計(jì)合適的表結(jié)構(gòu),以支持高效的查詢操作??梢圆捎眠m當(dāng)?shù)年P(guān)系型數(shù)據(jù)庫或者 NoSQL 數(shù)據(jù)庫,根據(jù)具體需求選擇合適的數(shù)據(jù)庫引擎。
  • 使用范式化或者反范式化的設(shè)計(jì)方式,根據(jù)查詢需求和數(shù)據(jù)一致性的要求來決定表結(jié)構(gòu)的規(guī)范化程度。
  • 使用合適的數(shù)據(jù)類型來存儲(chǔ)組織機(jī)構(gòu)數(shù)據(jù),以節(jié)省存儲(chǔ)空間并提高查詢效率。
  1. 索引優(yōu)化:
  • 通過創(chuàng)建合適的索引來加速組織機(jī)構(gòu)查詢。根據(jù)查詢的字段和條件,為經(jīng)常被用于查詢的列創(chuàng)建索引,以提高查詢的速度。
  • 選擇適當(dāng)?shù)乃饕愋?,?B-tree 索引、哈希索引或者全文索引,根據(jù)具體查詢的需求和數(shù)據(jù)的特點(diǎn)做出選擇。
  • 定期進(jìn)行索引維護(hù)和優(yōu)化,包括索引重建、碎片整理等操作,以保持索引的性能。
  1. 查詢優(yōu)化:
  • 編寫高效的查詢語句,使用合適的 SQL 語法和操作符,避免不必要的查詢或子查詢,提高查詢的效率。
  • 使用合適的查詢緩存機(jī)制,如數(shù)據(jù)庫自帶的查詢緩存或者第三方緩存工具,以減少對(duì)數(shù)據(jù)庫的頻繁查詢。
  • 避免過度使用 JOIN 操作,盡量減少關(guān)聯(lián)查詢的復(fù)雜性??紤]使用預(yù)加載或延遲加載的方式,根據(jù)實(shí)際需求來優(yōu)化查詢操作。
  • 分析查詢執(zhí)行計(jì)劃,通過索引優(yōu)化、表結(jié)構(gòu)調(diào)整或者 SQL 重寫等手段,改善查詢執(zhí)行計(jì)劃的性能。
  1. 數(shù)據(jù)分區(qū)和分片:
  • 如果組織機(jī)構(gòu)數(shù)據(jù)量非常大,可以考慮將數(shù)據(jù)分區(qū)或者分片存儲(chǔ)。通過按照一定規(guī)則將數(shù)據(jù)拆分為多個(gè)分區(qū)或者分片,提高查詢的并行度和擴(kuò)展性。
  • 使用分區(qū)表或者分片表的方式,將數(shù)據(jù)均勻地分布到多個(gè)物理存儲(chǔ)設(shè)備或者數(shù)據(jù)庫實(shí)例中,提高查詢的性能和響應(yīng)時(shí)間。
  1. 定期數(shù)據(jù)清理和維護(hù):
  • 定期清理不再使用的數(shù)據(jù),如過期的組織機(jī)構(gòu)信息或者歷史數(shù)據(jù)。通過刪除或者歸檔這些數(shù)據(jù),可以減少數(shù)據(jù)庫的存儲(chǔ)壓力和提高查詢性能。
  • 定期進(jìn)行數(shù)據(jù)庫的維護(hù)工作,包括備份、日志清理、統(tǒng)計(jì)信息更新等操作,以保持?jǐn)?shù)據(jù)庫的健康狀態(tài)和性能
#部門表設(shè)計(jì)
CREATE TABLE `department` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `name` varchar(100) COLLATE utf8mb4_unicode_ci NOT NULL,
  `parent_id` int(11) COLLATE utf8mb4_unicode_ci DEFAULT NULL,
  `remark` varchar(100) COLLATE utf8mb4_unicode_ci DEFAULT NULL,
  `created_at` datetime DEFAULT NULL,
  `deleted` tinyint(1) NOT NULL DEFAULT '0',
  PRIMARY KEY (`id`),
  KEY `department_parent_id_IDX` (`parent_id`) USING BTREE
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='部門';
//java代碼邏輯處理優(yōu)化
public DepartmentDTO findAll() {
    DepartmentDTO departmentDTO = new DepartmentDTO();
    departmentDTO.setId(0L);
    queryChildren(departmentDTO);
    return departmentDTO;
}

// 遞歸查詢
private void queryChildren(DepartmentDTO parent) {
    List<DepartmentPO> children = departmentDao.queryByParentId(parent.getId());
    if (CollectionUtils.isEmpty(children)) {
        return;
    }

    for (DepartmentPO po : children) {
        DepartmentDTO departmentDTO = new DepartmentDTO();
        BeanUtil.copyProperties(po, departmentDTO);
        parent.getChildren().add(departmentDTO);
        queryChildren(departmentDTO);
    }
}
5.應(yīng)用層優(yōu)化

在應(yīng)用服務(wù)里,每次查詢一次性從數(shù)據(jù)庫中獲取所有數(shù)據(jù),在內(nèi)存里排序、拼裝成樹形數(shù)據(jù),

private void queryChildren2(DepartmentDTO root) {
    // 一次性取出所有數(shù)據(jù)
    List<DepartmentPO> list = departmentDao.findAll(Sort.by("parentId", "id").ascending());
    Map<Long, DepartmentDTO> departmentMap = new HashMap<>();
    departmentMap.put(root.getId(), root);
    // 遍歷數(shù)據(jù),組成樹形結(jié)構(gòu)
    for(DepartmentPO po : list) {
        DepartmentDTO departmentDTO = new DepartmentDTO();
        BeanUtil.copyProperties(po, departmentDTO);
        departmentMap.put(po.getId(), departmentDTO);
        DepartmentDTO parent = departmentMap.get(po.getParentId());
        parent.getChildren().add(departmentDTO);
    }
}
6.分布式緩存

通過引入分布式緩存,我們可以將組織機(jī)構(gòu)數(shù)據(jù)緩存在內(nèi)存中,以避免頻繁訪問數(shù)據(jù)庫的開銷。引入分布式緩存是一種高效的優(yōu)化策略,通過減少對(duì)數(shù)據(jù)庫的訪問次數(shù)和加速查詢響應(yīng),可以極大地提升組織機(jī)構(gòu)查詢的效率和響應(yīng)時(shí)間,從而提高整體系統(tǒng)的性能和用戶體驗(yàn)。

public DepartmentDTO findAll() {
    // 查詢緩存
    DepartmentDTO departmentDTO = (DepartmentDTO) redisTemplate.opsForValue().get(DEPARTMENT_CACHE_KEY);
    // 緩存不存在則查詢數(shù)據(jù)庫
    if (Objects.isNull(departmentDTO)) {
        departmentDTO = new DepartmentDTO();
        departmentDTO.setId(0L);
        queryChildren2(departmentDTO);
    }
    // 更新緩存
    redisTemplate.opsForValue().set(DEPARTMENT_CACHE_KEY, departmentDTO, 1, TimeUnit.MINUTES);

    return departmentDTO;
}
7.加入本地緩存

本地緩存是指將數(shù)據(jù)緩存在應(yīng)用程序的內(nèi)存中,以避免頻繁地訪問分布式緩存或數(shù)據(jù)庫,從而快速響應(yīng)查詢請(qǐng)求。

引入本地緩存的好處是可以減少對(duì)分布式緩存的訪問次數(shù),從而降低網(wǎng)絡(luò)延遲和通信開銷。

當(dāng)應(yīng)用程序發(fā)起組織機(jī)構(gòu)查詢時(shí),首先檢查本地緩存中是否存在相應(yīng)的數(shù)據(jù)。如果數(shù)據(jù)在本地緩存中命中,則無需進(jìn)一步訪問分布式緩存或數(shù)據(jù)庫,可以直接返回結(jié)果,極大地提升了查詢的效率和響應(yīng)時(shí)間。

本地緩存的使用需要考慮以下幾個(gè)關(guān)鍵因素:

  1. 緩存策略:選擇合適的緩存策略,如 LRU(最近最少使用)、LFU(最近最不常用)等,以平衡內(nèi)存使用和數(shù)據(jù)命中率。
  2. 緩存更新機(jī)制:在組織機(jī)構(gòu)數(shù)據(jù)發(fā)生變化時(shí),及時(shí)更新本地緩存??梢酝ㄟ^訂閱數(shù)據(jù)庫或分布式緩存的數(shù)據(jù)變更事件,保持本地緩存與數(shù)據(jù)源的一致性。
  3. 緩存失效處理:設(shè)置合理的緩存失效時(shí)間,以確保緩存中的數(shù)據(jù)不過期。可以根據(jù)數(shù)據(jù)更新的頻率和重要性進(jìn)行調(diào)整。
  4. 內(nèi)存管理:合理管理本地緩存使用的內(nèi)存,避免因緩存過多導(dǎo)致內(nèi)存溢出或應(yīng)用程序性能下降的問題。

引入本地緩存的優(yōu)化方案需要綜合考慮應(yīng)用程序的特點(diǎn)、數(shù)據(jù)的更新頻率一致性要求。合理使用本地緩存可以減少對(duì)分布式緩存和數(shù)據(jù)庫的訪問,提升組織機(jī)構(gòu)查詢的效率和響應(yīng)時(shí)間,從而提高系統(tǒng)的整體性能和用戶體驗(yàn)。

<dependency>
    <groupId>com.github.ben-manes.caffeine</groupId>
    <artifactId>caffeine</artifactId>
    <version>3.1.2</version>
</dependency>
@Configuration
@EnableCaching
public class CacheConfig {

    @Bean
    public Caffeine<Object, Object> caffeineConfig() {
        return Caffeine.newBuilder()
                .expireAfterWrite(10, TimeUnit.MINUTES) // 設(shè)置緩存失效時(shí)間為10分鐘
                .maximumSize(10_000) // 設(shè)置緩存的最大容量為10_000
                .recordStats(); // 啟用統(tǒng)計(jì)信息,可通過Cache.stats()獲取緩存命中率等統(tǒng)計(jì)數(shù)據(jù)
    }

    @Bean
    public CacheManager cacheManager(Caffeine<Object, Object> caffeine) {
        CaffeineCacheManager cacheManager = new CaffeineCacheManager();
        cacheManager.setCaffeine(caffeine);
        return cacheManager;
    }
}
@Cacheable("department-cache")
public DepartmentDTO findAll() {
    // 省略其它代碼
}
8.網(wǎng)關(guān)層面優(yōu)化

在網(wǎng)關(guān)層面進(jìn)行優(yōu)化可以提高組織機(jī)構(gòu)樹接口查詢的性能和可擴(kuò)展性。

1.Nginx 壓縮

  • 在 Nginx 配置中啟用壓縮功能,將響應(yīng)數(shù)據(jù)進(jìn)行壓縮,減少數(shù)據(jù)傳輸?shù)拇笮。岣呔W(wǎng)絡(luò)傳輸效率和響應(yīng)速度。
  • 配置gzip指令開啟壓縮,可以設(shè)置合適的壓縮級(jí)別和壓縮類型。
http {
    gzip on;
    gzip_types text/plain text/css application/json;
    gzip_comp_level 5;
}

2.網(wǎng)關(guān)緩存

在 Nginx 中,可以通過配置代理緩存來緩存接口的響應(yīng)結(jié)果。以下是詳細(xì)的步驟:

1.啟用代理緩存:

在 Nginx 的配置文件中,開啟代理緩存功能并定義一個(gè)緩存區(qū)域。

http {
    proxy_cache_path /path/to/cache levels=1:2 keys_zone=my_cache:10m max_size=10g inactive=60m use_temp_path=off;
}
  • /path/to/cache:指定緩存存儲(chǔ)路徑。

  • levels=1:2:定義緩存路徑層級(jí)。

  • keys_zone=my_cache:10m:為緩存區(qū)域指定一個(gè)名稱(my_cache)和分配的內(nèi)存大?。?0MB)。

  • max_size=10g:設(shè)置緩存區(qū)域的最大容量為 10GB。

  • inactive=60m:指定緩存項(xiàng)在 60 分鐘內(nèi)沒有被訪問時(shí)被認(rèn)為是過期的。

  • use_temp_path=off:禁止使用臨時(shí)路徑。

    2.配置代理緩存:

在 Nginx 的配置文件中,為需要緩存的接口添加緩存配置。

server {
    location /api/department {
        proxy_cache my_cache;
        proxy_cache_valid 200 1d;
        proxy_pass http://backend_server;
    }
}
  • proxy_cache my_cache:指定使用前面定義的緩存區(qū)域(my_cache)進(jìn)行緩存。

  • proxy_cache_valid 200 1d:設(shè)置緩存有效期為 1 天,對(duì)于狀態(tài)碼為 200 的響應(yīng)進(jìn)行緩存。

  • proxy_pass http://backend_server:指定反向代理的目標(biāo)后端服務(wù)器。

    3.清除緩存:

如果需要手動(dòng)清除緩存,可以使用 Nginx 的proxy_cache_purge模塊,通過發(fā)送特定請(qǐng)求來清除緩存。

location /purge-cache {
    proxy_cache_purge my_cache "$scheme$request_method$host$request_uri";
}
  • proxy_cache_purge:啟用proxy_cache_purge模塊。
  • my_cache:指定要清除的緩存區(qū)域。
  • "$scheme$request_method$host$request_uri":指定要清除的緩存鍵值。

通過上述配置,Nginx 會(huì)在接收到請(qǐng)求時(shí)先查看緩存,如果緩存中存在對(duì)應(yīng)的響應(yīng),則直接返回緩存的結(jié)果,減少對(duì)后端服務(wù)的請(qǐng)求。如果緩存中不存在或已過期,則會(huì)轉(zhuǎn)發(fā)請(qǐng)求到后端服務(wù),并將響應(yīng)結(jié)果緩存起來,以供后續(xù)相同請(qǐng)求使用。

請(qǐng)注意,緩存接口需要根據(jù)具體的業(yè)務(wù)需求和接口特性進(jìn)行配置。需要根據(jù)接口的頻率、數(shù)據(jù)更新頻率和緩存策略進(jìn)行合理的調(diào)整。

對(duì)于動(dòng)態(tài)生成的樹形數(shù)據(jù)頁面,也可以采用頁面緩存技術(shù),將頁面內(nèi)容緩存到 CDN 或其他緩存中,并設(shè)置合適的緩存過期時(shí)間。

9.調(diào)優(yōu)小結(jié)

針對(duì)組織結(jié)構(gòu)樹的優(yōu)化設(shè)計(jì),我們可以在數(shù)據(jù)庫層面、緩存層面和 NGINX 網(wǎng)關(guān)層面進(jìn)行優(yōu)化,以提高查詢性能和系統(tǒng)的可伸縮性。

在數(shù)據(jù)庫層面,我們可以采取以下優(yōu)化設(shè)計(jì):

  • 使用適當(dāng)?shù)谋斫Y(jié)構(gòu)和索引,優(yōu)化組織結(jié)構(gòu)樹的存儲(chǔ)和查詢效率。
  • 考慮采用合適的分庫分表策略,將數(shù)據(jù)水平拆分,減少單表數(shù)據(jù)量。
  • 針對(duì)組織結(jié)構(gòu)樹的查詢,使用適當(dāng)?shù)牟樵冋Z句和優(yōu)化技巧,如遞歸查詢、嵌套集模型等。

在緩存層面,我們可以采取以下優(yōu)化設(shè)計(jì):

  • 使用 Redis 等緩存技術(shù),將組織結(jié)構(gòu)樹的數(shù)據(jù)緩存在內(nèi)存中,減少對(duì)數(shù)據(jù)庫的訪問次數(shù)。
  • 針對(duì)頻繁訪問的組織結(jié)構(gòu)樹數(shù)據(jù),設(shè)置合適的緩存過期時(shí)間和緩存策略。
  • 使用合理的緩存命名規(guī)則和緩存鍵設(shè)計(jì),確保緩存的準(zhǔn)確性和一致性。

在 NGINX 網(wǎng)關(guān)層面,我們可以采取以下優(yōu)化設(shè)計(jì):

  • 使用 Nginx 的壓縮功能,減小響應(yīng)數(shù)據(jù)的大小,提高網(wǎng)絡(luò)傳輸效率。
  • 使用 CDN 緩存靜態(tài)資源,減輕網(wǎng)關(guān)和后端服務(wù)器的負(fù)載,加速資源傳輸。
  • 配置代理緩存,將組織結(jié)構(gòu)樹接口的響應(yīng)結(jié)果緩存起來,減少對(duì)后端服務(wù)的請(qǐng)求。
  • 設(shè)置適當(dāng)?shù)呢?fù)載均衡機(jī)制,分發(fā)請(qǐng)求到多個(gè)后端服務(wù)實(shí)例,提高系統(tǒng)的可擴(kuò)展性和容錯(cuò)性。

綜合上述優(yōu)化設(shè)計(jì),可以大大提升組織結(jié)構(gòu)樹接口的查詢性能和系統(tǒng)的可伸縮性。通過數(shù)據(jù)庫的優(yōu)化,可以提高數(shù)據(jù)的存儲(chǔ)和查詢效率;通過緩存的優(yōu)化,可以減少對(duì)數(shù)據(jù)庫的訪問次數(shù);通過網(wǎng)關(guān)層面的優(yōu)化,可以降低網(wǎng)絡(luò)傳輸成本和后端服務(wù)的負(fù)載壓力。這些綜合的優(yōu)化措施將顯著改善系統(tǒng)的整體性能和用戶體驗(yàn)。

5.BigKey 問題

1.什么是 BigKey?

在 Redis 中,BigKey 指的是占用大量內(nèi)存空間的鍵值對(duì)。當(dāng)一個(gè)鍵值對(duì)的數(shù)據(jù)量過大,超過 Redis 的配置限制或者內(nèi)存容量時(shí),就稱之為 BigKey。

2.BigKey 導(dǎo)致的問題

BigKey 會(huì)導(dǎo)致以下問題:

  1. 內(nèi)存占用過高:BigKey 占用大量內(nèi)存空間,可能導(dǎo)致 Redis 服務(wù)器的內(nèi)存資源緊張,影響其他操作和緩存數(shù)據(jù)的存儲(chǔ)。
  2. 延遲和性能下降:由于讀寫大型 BigKey 的數(shù)據(jù)需要較長的時(shí)間,會(huì)導(dǎo)致讀寫操作的延遲增加,并且消耗更多的 CPU 資源和網(wǎng)絡(luò)帶寬,影響系統(tǒng)的整體性能。
  3. Redis 持久化和備份問題:BigKey 在進(jìn)行持久化(如 RDB 快照或 AOF 日志)或備份時(shí),會(huì)增加持久化和備份的時(shí)間和資源消耗。
3.解決方案

解決 BigKey 問題的一些優(yōu)化方法包括:

  1. 數(shù)據(jù)拆分:將大型 BigKey 拆分成多個(gè)較小的鍵值對(duì),根據(jù)數(shù)據(jù)的邏輯關(guān)聯(lián)性和查詢模式進(jìn)行合理的拆分和分組。這樣可以降低單個(gè)鍵值對(duì)的大小,減少內(nèi)存占用。
  2. 分片存儲(chǔ):將數(shù)據(jù)分散存儲(chǔ)在多個(gè)鍵中,使用合適的哈希函數(shù)或者分片算法,將數(shù)據(jù)均勻地分散到不同的鍵中,避免單個(gè)鍵值對(duì)過大。這樣可以平衡數(shù)據(jù)的存儲(chǔ)和訪問壓力。
  3. 數(shù)據(jù)壓縮:對(duì)于可以壓縮的數(shù)據(jù)類型(如字符串或 JSON 格式的數(shù)據(jù)),可以使用壓縮算法(如 LZF、Snappy、Gzip 等)進(jìn)行壓縮,減少 BigKey 的內(nèi)存占用。
  4. 數(shù)據(jù)分頁和增量加載:對(duì)于查詢結(jié)果過大的 BigKey,可以采用數(shù)據(jù)分頁或者增量加載的方式,只加載部分?jǐn)?shù)據(jù)或者按需加載,以降低單次查詢的數(shù)據(jù)量和內(nèi)存消耗。
  5. 避免無意義的數(shù)據(jù)存儲(chǔ):避免將無需持久化或頻繁變更的數(shù)據(jù)存儲(chǔ)為 BigKey,可以使用臨時(shí)緩存或者其他數(shù)據(jù)存儲(chǔ)方案。

通過合理的數(shù)據(jù)拆分、分片存儲(chǔ)和壓縮等優(yōu)化方法,可以有效地解決 BigKey 問題,提高 Redis 的性能和資源利用率。

因?yàn)樵诟?jié)點(diǎn)下的第一級(jí)優(yōu) 10 個(gè)組織,這 10 個(gè)組織分別有不同的子孫節(jié)點(diǎn),我們可以嘗試將單個(gè) key 先拆分成 10 個(gè) key,每個(gè) key 分別對(duì)應(yīng)這 10 個(gè)組織及子節(jié)點(diǎn)數(shù)據(jù)。

public DepartmentDTO findAll() {
    DepartmentDTO root = new DepartmentDTO();
    root.setId(0L);
    // 從10個(gè)key里獲取緩存數(shù)據(jù),放入到根節(jié)點(diǎn)的children集合里
    for (int i = 0; i< 10; i++) {
        DepartmentDTO child = (DepartmentDTO)  redisTemplate.opsForValue().get(DEPARTMENT_CACHE_KEY + i);
        // 沒有緩存數(shù)據(jù)
        if (Objects.isNull(child)) {
            root.setChildren(new ArrayList<>());
            break;
        }
        root.getChildren().add(child);
    }

    if (root.getChildren().size() == 0) {
       // 查詢數(shù)據(jù)庫
       queryChildren2(root);
    }

    // 將數(shù)據(jù)按照1級(jí)部門存儲(chǔ)到10個(gè)key
    List<DepartmentDTO> children = root.getChildren();
    for (int i = 0 ; i < 10; i++) {
        redisTemplate.opsForValue().set(DEPARTMENT_CACHE_KEY + i, children.get(i), 1, TimeUnit.HOURS);
    }
    return root;
}

6.重復(fù)提交問題?

重復(fù)提交問題是指用戶在某個(gè)操作或請(qǐng)求上多次點(diǎn)擊提交按鈕,從而導(dǎo)致重復(fù)執(zhí)行同一操作或請(qǐng)求的情況。這可能會(huì)引發(fā)各種問題,例如重復(fù)訂單、數(shù)據(jù)重復(fù)插入等。下面是一些解決重復(fù)提交問題的常見方法:

  1. 前端控制

    • 禁用提交按鈕:在用戶點(diǎn)擊一次提交按鈕后,禁用該按鈕,防止用戶連續(xù)點(diǎn)擊。
    • 顯示加載狀態(tài):在提交后,顯示一個(gè)加載指示器,讓用戶知道操作正在進(jìn)行中,從而減少他們的不必要點(diǎn)擊。
  2. 后端控制

    • 生成唯一標(biāo)識(shí)符:在每次請(qǐng)求時(shí),在后端生成一個(gè)唯一標(biāo)識(shí)符(如 Token),將其包含在請(qǐng)求中。服務(wù)器在處理請(qǐng)求后,可以檢查該標(biāo)識(shí)符是否已被使用,從而防止重復(fù)提交。
    • 請(qǐng)求限制:通過某種機(jī)制(例如 IP 地址、用戶會(huì)話等)限制用戶在特定時(shí)間內(nèi)只能提交一次請(qǐng)求,從而防止頻繁提交。
    • 冪等操作:設(shè)計(jì)操作為冪等,即無論操作執(zhí)行多少次,結(jié)果都是一致的。這樣,就不會(huì)因?yàn)橹貜?fù)執(zhí)行而產(chǎn)生問題。
  3. 時(shí)間戳和過期處理

    • 記錄時(shí)間戳:在用戶提交請(qǐng)求時(shí),記錄一個(gè)時(shí)間戳。后端在處理請(qǐng)求時(shí),可以檢查時(shí)間戳,如果距離上一次請(qǐng)求時(shí)間太短,則拒絕請(qǐng)求。
    • 設(shè)置有效期:為每個(gè)操作設(shè)置一個(gè)有效期,在有效期內(nèi)同一操作請(qǐng)求將被視為重復(fù)請(qǐng)求并被拒絕。
  4. 前后端協(xié)作

    • 雙重確認(rèn):在用戶提交后,彈出確認(rèn)對(duì)話框,要求用戶再次確認(rèn)是否要執(zhí)行操作,從而減少誤操作的可能性。
    • 提交完成頁面跳轉(zhuǎn):在操作成功后,將用戶重定向到一個(gè)新頁面,這樣刷新頁面不會(huì)再次提交相同的請(qǐng)求。
  5. 防止重復(fù)提交的庫和框架

    • 許多現(xiàn)代的 Web 框架和庫提供了防止重復(fù)提交的解決方案,例如使用單次令牌(one-time token)、防止多次點(diǎn)擊等機(jī)制。你可以查閱相關(guān)文檔了解如何在你的技術(shù)棧中應(yīng)用這些解決方案。
  6. 測試和監(jiān)控

    • 在開發(fā)過程中進(jìn)行測試,模擬快速點(diǎn)擊、重復(fù)請(qǐng)求等情況,確保系統(tǒng)能夠正確處理這些情況。
    • 監(jiān)控系統(tǒng)日志,查找重復(fù)提交問題的發(fā)生頻率和原因,及時(shí)采取措施解決。

綜合考慮以上方法,可以根據(jù)具體情況選擇適合你的應(yīng)用場景的解決方案,以減少或防止重復(fù)提交問題的發(fā)生。

7.接口冪等性設(shè)計(jì)?

接口的冪等性設(shè)計(jì)是指無論調(diào)用多少次,其產(chǎn)生的結(jié)果都是相同的,不會(huì)因?yàn)槎啻握{(diào)用而引發(fā)不一致或重復(fù)的操作。在設(shè)計(jì) API 接口時(shí),考慮到冪等性非常重要,特別是在網(wǎng)絡(luò)通信不穩(wěn)定、客戶端重試請(qǐng)求、系統(tǒng)故障等情況下。

以下是一些設(shè)計(jì)原則和方法來確保接口的冪等性:

  1. 使用唯一標(biāo)識(shí)符

    • 在每個(gè)請(qǐng)求中使用唯一標(biāo)識(shí)符(例如請(qǐng)求 ID、隨機(jī)生成的 UUID 等)。
    • 在服務(wù)器端檢查標(biāo)識(shí)符,以確保重復(fù)請(qǐng)求不會(huì)重復(fù)執(zhí)行操作。
  2. HTTP 方法的選擇

    • 使用合適的 HTTP 方法:GETHEAD、OPTIONS 是冪等的,而 POST、PUT、DELETE 則可能會(huì)改變資源狀態(tài),需要額外處理冪等性。
  3. Idempotency-Key 頭部

    • 客戶端可以在請(qǐng)求頭部添加一個(gè)自定義的 Idempotency-Key(冪等 key),用于標(biāo)識(shí)請(qǐng)求的冪等性。
    • 服務(wù)器端可以根據(jù)這個(gè)頭部來檢查重復(fù)請(qǐng)求。
  4. 冪等性驗(yàn)證

    • 在服務(wù)器端,對(duì)于可能會(huì)引起狀態(tài)變化的操作,檢查操作的影響,確保相同的請(qǐng)求不會(huì)產(chǎn)生不一致的結(jié)果。
  5. 狀態(tài)檢查和變更

    • 在處理請(qǐng)求之前,檢查資源的當(dāng)前狀態(tài),然后再?zèng)Q定是否執(zhí)行狀態(tài)變更操作,以避免重復(fù)的變更。
  6. 使用事務(wù)

    • 對(duì)于需要多個(gè)步驟的操作,使用事務(wù)來確保操作的原子性,從而避免部分操作成功、部分操作失敗的情況。
  7. 返回結(jié)果和響應(yīng)碼

    • 對(duì)于冪等請(qǐng)求,不管操作是否執(zhí)行,響應(yīng)的結(jié)果應(yīng)該一致。響應(yīng)碼可以根據(jù)操作是否成功來區(qū)分。
  8. 冪等性測試

    • 在開發(fā)過程中,編寫測試用例來模擬重復(fù)請(qǐng)求,確保接口的冪等性。
  9. 數(shù)據(jù)驗(yàn)證和處理

    • 對(duì)于數(shù)據(jù)插入、更新等操作,使用唯一約束條件判斷來防止重復(fù)數(shù)據(jù)插入。
  10. 日志和審計(jì)

    • 記錄所有接口請(qǐng)求響應(yīng),包括請(qǐng)求參數(shù)、響應(yīng)結(jié)果處理時(shí)間,以便后續(xù)排查重復(fù)請(qǐng)求的問題。

在設(shè)計(jì)冪等性時(shí),需要考慮到具體的業(yè)務(wù)場景和操作特點(diǎn),選擇適合的方法和策略。不同的接口可能需要不同的處理方式來保證冪等性。

8.超賣解決方案

超賣是指在電子商務(wù)或庫存管理系統(tǒng)中,同一商品被售出的數(shù)量超過了實(shí)際庫存的數(shù)量,從而導(dǎo)致庫存不一致的情況。這可能會(huì)給用戶和商家?guī)砝_。以下是一些解決超賣問題的常見方案:

  1. 悲觀鎖

    • 在更新庫存時(shí),使用數(shù)據(jù)庫的悲觀鎖機(jī)制,例如使用數(shù)據(jù)庫事務(wù)的排他鎖(SELECT … FOR UPDATE)。
    • 在進(jìn)行庫存檢查和更新操作之間,鎖定對(duì)應(yīng)的庫存記錄,確保其他事務(wù)無法同時(shí)操作,從而避免超賣。
  2. 樂觀鎖

    • 在更新庫存時(shí),使用樂觀鎖機(jī)制,例如在庫存記錄中添加版本號(hào)字段。
    • 檢查更新前的版本號(hào)和當(dāng)前數(shù)據(jù)庫中的版本號(hào)是否一致,如果一致則執(zhí)行更新,否則放棄更新。
  3. 隊(duì)列和異步處理

    • 將訂單和庫存更新操作放入消息隊(duì)列中,然后使用消費(fèi)者來處理訂單并更新庫存。
    • 這可以避免多個(gè)請(qǐng)求同時(shí)訪問庫存,通過隊(duì)列的方式進(jìn)行順序處理,減少超賣的可能性。
  4. 限制購買數(shù)量

    • 對(duì)用戶進(jìn)行限制,例如每次只能購買一個(gè)商品或限制購買數(shù)量不超過庫存數(shù)量。
    • 在前端和后端都進(jìn)行驗(yàn)證,防止用戶提交超過限制數(shù)量的請(qǐng)求。
  5. 并發(fā)控制和緩存

    • 使用分布式鎖或緩存來控制并發(fā)訪問,確保同一時(shí)刻只有一個(gè)請(qǐng)求能夠進(jìn)行庫存更新。
    • 當(dāng)一個(gè)請(qǐng)求正在更新庫存時(shí),其他請(qǐng)求需要等待或返回錯(cuò)誤信息。
  6. 預(yù)扣庫存

    • 在用戶下訂單時(shí),先將相應(yīng)數(shù)量的庫存進(jìn)行預(yù)扣除,但不立即更新實(shí)際庫存。
    • 在訂單支付完成后,再正式扣除庫存。如果支付失敗,釋放預(yù)扣的庫存。
  7. 事務(wù)和回滾

    • 使用數(shù)據(jù)庫事務(wù),將檢查庫存和更新庫存操作放在同一個(gè)事務(wù)中。
    • 如果庫存不足或其他異常,可以回滾事務(wù),確保庫存和訂單的一致性。
  8. 監(jiān)控和報(bào)警

    • 設(shè)置監(jiān)控機(jī)制,實(shí)時(shí)監(jiān)控庫存的變化和訂單的處理情況。
    • 當(dāng)庫存出現(xiàn)異常情況時(shí),及時(shí)報(bào)警并進(jìn)行處理。

不同的解決方案適用于不同的場景和需求,通常需要根據(jù)業(yè)務(wù)需求和系統(tǒng)架構(gòu)來綜合考慮選擇哪些方案或者采用多種方案組合來解決超賣問題。

9.消息推送的設(shè)計(jì)?

推送系統(tǒng)設(shè)計(jì)架構(gòu)

  1. 通知客戶端
  2. 通知服務(wù)
  3. 模板服務(wù)
  4. 消息分發(fā)服務(wù)
  5. 事件優(yōu)先級(jí)隊(duì)列(消息隊(duì)列)
  6. 通用出站處理程序
  7. 通知適配器
  8. 通道供應(yīng)商
  9. 用戶選擇服務(wù)
  10. 用戶配置文件服務(wù)
  11. 分析服務(wù)
  12. 通知跟蹤器
  13. 通知數(shù)據(jù)庫:Mysql 數(shù)據(jù)庫集群

【技術(shù)驛站】分布式基礎(chǔ)與常見面試問題,s10 分布式,分布式

10.多級(jí)緩存

多級(jí)緩存架構(gòu)的注意事項(xiàng):

  1. 由于本地緩存會(huì)占用 Java 進(jìn)程的 JVM 內(nèi)存空間,因此不適合存儲(chǔ)大量數(shù)據(jù),需要對(duì)緩存大小進(jìn)行評(píng)估。
  2. 如果業(yè)務(wù)能夠接受短時(shí)間內(nèi)的數(shù)據(jù)不一致,那么本地緩存更適用于讀取場景。
  3. 在緩存更新策略中,無論是主動(dòng)更新還是被動(dòng)更新,本地緩存都應(yīng)設(shè)置有效期。
  4. 考慮設(shè)置定時(shí)任務(wù)來同步緩存,以防止極端情況下數(shù)據(jù)丟失。
  5. 在 RPC 調(diào)用中,需要避免本地緩存被污染,可以通過合理的緩存淘汰策略,來解決這個(gè)問題。
  6. 當(dāng)應(yīng)用重啟時(shí),本地緩存會(huì)失效,因此需要注意加載分布式緩存的時(shí)機(jī)。
  7. 通過發(fā)布/訂閱解決數(shù)據(jù)一致性問題時(shí),如果發(fā)布/訂閱模式不持久化消息數(shù)據(jù),如果消息丟失,本地緩存就會(huì)刪除失敗。所以,要解決發(fā)布訂閱消息的高可用問題。
  8. 當(dāng)本地緩存失效時(shí),需要使用 synchronized 進(jìn)行加鎖,確保由一個(gè)線程加載 Redis 緩存,避免并發(fā)更新。

11.如何給接口限流?

接口限流是一種管理和控制訪問速率的方法,旨在保護(hù)服務(wù)器免受過多請(qǐng)求的影響。以下是一些常見的接口限流方法和策略:

  1. 固定窗口限流: 在一個(gè)固定的時(shí)間窗口內(nèi),只允許一定數(shù)量的請(qǐng)求通過。例如,在每秒鐘內(nèi)只允許 100 個(gè)請(qǐng)求。一旦達(dá)到限制,后續(xù)的請(qǐng)求會(huì)被阻塞或延遲。

  2. 滑動(dòng)窗口限流: 類似于固定窗口限流,但窗口是滑動(dòng)的,可以更靈活地控制請(qǐng)求速率??梢酝ㄟ^限制一個(gè)窗口內(nèi)的請(qǐng)求總數(shù)或平均速率來實(shí)現(xiàn)。

  3. 令牌桶限流: 令牌桶算法基于一個(gè)令牌桶,請(qǐng)求需要消耗令牌才能被處理。令牌以固定速率添加到桶中,如果桶中沒有足夠的令牌,請(qǐng)求將被延遲或拒絕。

  4. 漏桶限流: 漏桶算法類似于令牌桶,但請(qǐng)求以固定速率從“漏桶”中流出。如果請(qǐng)求到達(dá)時(shí)桶為空,請(qǐng)求將被延遲或拒絕。

  5. 動(dòng)態(tài)限流: 基于服務(wù)器負(fù)載、流量情況等動(dòng)態(tài)調(diào)整限流策略,以確保服務(wù)器不會(huì)超負(fù)荷??梢允褂米詣?dòng)擴(kuò)展、縮減限流速率等方式。

  6. 分布式限流: 適用于分布式系統(tǒng)的限流策略,可以通過共享限流狀態(tài)、中心化控制、令牌傳遞等方式來實(shí)現(xiàn)全局限流。

  7. 熔斷: 在服務(wù)出現(xiàn)故障或性能下降時(shí),可以關(guān)閉接口,避免對(duì)下游服務(wù)造成更大影響,直到服務(wù)恢復(fù)正常。

  8. 負(fù)載均衡: 將流量分散到多個(gè)服務(wù)器上,以避免單一服務(wù)器負(fù)載過高。

要實(shí)現(xiàn)接口限流,你可以使用諸如限流庫、反向代理(如 Nginx)、API 網(wǎng)關(guān)(如 Spring Cloud Gateway、Kong)等工具或技術(shù)。不同的技術(shù)和框架提供不同的限流實(shí)現(xiàn)方法,具體取決于你的技術(shù)棧和需求。

九.解決方案

1.數(shù)據(jù)遷移方案

數(shù)據(jù)遷移執(zhí)行流程,大概有 10 個(gè)步驟:

  1. 環(huán)境準(zhǔn)備:線上庫配置完成
  2. 全量同步:數(shù)據(jù)遷移工具上新建 2 張表(積分表、明細(xì)表)的全量任務(wù)
  3. 增量同步:全量遷移完成后開啟增量(自動(dòng)回溯全量開始時(shí)間,消息多次消費(fèi)會(huì)進(jìn)行冪等)
  4. 數(shù)據(jù)校驗(yàn):全量數(shù)據(jù)校驗(yàn),查看數(shù)據(jù)是否一致
  5. 切流測試:改造代碼預(yù)發(fā)測試(采集線上流量進(jìn)行回放,多種 case 跑一下,切流開關(guān)等校驗(yàn)),沒問題發(fā)布上線
  6. 二次校驗(yàn):再次全量進(jìn)行校驗(yàn)&訂正(數(shù)據(jù)追平)
  7. 開啟雙寫:打開雙寫(保證數(shù)據(jù)實(shí)時(shí)性)既寫老庫,又寫新庫
  8. 開啟讀灰度:低峰時(shí)段,進(jìn)行灰度切流userId%x,進(jìn)行驗(yàn)證,逐步流量打開,持續(xù)觀察
  9. 只寫新庫:寫流量切到新庫,只寫新庫,不寫老庫。完成數(shù)據(jù)遷移方案
  10. 遷移完成:系統(tǒng)穩(wěn)定運(yùn)行一段時(shí)間,遷移&雙寫代碼下線,老庫進(jìn)行資源釋放

2.64 億的 url,如何去重?

處理 64 億個(gè) URL 并在只分配 1GB 內(nèi)存的情況下進(jìn)行去重是一個(gè)非常具有挑戰(zhàn)性的任務(wù)。由于數(shù)據(jù)量巨大,傳統(tǒng)的內(nèi)存去重方法可能會(huì)受到內(nèi)存限制,并且可能需要更復(fù)雜的策略來實(shí)現(xiàn)高效的去重。

以下是一些可能的方法和建議:

  1. 分批處理: 將數(shù)據(jù)分為多個(gè)較小的批次,每次只加載一批數(shù)據(jù)到內(nèi)存中進(jìn)行去重,然后將結(jié)果寫回到數(shù)據(jù)庫。這可以減少單次加載的內(nèi)存壓力。

  2. 哈希集合: 使用哈希集合(例如 Java 中的 HashSet)來存儲(chǔ)已經(jīng)出現(xiàn)過的 URL。由于內(nèi)存有限,你可能需要考慮使用一些壓縮技術(shù)或哈希函數(shù)來降低內(nèi)存占用。注意,哈希集合可能會(huì)有一些沖突,需要額外的處理。

  3. 位圖: 如果 URL 的范圍有限,你可以使用位圖(Bitmap)來表示 URL 是否存在。這可以顯著減少內(nèi)存占用,但僅適用于特定情況。

  4. 外部存儲(chǔ): 如果內(nèi)存非常有限,你可以考慮使用外部存儲(chǔ),如硬盤或者分布式存儲(chǔ),來進(jìn)行去重操作。這可能會(huì)涉及更多的復(fù)雜性和性能開銷。

  5. 使用數(shù)據(jù)庫功能: 一些數(shù)據(jù)庫系統(tǒng)具有內(nèi)置的去重功能,你可以探索你正在使用的數(shù)據(jù)庫是否提供類似的功能。

  6. 采樣和統(tǒng)計(jì): 如果只需要大致估計(jì)去重后的 URL 數(shù)量,你可以考慮對(duì)數(shù)據(jù)進(jìn)行采樣,然后統(tǒng)計(jì)不同 URL 的出現(xiàn)次數(shù),從而得出去重后的近似數(shù)量。

需要根據(jù)具體情況綜合考慮選擇合適的方法。請(qǐng)注意,處理如此大規(guī)模的數(shù)據(jù)和內(nèi)存限制可能需要一定的技術(shù)深度和優(yōu)化經(jīng)驗(yàn)。最好在實(shí)際環(huán)境中進(jìn)行實(shí)驗(yàn)和測試,以找到最適合你情況的解決方案。

3.什么是腦裂問題?

腦裂問題(Split-Brain Problem)是分布式系統(tǒng)中的一個(gè)嚴(yán)重的一致性問題。它發(fā)生在具有多個(gè)節(jié)點(diǎn)或副本的分布式系統(tǒng)中,當(dāng)網(wǎng)絡(luò)通信發(fā)生故障或延遲時(shí),可能導(dǎo)致系統(tǒng)的不一致性和錯(cuò)誤操作。腦裂問題的名字源自于類似于“大腦被劈開”的圖像,其中分布式系統(tǒng)被分割成多個(gè)獨(dú)立的部分。

腦裂問題的主要原因是分布式系統(tǒng)中節(jié)點(diǎn)之間的通信中斷或延遲,導(dǎo)致不同節(jié)點(diǎn)之間無法及時(shí)同步數(shù)據(jù)和狀態(tài)。這可能會(huì)導(dǎo)致以下問題:

  1. 數(shù)據(jù)不一致: 不同節(jié)點(diǎn)之間無法及時(shí)更新數(shù)據(jù),導(dǎo)致不同節(jié)點(diǎn)上的數(shù)據(jù)不一致。例如,在數(shù)據(jù)庫復(fù)制中,一個(gè)節(jié)點(diǎn)可能更新了數(shù)據(jù),但由于網(wǎng)絡(luò)故障,其他節(jié)點(diǎn)無法及時(shí)同步,從而導(dǎo)致數(shù)據(jù)不一致。

  2. 雙重提交: 當(dāng)節(jié)點(diǎn)之間通信故障后,可能會(huì)導(dǎo)致同一個(gè)操作被多次提交。例如,在分布式事務(wù)中,如果一個(gè)事務(wù)提交了,但由于通信問題,協(xié)調(diào)節(jié)點(diǎn)沒有收到確認(rèn),那么協(xié)調(diào)節(jié)點(diǎn)可能會(huì)重試提交,從而導(dǎo)致雙重提交。

  3. 資源競爭: 腦裂可能導(dǎo)致資源的競爭和沖突。例如,多個(gè)節(jié)點(diǎn)認(rèn)為自己是主節(jié)點(diǎn),同時(shí)操作相同的資源,導(dǎo)致資源沖突。

4.如何解決腦裂問題?

為了解決腦裂問題,分布式系統(tǒng)需要采取一些策略和機(jī)制:

  1. 心跳檢測: 使用心跳檢測來監(jiān)測節(jié)點(diǎn)的健康狀態(tài)。如果一個(gè)節(jié)點(diǎn)被認(rèn)為不健康,可以將其從集群中隔離,以避免不一致性。

  2. Quorum 和多數(shù)投票: 使用 Quorum 原則來決定是否執(zhí)行某個(gè)操作。只有在多數(shù)節(jié)點(diǎn)同意的情況下,才能執(zhí)行操作,這有助于防止雙重提交和資源競爭。

  3. 選主機(jī)制: 使用選主(Leader Election)機(jī)制來確保在任何時(shí)候只有一個(gè)主節(jié)點(diǎn)對(duì)外提供服務(wù),從而避免資源沖突和數(shù)據(jù)不一致。

  4. 超時(shí)和重試: 在通信發(fā)生故障時(shí),使用合理的超時(shí)和重試機(jī)制,以避免因網(wǎng)絡(luò)延遲導(dǎo)致的誤判。

  5. 分區(qū)容錯(cuò): 將分布式系統(tǒng)設(shè)計(jì)為分區(qū)容錯(cuò),即在面對(duì)網(wǎng)絡(luò)分區(qū)時(shí)能夠保持一致性。這可以通過一致性算法如 Paxos、Raft 等來實(shí)現(xiàn)。

腦裂問題在分布式系統(tǒng)中是一個(gè)復(fù)雜且需要謹(jǐn)慎處理的問題。選擇合適的一致性算法、網(wǎng)絡(luò)配置和節(jié)點(diǎn)通信策略,以及實(shí)施適當(dāng)?shù)墓收蠙z測和恢復(fù)機(jī)制,都是減輕腦裂問題影響的關(guān)鍵。

5.什么是雪花算法?

雪花算法生成的 ID 由哪些部分組成?

  1. 符號(hào)位,占用 1 位。
  2. 時(shí)間戳,占用 41 位,可以支持 69 年的時(shí)間跨度。
  3. 機(jī)器 ID,占用 10 位。
  4. 序列號(hào),占用 12 位。一毫秒可以生成 4095 個(gè) ID。

【技術(shù)驛站】分布式基礎(chǔ)與常見面試問題,s10 分布式,分布式

6.雪花算法時(shí)鐘回?fù)軉栴}?

雪花算法(Snowflake Algorithm)是一種分布式唯一 ID 生成算法,通常用于分布式系統(tǒng)中生成全局唯一的 ID。該算法的核心思想是將一個(gè) 64 位的整數(shù) ID 分成不同的部分,其中包括時(shí)間戳、機(jī)器 ID、數(shù)據(jù)中心 ID 和序列號(hào)等字段,以確保生成的 ID 在分布式環(huán)境中是唯一的。

問題的關(guān)鍵點(diǎn)是時(shí)鐘回?fù)?,這是指服務(wù)器的系統(tǒng)時(shí)間被調(diào)整回到了過去。時(shí)鐘回?fù)芸赡軙?huì)導(dǎo)致生成重復(fù)的 ID,因?yàn)檠┗ㄋ惴ㄖ械臅r(shí)間戳部分通常用來確保 ID 的唯一性。如果系統(tǒng)時(shí)間回?fù)艿搅酥耙呀?jīng)生成過的時(shí)間戳,那么在同一毫秒內(nèi)生成的 ID 可能會(huì)相同。

為了解決時(shí)鐘回?fù)軉栴},可以考慮以下幾種方法:

  1. 時(shí)鐘同步: 確保服務(wù)器的時(shí)鐘是同步的,可以使用網(wǎng)絡(luò)時(shí)間協(xié)議(NTP)等工具來同步系統(tǒng)時(shí)間,減小時(shí)鐘回?fù)艿目赡苄浴?/p>

  2. 時(shí)鐘漂移檢測: 在生成 ID 時(shí),檢測當(dāng)前時(shí)間戳是否小于上一次生成的時(shí)間戳,如果是,則表示發(fā)生了時(shí)鐘回?fù)?,可以等待一段時(shí)間再生成 ID,或者采取其他措施來處理。

  3. 保留序列號(hào): 在雪花算法中,通常有一部分位用來表示序列號(hào)。當(dāng)發(fā)生時(shí)鐘回?fù)軙r(shí),可以通過遞增序列號(hào)來確保生成的 ID 不會(huì)重復(fù)。

  4. 使用更高精度的時(shí)間戳: 使用更高精度時(shí)間戳(例如毫秒級(jí)別或微秒級(jí)別)可以減小在同一時(shí)間戳下生成重復(fù) ID 的概率。

  5. 記錄時(shí)鐘回?fù)苁录?/strong> 如果檢測到時(shí)鐘回?fù)?,可以記錄事件并采取適當(dāng)?shù)奶幚聿呗?,例如等待一段時(shí)間再生成 ID,或者生成一個(gè)特殊標(biāo)識(shí)的 ID 來表示時(shí)鐘回?fù)苁录?/p>

處理時(shí)鐘回?fù)軉栴}需要根據(jù)具體的應(yīng)用場景和要求來選擇合適的方法。不同的解決方案可以根據(jù)系統(tǒng)的可用性、性能要求和復(fù)雜性進(jìn)行權(quán)衡。

7.關(guān)于語雀掛了思考?

  • 故障原因簡單來說就是新的運(yùn)維升級(jí)工具 bug 帶來的一系列影響。其實(shí),究其根本原因還是高可用架構(gòu)體系設(shè)計(jì)、運(yùn)維以及項(xiàng)目規(guī)范,甚至是團(tuán)隊(duì)管理和制度上存在嚴(yán)幣的問題需要去改進(jìn)和完善。
  • 這件事兒也給大家提了個(gè)醒,自己寫的文檔,記得還是在本地留存一份。
  • 當(dāng)場拔網(wǎng)線,模擬火災(zāi)報(bào)警,地震來襲,光纖挖斷啥的,你以為這些是開玩笑的?
  • 作為程序員,大家聊到這里的時(shí)候,一遍都會(huì)談到高可用、容災(zāi)備份、兩地三中心、異地多活、同城雙活

覺得有用的話點(diǎn)個(gè)贊 ???? 唄。
??????本人水平有限,如有紕漏,歡迎各位大佬評(píng)論批評(píng)指正!??????

??????如果覺得這篇文對(duì)你有幫助的話,也請(qǐng)給個(gè)點(diǎn)贊、收藏下吧,非常感謝!?? ?? ??

??????Stay Hungry Stay Foolish 道阻且長,行則將至,讓我們一起加油吧!??????

【技術(shù)驛站】分布式基礎(chǔ)與常見面試問題,s10 分布式,分布式文章來源地址http://www.zghlxwxcb.cn/news/detail-759041.html

到了這里,關(guān)于【技術(shù)驛站】分布式基礎(chǔ)與常見面試問題的文章就介紹完了。如果您還想了解更多內(nèi)容,請(qǐng)?jiān)谟疑辖撬阉鱐OY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!

本文來自互聯(lián)網(wǎng)用戶投稿,該文觀點(diǎn)僅代表作者本人,不代表本站立場。本站僅提供信息存儲(chǔ)空間服務(wù),不擁有所有權(quán),不承擔(dān)相關(guān)法律責(zé)任。如若轉(zhuǎn)載,請(qǐng)注明出處: 如若內(nèi)容造成侵權(quán)/違法違規(guī)/事實(shí)不符,請(qǐng)點(diǎn)擊違法舉報(bào)進(jìn)行投訴反饋,一經(jīng)查實(shí),立即刪除!

領(lǐng)支付寶紅包贊助服務(wù)器費(fèi)用

相關(guān)文章

覺得文章有用就打賞一下文章作者

支付寶掃一掃打賞

博客贊助

微信掃一掃打賞

請(qǐng)作者喝杯咖啡吧~博客贊助

支付寶掃一掃領(lǐng)取紅包,優(yōu)惠每天領(lǐng)

二維碼1

領(lǐng)取紅包

二維碼2

領(lǐng)紅包