不如瞭解一下分佈式系統?

分佈式

什麼是分佈式?什麼是微服務?

  1. 分佈式是把項目分佈到多個服務器中,每個服務器可能運行一個或多個業務。微服務是把項目切分成若干個可獨立運行的微服務,可以分佈在一個或多個服務器上。本質上說,是兩個不同層面的東西,但是因爲實際工程中,微服務常常要利用到分佈式。所以:微服務是架構設計方式,分佈式是系統部署方式
  2. 微服務框架邏輯視圖

    REST其實就是向外界提供了由一個語言無關的調用API,因爲微服務就是一個獨立的服務,任何請求都可以調用。微服務因爲要被調用,因此要實現RPC。微服務本身可能需要集羣或者分佈式部署。一個大項目可以切分成很多微服務,然後讓不同的團隊進行開發,這就是微服務架構。
  3. 集羣是把同一個業務拆分成多個子業務,部署在不同的服務器上。分佈式的每一個節點都可以做集羣。
  4. SOA,Service-Oriented Architecture,面向服務的架構。舉例來說,比如有多個客戶端向訪問同一個數據庫,你應該怎麼做呢?方案A:多個客服端寫程序訪問數據庫服務器。方案B:服務器寫程序,多個終端通過服務器提供的接口(REST+HTTP Method,或者基於Socket的RPC)來完成遠程調用。注意,雖然我這裏說的是多個客戶端向服務端請求,但是本質上,也可以是不同服務之間互相請求 好處是各個客戶端(或服務端)可以由不同的語言處理,因爲相互調用的API是語言無關的,每個客戶端(或服務端)都是可以由獨立的團隊開發維護的。除此之外,SOA還要有服務治理,當服務越來越多時,作爲服務的提供方希望知道爲誰提供了服務,哪些服務是熱點,這些都是服務治理需要去做的。

分佈式鎖

爲什麼要有分佈式鎖?

  1. 在單機情況下,可以用語言層面的鎖實現進程同步,但是在分佈式場景中,需要同步的進程可能位於不同節點上,那麼就需要使用分佈式鎖。
  2. 阻塞鎖通過互斥量來實現。

分佈式鎖的實現方案概述

數據庫的唯一索引

  1. 獲得鎖時向表中插入一條記錄,釋放鎖時刪除記錄。唯一索引可以保證記錄只被插入一次,因此這個記錄可以被用來判斷是否處於鎖定狀態。
  2. 存在以下問題:
    • 鎖沒有失效時間,解鎖失敗而話,其他進程無法獲得鎖。
    • 只能是非阻塞鎖,插入失敗直接報錯,無法重試。
    • 不可重入,已經獲得鎖的進程必須重新獲取鎖。

Redis的SETNX指令

  1. 使用SETNX(set if not exist)指令插入一個鍵值對,如果key已經存在,那麼就返回false,插入成功返回true。setnx本質上和數據庫插入唯一索引類似,保證了只存在一個key的鍵值對,那麼就可以用一個key的鍵值對判斷是否處於鎖定狀態。但是和數據庫項目,可以通過expire指定爲鍵值對設置一個過期時間,從而避免釋放鎖失敗的問題。注意,因爲redis的刪除方式是惰性刪除(被訪問時發現失效則刪除,週期性的從失效的主鍵中選擇一部分刪除)+註定刪除(超過一定限制是,觸發主動情理)。

Redis的ReadLock算法

  1. 使用多個redis來實現分佈式鎖,保障單點故障時依然可用。嘗試從N個互相獨立的Redis實例獲取鎖,計算獲取鎖消耗的時間,只有當這個時間小於鎖的過期時間,並且從大多數實例上獲取了鎖,才認爲是獲取成功了,如果鎖獲取失敗,那每個redis實例都需要釋放鎖。

ZooKeeper的有序節點

  1. Zookeeper抽象模型 一種樹形結構的命名空間
  2. 節點類型
    • 永久節點:不會因爲會話結束或超時而消失
    • 臨時節點:如果會話結束或超時就會消失
    • 有序節點:會在節點名的後面加一個數字後準,並且有序的。比如有序節點爲/lock/node-000,那下一個就是/lock/node-001
  3. 監聽器 爲一個節點註冊監聽器,在節點狀態發生改變時,會給客戶端發送消息
  4. 分佈式鎖實現
    • 創建一個鎖目錄/lock
    • 當一個客戶端需要獲取鎖時,在/lock下創建臨時的且有序的子節點
    • 客戶端獲取/lock下的子節點列表,判斷自己創建的子節點是否爲當前子節點列表中序號最小的子節點,如果是則認爲獲得鎖,否則監聽自己的前一個子節點,獲得子節點的變更通知後- 重複此步驟直到獲取鎖。
    • 執行代碼,完成後,刪除相應的子節點。
    • 這TM根本就是AQS完全一樣的思路啊,所謂的同層臨時有序節點不就是同步隊列麼,監聽不就是CAS一直判斷自己是否在頭結點麼,根本就是完全一樣的思路啊。
  5. 會話超時 如果一個已經獲得鎖的會話超時了,因爲創建的是臨時節點,所以該會話對應臨時節點會被刪除,其他會話就可以獲得鎖。可以看到,Zookeeper分佈式鎖不會出現數據庫的唯一索引實現的分佈式鎖釋放鎖失敗的問題
  6. 羊羣效應 一個節點未獲得鎖,只需要監聽自己的前一個子節點。解釋那麼多,這TM就是AQS的思路,只有頭結點能獲得同步狀態就完事兒了。

分佈式事務

什麼是分佈式事務

  1. 事務的操作位於不同的節點上,但是仍然可以保證事務的ACID特性,比如在下單場景下,庫存和訂單如果不在同一個節點上,就涉及分佈式事務。
  2. 如下圖,購買的核心是減庫存,生成訂單。如果減庫存和插入訂單是在兩個微服務中完成的,那麼可能存在庫存減少但插入訂單失敗的可能,由此產生了不一致的情形。

2PC

什麼是2PC

  1. 兩段式提交(2-phase commit,2PC),通過引入協調者Coordinator來協調參與者的行爲,並最終決定這些參與者是否要真正執行事務。

運行過程

  1. 準備階段,協調者詢問參與者事務是否執行成果,參與者返回事務執行結果
  2. 提交階段,如果事務在每個參與者上都執行成功,事務協調者發送通知讓參與者提交事務,否則,協調者發送通知,讓參與者回滾事務。需要注意的是,在準備節點,參與者執行了事務,但是還未提交。只有在提交階段接收到協調者發類似的通知後,才進行提交或者回滾。 那如果在協調者發送通知後發生了問題,不還是沒辦法保證事務的一致性麼?
    [外鏈圖片轉存失敗(img-6LBsdfyW-1566629277159)(https://www.kunzhao.org/blog/2018/06/22/consistency-problem-of-the-distrubuted-system/two_phase_commit_protocol.jpg)]
    因爲本質上數據庫通過日誌先行的方式記錄了,哪怕是提交失敗,也會通過日誌一起回滾。

存在問題

  1. 同步阻塞 所有事務參與者在登臺其他參與者響應時,都處於同步阻塞狀態,無法進行其他操作。
  2. 單點問題 協調者在2PC中起到非常大的作用,發生故障會造成很大影響,特別是階段二發生故障,會導致所有參與者一直阻塞
  3. 數據不一致 如果在階段二,只有部分參與者收到了協調者發送的Commit消息,那麼只會有部分參與者提交了事務,使得系統數據不一致
  4. 過於保守 任意一個節點失敗就會導致整個事務失敗,沒有完善的容錯機制

3PC

  1. CanCommit階段。 事務協調者確認所有參與者是否可以完成事務。
  2. PreCommit階段。 如果階段一全部返回yes,就進入PreCommit階段預提交。此時,協調者會通知所有參與者執行事務,並將redo和undo記錄到事務日誌中,向協調者返回ack代表可以提交。注意! 如果階段一中有任何一個參與者節點返回的結果是No,或者協調者在等待參與者節點反饋的過程中超時,整個分佈式事務中斷。
  3. DoCommit階段 協調者通知所有參與者進行提交。
  4. 相比於2PC優化的點在哪裏? 對協調者和參與者都設置了超時時間,一旦超時,協調者和參與者都會提交事務。
  5. 存在問題: 如果因爲網絡原因沒有收到協調者的Abort則會提交事務,從而產生數據不一致的現象。

TCC

  1. TCC是一種補償型柔性事務,在一個長事務中,比如服務器A發起事務,服務器B參與事務,那麼A執行後先提交,B執行後後提交,所謂補償是指,如果B回滾,那就對A執行反操作。
  2. TCC指的是try,confirm,cancel三個方法,這個是寫在業務裏面的,而2PC和3PC都是資源層面的。那麼優勢就很明顯了,TCC控制的粒度是可自定義的,降低了鎖衝突,提高了吞吐量。但是因爲對業務有侵入性,所以實現難度比較大。

本地消息表

  1. 本地消息表與業務數據表處於同一個數據庫中,這樣就能利用本地事務來保證在對着兩個表的操作滿足事務特性,並且使用消息隊列來保證最終一致性。
    • 在分佈式操作的一方完成寫業務數據的操作後,向本地消息表發送一個消息,本地事務能保證這個消息一定被寫入本地消息中
    • 之後,將本地消息表中的消息轉發到消息隊列中,如果轉發成功則將消息從本地消息表中刪除,否則繼續重新轉發
    • 在分佈式事務操作的另一方,從消息隊列讀取一個消息,並執行消息中的操作。
  2. 上圖也很明顯了,這種模式可以保障的是最終一致性,因爲需要用消息隊列來保證一致性。

CAP

什麼是CAP

  1. C:一致性 A:可用性 P:分區容錯性,CAP最多隻能滿足兩項。
  • 一致性 多個數據副本能否保持一致性,在一致性的條件下,系統在執行數據更新操作之後能夠從一個一致性狀態轉移到另一個一致性狀態。如果對系統的一個數據更新成功後,所有用戶都能夠讀取到最新的值,就被人爲是強一致性的。
  • 可用性 在分佈式系統面對各種異常時可以提供正常服務的能力,對於用戶的每一個請求總能在有限時間內返回結果。
  • 分區容錯性 網絡分區指的是分佈式系統中的節點被劃分爲多個區域,每個區域內部可以通信,但是區域之間無法通信(這裏的無法通行是被動地,本來整個分佈式系統都可以連通,但是因爲網絡問題而導致變成了一個個獨立的分區)。在分區容忍性條件下,分佈式系統在遇到任何網絡分區故障時,仍然需要能對外提供一致性和可用性的服務,除非是整個網絡環境都發生了故障。
  • 權衡:分區容忍性必須要,那就是在可用性和一致性間做取捨。爲了保證一致性,不能訪問未同步完成的節點,失去了部分可用性。爲了保證可用性,允許讀取所有節點的數據,但是可能不一致。
  • 舉例說明:N1的數據已經更新到y,但是N2因爲網絡的問題還未更新,此時N2應該返回什麼呢?如果返回x,就滿足了可用性,但是不滿足一致性,如果不返回,就滿足了一致性,但是不滿足可用性。

實際案例

  1. 交易系統必然採用關係型數據庫+強悍硬件。NoSQL用於數據分析,日誌,推薦等
  2. 以用戶管理爲例,在實現賬號數據時,可以採用消息隊列來實現CA,實時性較好,但實現較爲複雜,用戶信息可以用過數據庫通過來實現CA,但是延遲較高。

BASE

什麼是BASE

  1. BA:基本可用 S:軟狀態 E:最終一致性。核心思想:既然無法做到強一致性,但是每個應用都可以根據自身業務特點,採用適當的方式來使系統達到最終一致性。
  • 基本可用 分佈式系統出現故障時,保證核心可用,允許損失部分可用性。 比如電商促銷時,爲了保證購物的穩定性,部分頁面或部分功能將被降低等級。
  • 軟狀態 允許分佈式系統中多個節點的數據存在中間狀態,即允許不同節點的數據同步之間存在時延
  • 最終一致性 所有節點的數據,經過一段時間的同步後,最終總能達到一致。
  1. 犧牲強一致性而獲得可用性,要求系統在處理請求的過程中可以存在短暫的不一致,在這個時間窗口內,請求的每一步操作,都要記錄下來,

一致性算法

分佈式系統可能存在的一致性該問題

  1. 舉例說明
    (1) 先下訂單還是先扣庫存?下訂單成功扣庫存失敗則超賣;下訂單失敗扣庫存成功則多賣。
    (2) 系統 A 同步調用系統 B 服務超時後,這個時候系統 A 應該做什麼?
    (3) 系統 B 異步回調系統 A 超時後,系統 A 遲遲沒有收到回調結果怎麼辦?
    (4) 某個訂單在系統 A 中能查詢到,但是系統 B 中不存在。
    (5) 系統間都存在請求,只是狀態不一致。
    (6) 交易系統依賴於數據庫的 ACID,緩存和數據庫之間如何保持一致性?強一致性還是弱一致性?
    (7) 多個節點上緩存的內容不一致怎麼辦?請求恰好在這個時間窗口進來了。
    (8) 緩存數據結構不一致。某個數據由多個數據元素組成,如果其中某個子數據依賴於從其它服務中獲取數據,假設這部分數據獲取失敗,那麼就會導致數據不完整,可能會出現 NullPointerException 等。
  2. 個人理解:分佈式系統中,可能有某些業務數據需要在不同的系統中得到統一,即一致。比如說A,B,C三個系統,現在需要通過一致性算法,使得在有限時間內內,A,B,C的數據相同。A,B,C的數據最終必須統一爲一個,不允許A,B相同但是C不同。

Paxos

算法目的

  1. 一致性算法,對於多個節點產生的值,該算法能保證只選出唯一一個值。
  2. 實際案例:在異步環境下,比如一個值A,要向三臺服務器寫,但是因爲網絡、故障、亂序等問題,會導致三臺服務器可能出現缺寫、順序不一致等現象。

https://mp.weixin.qq.com/s?__biz=MzI4NDMyNTU2Mw==&mid=2247483695&idx=1&sn=91ea422913fc62579e020e941d1d059e&scene=0#wechat_redirect
我們需要轉換一下切入點,也許我們需要paxos確定的值,並不一定是我們真正看到的數據。我們觀察大部分存儲系統,如LevelDB,都是以AppendLog的形式,確定一個操作系列,而後需要恢復存儲的時候都可以通過這個操作系列來恢復,而這個操作系列,正是確定之後就永遠不會被修改的。到這已經很豁然開朗了,只要我們通過paxos完成一個多機一致的有序的操作系列,那麼通過這個操作系列的演進,可實現的東西就很有想象空間了,存儲服務必然不是問題。

執行過程

  1. 規定一個提議包括兩個字段[n,v],其中n爲序號(具有唯一性),v爲提議值。
  2. Prepare階段 兩個Proposer和三個Acceptor。首先兩個Proposer向Acceptor發送prepare請求,攜帶一組[n,v]。當Acceptor收到[n1,v1]後,不再接受比n1更小的序號。如果n1之前沒有序號,就返回[no previous],否則返回[n0,v0].
  3. Accept階段 一個Proposer接收到超過一半的Acceptor的Prepare響應後,就可以發送Accept請求。那proposer在Acceptor階段發送的不再是[n1,v1],而是——如果Proposer收到的超過一半的響應爲[n2,v2] [n2,v2] [n3,v3]且n3>n2,此時發送的爲[n1,v3],即proposer在accept階段會改變自己的提案,改成獲得到最大序號的V值。

Raft

算法目的

  1. 分佈式一致性協議,主要用於競選主節點。

單個Candidate的競選

  1. 有三種節點:Follower,Candidate和Leader。Leader會週期性的發送心跳包給Follower。每個Follower都設置了一個隨機的競選超時時間,一般爲150ms~300ms,如果在這個時間內沒有收到Leader的心跳包,就會變成Candidate,進入競選階段。重點:一段時間內未收到Leader發來的心跳包,則進入競選階段

    變爲Candidate的節點會發送投票請求給其他所有節點,如果有超過一半的節點回復,則Candidate變爲Leader。

多個Candidate的競選

  1. 多個Follower變成Candidate,且獲得同樣的票數,則需要重新開始投票,直到選出一個票數多的。

數據同步

  1. 客戶端的修改會被傳入Leader。此時修改還未被提交,只是寫入日誌。
  2. Leader會把修改複製到所有Follower
  3. Leader會等待大多數的Follower進行修改,然後纔將修改提交。
  4. Leader會通知所有Follower讓他們也提交修改,此時所有的節點的值達成一致。
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章