架構雜談《五》

AjuPrince發表於2019-07-19

架構雜談《五》

保證最終一致性的模式

  在大規模、高併發服務化系統中,一個功能被拆分成多個具有功能單一的子功能,一個流程會有多個系統的多個單一功能的服務組合實現,如果使用兩階段提交協議和三階段提交協議,確實能解決系統間的一致性問題。其實現也比較複雜、成本比較高,最重要的是效能不夠好,相比來看,TCC協議簡單、更容易實現,但TCC協議由於每個事務都需要執行Try再執行Confirm 顯得有點臃腫,實現最終一致性有一些非常有效、簡單的模式。

一、查詢模式

  任何服務操作都需要提供一個查詢介面,用來向外部輸出操作執行的狀態。服務操作的使用方可通過查詢介面得知服務執行的狀態,然後根據不同的狀態來做不同的處理操作。

  為了實現查詢,每個服務操作都需要有唯一的識別符號。

  首先,單筆查詢操作的必須提供的(也鼓勵使用單筆訂單查詢),因為每次呼叫需要佔用的負載是可控的。批量查詢則根據需要來提高(如果使用了批量查詢,需要有合理的分頁機制,並且必須限制分頁的大小,以及對批量查詢的吞吐量有熔斷、隔離和限流等措施)。

(查詢模式圖)

二、補償模式

  有了查詢模式,可以在任何情況下都可得知具體操作所處的狀態,如果整個操作都處於不正常狀態,則需要我們修正操作中有問題的子操作,這可能需要重新執行未完成的子操作,或者取消已經完成的子操作,通過修復使整個分散式系統達到一致。為了讓系統最終達到一致狀態而做的努力都叫做補償。

  對於服務化系統中同步呼叫的操作,若業務操作發起方沒有收到業務操作執行方的明確返回或者呼叫超時,這時業務發起方需要及時地呼叫業務執行方來獲得操作執行的狀態(這就是查詢模式)。在獲得業務操作執行方的狀態後,如果業務執行方已完成預設工作,則業務發起方向業務的使用方返回成功;如果業務操作執行方的狀態為失敗或者未知,則會立即告訴業務使用方失敗(也叫做快速失敗策略),然後呼叫業務操作的逆向操作,保證操作不被執行或者回滾已經執行的操作,讓業務使用方、業務操作發起方和業務操作執行方最終達到一致狀態。

(補償模式圖)

  補償操作模式發起形式分為以下幾種:

  (1)自動恢復:程式根據發生不一致的環境,通過繼續執行未完成的操作或者回滾已經完成的操作達到一致性狀態。

  (2)通知運營:如果程式無法自動恢復,並且設計師考慮到了不一致的場景,則可以提供運營功能,通過運營手工進行補償。

  (3)技術運營:如果程式無法自動恢復、又沒有運營功能,那麼必須通過技術手段來解決,技術手段包括進行資料庫變更或者程式碼變更,這是最糟的種場景也是我們在生中盡避免的場景 。 

三、非同步確保模式

  非同步確保模式是補償模式的一個典型案例,經常應用到使用方對響應時間要求不太高的場景中,通常把這類操作從主流程中摘除,通過非同步的方式進行處理,處理後把結果通過通知系統通知給使用方。這種方案的最大好處是能夠對高併發流量進行消峰,例如:電商系統中的物流、配迭,以及支付系統中的計費、入賬等。
  在實踐中將要執行的非同步操作封裝後持久入庫,然後通過定時撈取未完成的任務進行補償操作來實現非同步確保模式,只要定時系統足夠健壯,則任何任務最終都會成功執行 。

(非同步確保模式圖)

四、定期校對模式

  系統在沒有達到一致之前,系統間的狀態是不一致的,甚至是混亂的,需要通過補償操作來達到最終一致性的目的,但是如何來發現需要補償的操作呢?

  在操作主流程中的系統間執行校對操作,可以在事後非同步地批量校對操作的狀態,如果發現不一致的操作則進行補償,另外,實現定期校對的一個關鍵就是分散式系統中需要有一個自始至終唯一的ID。

  在實踐中想在分散式系統中迅速定位問題時,可通過分散式系統的呼叫鏈跟蹤系統進行,它能夠跟蹤一個請求的呼叫鏈。呼叫鏈是從二維的維度跟蹤一個呼叫請求 , 最後形成一個呼叫樹。

  全域性的唯一ID 可以將一個請求在分散式系統中的流轉路徑聚合,而呼叫鏈中的SpanID 可以將聚合的請求路徑通過樹形結構進行展示,讓技術支援工作人員輕鬆地發現系 統出現的問題 , 能夠快速定位出現問題的服務節點,提高應急效率。

  在分散式系統中構建了唯一 ID、呼叫鏈等基礎設施後,我們很容易對系統間的不一致進行核對 。 通常我們需要構建第三方的定期核對系統,從第三方的角度來監控服務執行的健康程度 。

(定期核對模式圖)

 

五、可靠訊息模式

  在分散式系統中,對於主流程中優先順序 比較低的操作,大多采用非同步的方式執行,也就是前面提到的非同步確保模型,為了讓非同步操作的呼叫方和被呼叫方充分解耦,也由於專業的訊息佇列本身具有可伸縮、可分片、可持久等功能,我們通常通過訊息佇列實現非同步化。對於訊息佇列,我們需要建立特殊的設施來保證可靠的訊息傳送及處理機的幕等性。

  (1)訊息的可靠傳送

      訊息的可靠傳送可以認為是盡最大努力傳送訊息通知,有以下兩種實現方法。

      a)在傳送訊息之前將訊息持久到資料庫,狀態標記為待傳送 , 然後傳送訊息,如果傳送成功,則將訊息改為傳送成功。定時任務定時從資料庫撈取在一定時間內未傳送的訊息並將訊息傳送。

 

可靠訊息傳送模式1 

       b)該實現方式與第 1 種類似,不同的是持久訊息的資料庫是獨立 的, 並不藕合在業務系統中。傳送訊息前,先傳送一個預訊息給某個第三方的訊息管理器,訊息管理器將其持久到資料庫,並標記狀態為待傳送,在傳送成功後,標記訊息為傳送成功。定時任務定時從資料庫中撈取一定時間內未傳送的訊息,查詢業務系統是否要繼續傳送,根據查詢結果來確定訊息的狀態。

 

 

 

(可靠訊息傳送模式 2 )

  

(2)訊息處理器的冪等性  

  如果我們要保證可靠地傳送訊息,簡單來說就是要保證訊息一定傳送出去,那麼需要有重試機制。有了重試機制後,訊息就一定會重複,那麼我們需要對重複的問題進行處理。處理重複問題的最佳方式是保證操作的幕等性,幕等性的數學公式為:
        f (j(x)) =f (x)
  保證操作的幕等性的常用方法如下。
    1、使用資料庫表的唯一鍵進行濾重,拒絕重複的請求。
    2、使用分散式表對請求進行濾重。
    3、使用狀態流轉的方向性來濾重,通常使用資料庫的行級鎖來實現。
    4、根據業務的特點,操作本身就是幕等的 , 例如 : 刪除一個資源、增加一個資源、獲得一個資源等。

六、快取一致性模式

  在大規模、高併發系統中的一個常見的核心需求就是億級的讀需求,顯然,關係型資料庫並不是解決高併發讀需求的最佳方案,網際網路的經典做法就是使用快取來抗住讀流量。下面是使用快取來保證一致性的最佳實踐。

  1、如果效能要求不是非常高,則儘量使用分散式快取,而不要使用本地快取。

  2、寫快取時資料一定要完整

  3、使用快取犧牲了一致性,為了提高效能,資料庫與快取只需要保持弱一致性,而不需要保持強一致性,否則違背了使用快取的初衷。

  4、讀的順序是先讀快取,後讀資料庫,寫的順序要先寫資料庫,後寫快取。 

說明:

  1、參考書籍:《分散式服務架構:原理、設計與實戰》

  2、如有不合適的地方請反饋。綜合後更改。

相關文章