高可用保證消息絕對順序消費的BROKER設計方案

轉自: http://www.infoq.com/cn/articles/high-availability-broker-design?utm_source=tuicool&utm_medium=referral數據庫

 

在要求嚴格順序消息的場景下,消息的發送者,BROKER端(BROKER端和消息存儲放在一塊兒),消息的消費者都要求按照順序進行,三者任何一個環節的亂序都會致使消息最終的消費順序被打亂。性能

若是爲每個消息維護一個有序的ID,發送和存儲消息無序,消費邏輯會變得很是複雜,消費端要對消息進行從新編排,會影響消費的性能。ui

爲了保證消息發送、保存、消費三個環節都有順序,就要求在同一個時刻只能有一個同步發送消息的線程,消息必須按照接收到的順序進行保存,消息的消費也只能由一個線程處理。人工智能

發送端,消費端爲了高可用須要部署多個實例,而後再經過一個協調者,好比ZOOKEEPER等,控制單個實例工做,其餘實例處於待命狀態。當工做實例發生了故障,協調者就會喚醒待命的實例進行工做。因爲發送端、消費端實例是無狀態的,切換工做實例不會產生亂序的問題。消息保存的BROKER端是一個有狀態的應用,若是部署多個實例,當發生故障時,因爲故障實例上可能還有未消費的消息就不能進行切換。url

在一些要求數據不丟失、必須有序、BROKER高可用的場景下(好比跨數據中心數據庫表的同步,須要按照數據庫LOG順序回放到另外一個數據中心,數據亂序或者丟失信息均可能致使兩個數據中心的數據不一致),BROKER每每採用MASTER-SLAVE同步雙寫,或者同一個消息被同步寫到多臺機器上,爲了保證服務宕機等狀況下消息不丟失,有的業務要求每條消息都落到磁盤上。若是採用同步寫多份會嚴重影響性能,若是採用單組MASTER-SLAVE的結構,當MASTER宕機後,SLAVE成爲新的MASTER能夠接受發送者的消息,可是沒法知足數據任一時刻都有兩份的要求。線程

咱們如今須要一種設計方案,在保證數據可靠性的條件下性能儘量的高,同時知足任一時刻數據至少寫入2份。設計

下面提供一種BROKER高可用,又能知足數據任一時刻都有兩份的方案 :cdn

  1. 採用MASTER-SLAVE結構方式,同步寫入消息(消息容許重複),MASTER-SLAVE上的消息在邏輯上保持一致;
  2. SLAVE在MASTER宕機後不接受發送請求,但能夠進行消費;
  3. 一個消息隊列分配兩組以上的BROKER組(一個BROKER組由MASTER-SLAVE組成),BROKER組的集羣信息在協調者上保存爲一個單向的鏈表,消費者和發送者各有一份獨立的鏈表數據。有消息的BROKER組必定會按受理髮送請求的前後順序保存在消費者對應的鏈表上,消費者只能從鏈表表頭的BROKER組上消費,當BROKER組上的消息消費完且不爲當前受理髮送請求的BROKER組則從消息鏈表中移除;
  4. 沒有積壓消息的BROKER組才能被添加到發送鏈表的表尾,當有BROKER組發生故障時會從BROKER組中移除,移除的BROKER組必須保證沒有積壓消息後才能被添加回鏈表;
  5. 只有發送鏈表表頭的BROKER組才能接受發送請求,同時新切換爲受理髮送請求的BROKER組會添加到消費鏈表的表尾。

異常處理流程:隊列

  1. BROKER組有機器宕機則從發送鏈表中移除;
  2. 當新BROKER組被挑選爲當前發送者,則把該組BROKER添加到消費鏈表的表尾;
  3. 當異常BROKER組的消息消費完成則從消費鏈表表頭移除;
  4. 當BROKER組機器都恢復正常,且沒有能夠消費的消息則添加到發送鏈表的表尾。

(點擊放大圖像)事件

 

具體的處理流程描述以下所述。

發送者處理流程

正常狀況下,咱們能夠採用單組MASTER-SLAVE結構的集羣方案,MASTER接收到發送者的消息後同步轉發給SLAVE。發送者只有接收到MASTER,SLAVE都寫入成功的信息纔算成功,不然這條消息須要發送者再次進行發送。可是當有一臺機器發生故障時這個集羣沒法知足MASTER,SLAVE都寫入成功的條件。這個時候咱們須要把發送者的發送請求FAILOVER到其餘的集羣上。若是隻是簡單地進行發送請求的切換,若是切換到的BROKER集羣上有未消費的消息就可能破壞數據的順序要求。同時消費者還必須知道發送者切換的過程,不然消費者沒法知道本身應該先從哪一個BROKER集羣上消費,一旦獲取消費的BROKER集羣順序與發送時的順不一致,順序性就會被破壞。咱們須要記錄好發送到不一樣BROKER集羣的前後順序,消費者按照記錄的順序進行消費。

若是BROKER集羣發生過切換,當前接受請求的BROKER集羣可能和消費者當前應該消費的集羣不一樣,須要對發送者和消費者單獨維護當前應該使用的集羣信息。

BROKER集羣發生故障後怎麼通知發送者,能夠有多種方式,好比由ZOOKEEPER協調,或者由客戶端處理。咱們能夠採用發送者來處理BROKER集羣故障的問題,當發送者感知到發送失敗或者鏈接失敗時向協調者發起請求,由協調者返回當前可用的BROKER集羣。

協調者判斷BROKER集羣是否能夠接收新的消息,除了要判斷BROKER是否存活外,還須要查詢其是否有未消費的消息,只有集羣上沒有可消費的消息時才能接收新的發送請求。所以協調者須要知道每一個BROKER集羣上存放的消息狀況。咱們能夠在BROKER集羣被選中爲能夠接收發送請求時,標識其爲有未消費消息的狀態,當消費者把上面的消息都消費完成後,由該BROKER集羣向協調者彙報本身已經消費完成。若是該集羣服務都不可用時,沒法彙報本身的消息積壓狀況,協調者會一直標記其爲有未消費的消息,直到該集羣服務恢復後,彙報完是否存在有未消費的消息。

(點擊放大圖像)

消費者處理流程

消費者須要消費消息時,先從協調者上獲取當前應該獲取消息的BROKER集羣,當消費完成時,BROKER集羣會向協調者彙報本身已經沒有積壓消息了。協調者接收到彙報後就把當前BROKER集羣從須要消費的列表中移除。消費者從一個集羣上獲取不到消息後會再次請求協調者,獲取下一個能夠消費的集羣信息,重新的集羣上繼續消費消息。

協調者處理流程

當協調者接收到發送者的請求時,先查看發送列表中是否存在可用的集羣,若是沒有就會檢查消息分配的全部集羣,把知足條件(消息無積壓,MASTER-SLAVE都工做正常)的集羣加入到可發送集羣列表中。若是也沒有找到可用集羣,那麼發送者會被阻塞,直到找到可使用的集羣。

當集羣被選爲當前可用集羣時,須要在未返回給發送者以前把該集羣信息同步添加到消費集羣列表中,防止協調者出現故障時,消費者獲取不到這個集羣的信息,被跳過致使消費亂序。

當協調者接收到消費者的請求時,協調者只須要把消費集羣列表表頭第一個集羣返回給消費者就能夠了。消費者消費完消息會通知相應的BROKER集羣,該集羣感知到消息都已經被消費後立刻彙報給協調者,協調者收到彙報信息就會把該集羣從消費集羣列表的表頭移除。

(點擊放大圖像)

如何控制單個實例發送

上面主要描述了對BROKER集羣的控制,防止消息因爲BROKER集羣調度順序不對致使消息亂序。

順序消息還須要知足發送者順序發送,消費者順序消費,一般爲了保證應用的高可用。咱們會對發送者和消費者部署多個實例,當一個實例發生異常宕機時,其餘的實例能夠繼續工做,防止單點故障。對於順序消息同一個時間點只能有一個線程在工做,單個實例只啓動一個線程進行發送和消費,只須要編寫代碼的時候控制就能夠作到,可是當咱們把應用部署爲多個實例時,實例之間就須要一個協調者,保證每次都只有一個工做實例。

發送者啓動時先註冊一個ZOOKEEPER的監聽事件,經過ZOOKEEPER選舉出來一個LEADER,只有拿到LEADER權限的發送者實例纔可以發送消息,沒有取到LEADER權限的發送者須要立刻中斷髮送消息的線程。消費者應用能夠按照上述方案進行相同的處理。

注意事項

MASTER-SLAVE集羣中單臺機器接收到消息,發送者視爲發送失敗,可能存在消息重複發送,SLAVE成爲MASTER後繼續接受消費請求,消費者可能取到已經消費過的消息,所以須要業務邏輯作能夠重複消費的處理。

若是有積壓的消息,MASTER和SLAVE同時宕機,因爲順序的要求,消費者會被阻塞,不能繼續進行消費,雖然這種狀況極少發生,仍是須要注意。消費者被阻塞,可是不會影響發送者,只要有能夠接收消息的BROKER集羣,發送者能夠繼續進行工做。

主從之間同步複製消息也須要保證順序處理,避免SLAVE上消息的順序與MASTER上的順序不一致。

單個線程發送和消費,在一些業務場景下可能不能知足性能需求,用戶能夠根據本身的業務邏輯,把沒有順序要求的業務進行拆分,分紅不一樣的消息類型進行發送,單個消息類型保證順序。

相關文章
相關標籤/搜索