分布式系統服務單點問題的探討


在分布式系統中,單點問題是一個比較常見的問題,對於單點問題可以分為有狀態服務的單點問題和無狀態服務的單點問題。

無狀態服務的單點問題

  對於無狀態的服務,單點問題的解決比較簡單,因為服務是無狀態的,所以服務節點很容易進行平行擴展。比如,在分布式系統中,為了降低各進程通信的網絡結構的復雜度,我們會增加一個代理節點,專門做消息的轉發,其他的業務進行直接和代理節點進行通信,類似一個星型的網絡結構。


 
無狀態單點

 
無狀態多節點

  參考上面兩個圖,圖中proxy是一個消息轉發代理,業務進程中的消息都會經過該代理,這也是比較場景的一個架構。在上圖中,只有一個proxy,如果該節點掛了,那么所有的業務進程之間都無法進行通信。由於proxy是無狀態的服務,所以很容易想到第二個圖中的解決方案,增加一個proxy節點,兩個proxy節點是對等的。增加新節點后,業務進程需要與兩個Proxy之間增加一個心跳的機制,業務進程在發送消息的時候根據proxy的狀態,選擇一個可用的proxy進行消息的傳遞。從負載均衡的角度來看,如果兩個proxy都是存活狀態的話,業務進程應當隨機選擇一個proxy。
那么該解決方案中會存在什么問題呢?
  主要存在的問題是消息的順序性問題。一般來說,業務的消息都是發送、應答,再發送、再應答這樣的順序進行的,在業務中可以保證消息的順序性。但是,在實際的應用中,會出現這樣一個情況:在業務進程1中,有個業務需要給業務進程3發送消息A和消息B,根據業務的特性,消息A必須要在消息B之前到達。如果業務進程1在發送消息A的時候選擇了proxy1,在發送消息B的時候選擇了proxy2,那么在分布式環境中,我們並不能確保先發送的消息A一定就能比后發送的消息B先到達業務進程3。那么怎么解決這個問題?其實方案也比較簡單,對於這類對消息順序有要求的業務,我們可以指定對應的proxy進行發送,比如消息A和消息B都是使用proxy1進行發送,這樣就可以保證消息A比消息B先到達業務進程3。
整體來說,對於無狀態的服務的單點問題的解決方案還是比較簡單的,只要增加對應的服務節點即可。

有狀態服務的單點問題

  相對無狀態服務的單點問題,有狀態服務的單點問題就復雜多了。如果在架構中,有個節點是單點的,並且該節點是有狀態的服務,那么首先要考慮的是該節點是否可以去狀態,如果可以,則優先選擇去除狀態的方案(比如說把狀態存儲到后端的可靠DB中,可能存在性能的損耗),然后就退化成了一個無狀態服務的單點問題了,這就可以參考上一節的方案了。
但是,並不是所有的服務都是可以去狀態的,比如說對於一些業務它只能在一個節點中進行處理,如果在不同的節點中處理的話可能會造成狀態的不一致,這類型的業務是無法去除狀態的。對於這種無法去除狀態的單點的問題的解決方案也是有多種,但是越完善的方案實現起來就越復雜,不過整體的思路都是采用主備的方式。

 
簡單主備

  第一個方案就是就是增加一個備用節點,備用節點和業務進程也可以進行通信,但是所有的業務消息都發往Master節點進行處理。Master節點和Slave節點之間采用ping的方式進行通信。Slave節點會定時發送ping包給Master節點,Master節點收到后會響應一個Ack包。當Slave節點發現Master節點沒有響應的時候,就會認為Master節點掛了,然后把自己升級為Master節點,並且通知業務進程把消息轉發給自己。該方案看起來也是挺完美的,好像不存在什么問題,Slave升級為Master后所有的業務消息都會發給它。但是,如果在Master內部有一些自己的業務邏輯,比如說隨機生成一些業務數據,並且定時存檔。那么當Master和Slave之間的網絡出現問題的時候,Slave會認為Master掛了,就會升級為Master,同樣會執行Master的相應的業務邏輯,同樣也會生成一些業務數據回寫到DB。但是,其實Master是沒有掛的,它同樣也在運行對應的業務邏輯(即使業務進程的消息沒有發給舊的Master了),這樣就會出現兩個Master進行寫同一份數據了,造成數據的混亂。所以說,該方案並不是一個很好的方案。
那么怎么解決可能會出現多個Master的問題?
  換個角度看,該問題其實就是怎么去裁決,哪個節點是Master的問題。
  方案一:引入第三方的服務進行裁決。
  我們可以引入ZooKeeper,由ZooKeeper進行裁決。同樣,我們啟動兩個主節點,“節點A”和節點B。它們啟動之后向ZooKeeper去注冊一個節點,假設節點A注冊的節點為master001,節點B注冊的節點為master002,注冊完成后進行選舉,編號小的節點為真正的主節點。那么,通過這種方式就完成了對兩個Master進程的調度。
 
ZooKeeper托管主節點

ZooKeeper
ZooKeeper有一套機制,可以保證不會出現多個Master的情況,具體可以參考:
https://segmentfault.com/a/1190000012185322
  方案二: 通過選舉算法和租約的方式實現Master的選舉
  對於方案一的缺點主要要多維護一套ZooKeeper的服務,如果原本業務上並沒有部署該服務的話,要增加該服務的維護也是比較麻煩的事情。這個時候我們可以在業務進程中加入Master的選舉方案。目前有比較成熟的選舉算法,比如Paxos和Raft。然后再配合租約機制,就可以實現Master的選舉,並且確保當前只有一個Master的方案。但是,這些選舉算法理解起來並不是那么地容易,要實現一套完善的方案也是挺難的。所以不建議重復造輪子,業內有很多成熟的框架或者組件可以使用,比如微信的PhxPaxos。

 

 
Paxos選舉

  比如上圖的方案中,三個節點其實都是對等的,通過選舉算法確定一個Master。為了確保任何時候都只能存在一個Matster,需要加入租約的機制。一個節點成為Master后,Master和非Master節點都會進行計時,在超過租約時間后,三個節點后可以發起“我要成為Master”的請求,進行重新選舉。由於三個節點都是對等的,任意一個都可以成為Master,也就是說租期過后,有可能會出現Master切換的情況,所以為了避免Master的頻繁切換,Master節點需要比另外兩個節點先發起自己要成為Master的請求(續租),告訴其他兩個節點我要繼續成為Master,然后另外兩個節點收到請求后會進行應答,正常情況下另外兩個節點會同意該請求。關鍵點就是,在租約過期之前,非Master節點不能發起“我要成為Master”的請求,這樣就可以解決Master頻繁切換的問題。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM