JAVA中使用最廣泛的本地快取?Ehcache的自信從何而來3 —— 本地快取變身分散式叢集快取,打破本地快取天花板

架構悟道發表於2023-01-06

大家好,又見面了。


本文是筆者作為掘金技術社群簽約作者的身份輸出的快取專欄系列內容,將會透過系列專題,講清楚快取的方方面面。如果感興趣,歡迎關注以獲取後續更新。


上一篇文章中,我們知曉瞭如何在專案中透過不同的方式來整合Ehcache並在業務邏輯中進行使用。作為JAVA本地快取框架綜合實力天花板級別的Ehcache,除了在本地快取方面具有強悍的實力外,還具有一個其它對手所不具備的特色功能,即Ehcache提供了對於叢集能力的支援,這也使得Ehcache不僅僅是個本地單機快取,更是一個分散式快取。

分散式快取的意義是什麼?叢集方案又可以解決哪些問題?它與單機快取有啥區別?與Redis等集中式快取有啥不同?如何去選擇、又該如何使用?帶著這一連串的疑問,讓我們一起探討下Ehcache的各種不同叢集方案,找出上述問題的答案。

本地快取或者集中快取的問題

在正式開始闡述Ehcache的叢集解決方案前,先來做個鋪墊,瞭解下單機快取與集中式快取各自存在的問題。

單機快取不可言說的痛

對於單機快取而言,快取資料維護在程式中,應用系統部署完成之後,各個節點程式就會自己維護自己記憶體中的資料。在叢集化部署的業務場景中,各個程式獨自維護自己記憶體中的資料,而經由負載均衡器分發到各個節點進行處理的請求各不相同,這就導致了程式內快取資料不一致,進而出現各種問題 —— 比較典型的就是快取漂移問題。

快取漂移,是單機快取在分散式系統下無法忽視的一個問題。在這種情況下,大部分的專案使用中會選擇避其鋒芒、或者自行實現同步策略進行應對。常見的策略有:

  • 本地快取中僅儲存一些固定不變、或者不常變化的資料。

  • 透過過期重新載入、定時refresh等策略定時更新本地的快取,忍受資料有一定時間內的不一致

  • 對於少量更新的場景,藉助MQ構建更新機制,有變更就發到MQ中然後所有節點消費變更事件然後更新自身資料。

集中式快取也並非萬能銀彈

在叢集部署的場景下,為了簡化快取資料一致性方面的處理邏輯,大部分的場景會直接選擇使用Redis等集中式快取。集中式快取的確是為分散式叢集場景而生的,透過將快取資料集中存放,使得每個業務節點讀取與操作的都是同一份快取記錄。這樣只需要由快取服務保證併發原子性即可。

但集中式快取也並非是分散式場景下快取方案的萬能銀彈。

專案中使用快取的目的,主要是為了提升整體的運算處理效率,降低對外的IO請求等等。而集中式快取是獨立於程式之外部署的遠端服務,需要基於網路IO互動的方式來獲取,如果一個業務邏輯中涉及到非常頻繁的快取操作,勢必會導致引入大量的網路IO互動,進而導致非常嚴重的效能損耗

為了解決這個問題,很多時候還是需要本地快取結合集中式快取的方式,構建多級快取的方式來解決。

Ehcache分散式叢集方案

相比純粹的本地快取,Ehcache自帶叢集解決方案,透過相應的配置可以讓本地快取變身叢集版本,以此來應付分散式場景下各個節點快取資料不一致的問題,並且由於資料都快取在程式內部,所以也可以避免集中是快取頻繁在業務流程中頻繁網路互動的弊端。

Ehcache官方提供了多種叢集方案供選擇,下面一起看下。

RMI組播

RMI是一種點對點(P2P)的通訊互動機制,Ehcache利用RMI來實現多個節點之間資料的互通有無,相互知會彼此更新資料。對於叢集場景下,這就要求叢集內所有節點之間要兩兩互通,組成一張網狀結構。

在叢集方式下進行資料通訊互動,要求被傳輸的資料一定是要可序列化與反序列化的,對於JAVA而言,直白的說,就是物件一定是要實現了Serializable介面。

基於RMI組播的方式,Ehcache會向對應地址傳送RMI UDP組播包,由於Ehcache對於組播的實現較為簡單,所以在一些網路情況較為複雜的場景的支援度不是很完善,方案選擇的時候需注意。此外,由於是即時訊息模式,如果中途某個程式由於某些原因不可達,也可能會導致同步訊息的丟失。所以對於可靠性以及資料一致性要求較高的場景需要慎選

JMS訊息

JMS訊息方案是一種很常用的Ehcache叢集方案。JMS是一套JAVA中兩個程式之間的非同步通訊API,定義了訊息通訊所必須的一組通用能力介面,比如訊息的建立、傳送、接收讀取等。

JMS也支援構建基於事件觸發模型的訊息互動機制,也即生產者消費者模式(又稱釋出訂閱模式),其核心就是一個訊息佇列,叢集內各個業務節點都訂閱對應的訊息佇列topic主題,如果有資料變更事件,也傳送到訊息佇列的對應的topic主題下供其它節點消費。

相比於RMI組播方式,JMS訊息方式有個很大的優勢在於不需要保證所有節點都全部同時線上,因為是基於釋出訂閱模式,所以即使有節點中途某些原因當機又重啟了,重啟之後仍然可以接收其他節點已釋出的變更,然後保證自己的快取資料與其它節點一致。

Ehcache支援對接多種不同的MQ來實現基於JMS訊息的叢集組網方案,預設使用ActiveMQ,也可以切換為Kafka或者RabbitMQ等訊息佇列元件。

Cache Server模式

Ehcache的Cache Server是一種比較特殊的存在形式,它通常是一個獨立的程式進行部署,然後多個獨立的程式之間組成一個分散式叢集。Cache Server是一個純粹的快取叢集,對外提供restful介面或者soap介面,各個業務可以透過介面來獲取快取 —— 這個其實已經不是本地程式內快取的概念了,其實就是一個獨立的集中式快取,類似Redis般的感覺。

看一下一個典型的高可用水平擴容模式的Cache Server組網與業務呼叫的場景示意圖:

可以看到不管業務模組是用的什麼編碼語言,或者是什麼形態的,都可以透過http介面去訪問快取資料,而Cache Server就是一個集中式快取。在Cache Server中,叢集內部可以有一個或者多個節點,這些節點具有完全相同的資料內容,做到了資料的冗餘備份,而叢集之間資料可以不同,實現了資料容量的水平擴充套件。

值得注意的一點是,如果你訪問Ehcache的官網,會發現其官方提供的3.x版本的說明文件中不再有Cache Server的身影,而在2.x版本中都會作為一個單獨的章節進行介紹。為什麼在3.x版本中不再提供Cache Server模式呢?我在官方文件中沒找到相關的說明,個人猜測主要有下面幾個原因:

  • 定位過於尷尬,如果說要作為集中式快取來使用,完全可以直接使用redis,沒有必要費事勞神的去搭建Cache Server

  • Terracotta方式相比而言功能上更加的完備,兼具水平擴充套件與本地快取的雙重優勢,完全可以取代Cache Server

JGroups方式

JGroups的方式其實和RMI有點類似。JGroups是一個開源的群組通訊工具,可以用來建立一個組,這個組中的成員可以給其他成員傳送訊息。其工作模式基於IP組播(IP multicast),但可以在可靠性和群組成員管理上進行擴充套件,而且JGroups的架構上設計非常靈活,提供可以相容多種協議的協議棧。

JGroups的可靠性體現在下面幾個方面:

  1. 對所有接收者的訊息的無丟失傳輸(透過丟失訊息的重發)
  2. 大訊息的分割傳輸和重組
  3. 訊息的順序傳送和接收
  4. 保證原子性,訊息要麼被所有接收者接收,要麼所有接收者都收不到

也正是由於JGroups具備的上述諸多優秀特性,它常常被選擇作為叢集內各個節點之間資料同步的解決方案。而Ehcache也一樣,支援基於JGroups實現的叢集方案,透過IP組播訊息,保證叢集內各個節點之間資料的同步。

Terracotta方式

Terracotta是什麼?看下來自百度百科的介紹:

Terracotta是一款由美國Terracotta公司開發的著名開源Java叢集平臺。它在JVM與Java應用之間實現了一個專門處理叢集功能的抽象層,以其特有的增量檢測、智慧定向傳送、分散式協作、伺服器映象、分片等技術,允許使用者在不改變現有系統程式碼的情況下實現單機Java應用向叢集化應用的無縫遷移。使得使用者可以專注於商業邏輯的開發,由Terracotta負責實現高效能、高可用性、高穩定性的企業級Java叢集。

所以說,Terracotta是一個JVM層專門負責做分散式節點間協同處理的平臺框架。那麼當優秀的JVM級快取框架Ehcache與同樣優秀的JVM間多節點協同框架Terracotta組合到一起,勢必會有不俗的表現。

看下來自Ehcache官網的對於其Terracotta叢集模式的圖片說明:

基於Terracotta方式,Ehcache可以支援:

  • 熱點資料儲存在程式本地,然後根據熱度進行最佳化儲存,熱度高的會優先儲存在更快的位置(比如heap中)。

  • 儲存在其中一臺應用節點上的快取資料,可以被叢集中其它節點訪問到。

  • 快取資料在叢集層面是完整的,也支援按照HA模式設定高可用備份。

可以說這種模式下,既保留了Ehcache本地快取的超高處理效能,又享受到了分散式快取帶來的叢集優勢,不失為一種比較亮眼的組合。

引申思考 —— 本地快取的設計邊界與定位

如上所言,縱使Ehcache提供了多種叢集化策略,但略顯尷尬的是實際中各個公司專案並沒有大面積的使用。其實分析下來也很好理解:

如果真的需要很明確的訴求去解決分散式場景下的快取一致性問題,直接選擇redis、memcache等主流的集中式快取元件即可

所以Ehcache的整體綜合功能雖然是最強大的,整體定位偏向於大而全,但也導致在各個細分場景下表現不夠極致:

  • 相比Caffeine:略顯臃腫, 因為提供了很多額外的功能,比如使用磁碟快取、比如支援多節點間叢集組網等;

  • 相比Redis: 先天不足,畢竟是個本地快取,縱使支援了多種組網模式,依舊無法媲美集中式快取在分散式場景下的體驗。

但在一些相對簡單的叢集資料同步場景下,或者對可靠性要求不高的叢集快取資料同步場景下,Ehcache還是很有優勢的、尤其是Terracotta叢集模式,也不啻為一個很好的選擇。

小結回顧

好啦,關於Ehcache的叢集相關能力,就介紹到這裡咯,而關於文章開頭的幾個問題,我們也在文章內容中做了解答與探討。至此呢,我們關於Ehcache的相關介紹就全部結束了。那麼你對Ehcache是否還有什麼自己的觀點呢?歡迎評論區一起交流下,期待和各位小夥伴們一起切磋、共同成長。

隨著本篇Ehcache介紹文章的結束,我們快取專欄關於主流本地快取框架的介紹就告一段落了。下一篇文章開始,我們將開始將目光聚焦到集中式快取的身上,比如大家耳熟能詳的Redis,以及經常在面試中會拿來與Redis做比較的Memcache等等。如有興趣,歡迎關注。

? 補充說明1

本文屬於《深入理解快取原理與實戰設計》系列專欄的內容之一。該專欄圍繞快取這個宏大命題進行展開闡述,全方位、系統性地深度剖析各種快取實現策略與原理、以及快取的各種用法、各種問題應對策略,並一起探討下快取設計的哲學。

如果有興趣,也歡迎關注此專欄。

? 補充說明2

我是悟道,聊技術、又不僅僅聊技術~

如果覺得有用,請點贊 + 關注讓我感受到您的支援。也可以關注下我的公眾號【架構悟道】,獲取更及時的更新。

期待與你一起探討,一起成長為更好的自己。

相關文章