天天看點

老司機經驗分享:生産級中間件系統架構設計實踐

作者:石杉的架構筆記

目錄

  • 1、Master-Slave架構
  • 2、異步日志持久化機制
  • 3、檢查點機制:定時持久化全量資料
  • 4、引入檢查點節點
  • 5、總結 & 思考

這篇文章,給大家來聊一個生産級的中間件系統的架構設計實踐,希望給對中間件系統感興趣的同學一點啟發。

1、Master-Slave架構

這個中間件系統的本質是希望能夠用分布式的方式來處理一些資料,但是具體的作用涉及到核心技術,是以這裡不能直接說明。

但是他的核心思想,就是把資料分發到很多台機器上來處理,然後需要有一台機器來控制N多台機器的分布式處理,大概如下圖所示。

老司機經驗分享:生産級中間件系統架構設計實踐

那麼既然是分布式的處理,就肯定涉及到在Master中要維護這個叢集的一些核心中繼資料。

比如說資料的分發處理是如何排程的,處理的具體過程現在什麼進度了,還有就是對叢集裡存放資料進行描述的一些核心中繼資料。

這些核心中繼資料肯定會不斷的頻繁的修改,大家此時可以想,無論你是基于外部的檔案還是資料庫,或者是zookeeper來存放這些中繼資料的話,其實都會導緻他的中繼資料更新性能降低,因為要通路外部依賴。

何況這種複雜的中繼資料其實還不一定能通過zk或者資料庫來存放,因為他可能是非格式化的。

是以這裡一個核心的設計,就是将核心中繼資料直接存放在Master的記憶體裡,這樣可以保證高并發更新中繼資料的時候,他的性能是極高的,而且直接基于記憶體來提供對外的更新服務。

如果Master部署在高配置實體機上,比如32核128GB的那種,每秒支援10萬+的請求都沒問題。

老司機經驗分享:生産級中間件系統架構設計實踐

2、異步日志持久化機制

但是這裡有一個問題,假如說Master程序重新開機,或者是突然當機了,那麼記憶體裡的資料不就丢失了麼?

對,是以針對這個問題,既然已經否決掉了基于外部存儲來寫入中繼資料,那麼這裡就可以采取異步持久化日志的機制,來通過異步化的方式把中繼資料的更新日志寫入磁盤檔案。

每次Master收到一個請求,在記憶體裡更新中繼資料之後,就需要生成一條中繼資料的更新日志,把這個更新日志需要寫入到一個記憶體緩沖裡去。

然後等記憶體緩沖滿了之後,由一個背景線程把這裡的資料重新整理到磁盤上去,如下圖。

老司機經驗分享:生産級中間件系統架構設計實踐

肯定會有人說,那如果一條更新日志剛寫入緩沖區,結果Master當機了,此時不是還是會丢失少量資料嗎?因為還沒來得及刷入磁盤。

沒錯啊,這個為了保證高并發請求都是由記憶體來處理的,你必須得用異步持久化磁盤的模式,是以必然要容忍極端當機情況下,可能丢失比如幾秒鐘的資料。

那麼如果是正常的Master重新開機呢?

那簡單,必須先把日志緩沖區清空刷入磁盤,然後才能正常重新開機Master,保證資料都在磁盤上不會丢失。

接着重新開機的時候,從磁盤上讀取更新日志,每一條都依次回訪到記憶體裡,恢複出來核心中繼資料即可。

3、檢查點機制:定時持久化全量資料

但是這裡又有一個問題了,那個磁盤上的日志檔案越來越大,因為中繼資料不斷的在更新,不斷在産生最新的變更日志寫入磁盤檔案。

那麼系統運作一段時間以後,每次重新開機都需要從磁盤讀取曆史全部日志,一條一條回放到記憶體來恢複核心中繼資料嗎?

不可能,是以這裡一定要配合引入檢查點機制。

也就是說,每隔一段時間,就需要開啟一個背景線程,把記憶體裡的全部核心中繼資料序列化後寫入磁盤上的中繼資料檔案,作為這個時間的一個快照檔案,同時清空掉日志檔案,這個叫做檢查點操作。

下次重新開機,隻要把中繼資料檔案讀取出來直接反序列化後方入記憶體,然後把上次檢查點之後的變更日志從日志檔案裡讀出來回放到記憶體裡,就可以恢複出來完整的中繼資料了。

這種方式,可以讓Master重新開機很快,因為大部分資料都是在檢查點寫入的那個中繼資料檔案裡。

整個過程,如下圖所示:

老司機經驗分享:生産級中間件系統架構設計實踐

4、引入檢查點節點

但是這個時候又有一個問題了。

大家可以想一下,Master記憶體裡的中繼資料需要高并發的被人通路和修改,同時每隔一段時間還要檢查點寫入磁盤。

那麼在檢查點過程中,是不是需要把記憶體資料全部加鎖,不允許别人修改?

在加鎖的時候,把不會變動的資料寫入磁盤檔案中,但是這個過程是很慢的,意味着此時别人高并發的寫入操作都需要等待核心中繼資料的鎖。

因為此時别人鎖住了,你無法加鎖去寫資料進去,這會導緻系統在幾秒内出現卡頓無法響應請求的問題。

是以此時需要在架構設計裡引入一個檢查點節點,專門負責同步Master的變更日志。

然後在自己記憶體裡維護一份一模一樣的核心中繼資料,每隔一段時間由檢查點節點來負責将記憶體資料寫入磁盤,接着上傳發送給Master。

這樣做,就不需要Master自己執行檢查點的時候對自己記憶體資料進行加鎖了,如下圖。

老司機經驗分享:生産級中間件系統架構設計實踐

在這樣的一個架構下,對Master來說,他隻需要一個背景線程負責接收Checkpoint程序定時傳送過來的中繼資料檔案快照然後寫入本地磁盤就可以了,完全規避掉了對自己記憶體中繼資料的鎖沖突的問題。

5、總結 & 思考

總結一下這個架構設計,其實就是Master基于記憶體維護中繼資料,這樣一台實體機可以支撐每秒10萬+的高并發請求。

每次中繼資料出現更新,寫一條日志到記憶體緩沖區,然後背景線程去重新整理日志到日志檔案裡去,同時需要發送一條日志到Checkpoint節點去。

Checkpoint節點會在自己記憶體裡維護一份一模一樣的中繼資料,然後每隔一段時間執行checkpoint檢查點寫一份中繼資料檔案快照。

接着上傳給Master節點後清空掉他的日志檔案。然後Master節點每次重新開機的時候直接讀取本地中繼資料檔案快照,加上回放上次checkpoint之後的日志即可。

這裡可能大家會提幾個問題,比如說Master節點突然當機會如何?

那很簡單,直接影響就是他記憶體緩沖裡的那些日志丢了,導緻少量資料丢失,這個在我們的場景下可以容忍。

如果Checkpoint節點當機怎麼辦?

那不要緊,因為他之前上傳過中繼資料檔案的快照,是以對Master而言最多就是無法同步資料過去。

但是Master重新開機,還是可以讀取最近一次的中繼資料快照,然後回放日志即可。

等Checkpoint節點恢複了,可以繼續接着上一次同步日志,然後繼續執行checkpoint操作。

繼續閱讀