上一篇咱們介紹了在Spring Boot中整合EhCache的方法。既然用了ehcache,咱們天然要說說它的一些高級功能,否則咱們用默認的ConcurrentHashMap
就行了。本篇不具體介紹EhCache緩存如何落文件、如何配置各類過時參數等常規細節配置,這部份內容留給讀者本身學習,若是您不知道如何搞,能夠看看這裏的官方文檔。html
那麼咱們今天具體講什麼呢?先思考一個場景,當咱們使用了EhCache,在緩存過時以前能夠有效的減小對數據庫的訪問,可是一般咱們將應用部署在生產環境的時候,爲了實現應用的高可用(有一臺機器掛了,應用還須要可用),確定是會部署多個不一樣的進程去運行的,那麼這種狀況下,當有數據更新的時候,每一個進程中的緩存都是獨立維護的,若是這些進程緩存同步機制,那麼就存在因緩存沒有更新,而一直都用已經失效的緩存返回給用戶,這樣的邏輯顯然是會有問題的。因此,本文就來講說當使用EhCache的時候,若是來組建進程內緩存EnCache的集羣以及配置配置他們的同步策略。java
因爲下面是組建集羣的過程,務必採用多機的方式調試,避免沒必要要的錯誤發生。spring
本篇的實現將基於上一篇的基礎工程來進行。先來回顧下上一篇中的程序要素:數據庫
User實體的定義緩存
@Entity @Data @NoArgsConstructor public class User { @Id @GeneratedValue private Long id; private String name; private Integer age; public User(String name, Integer age) { this.name = name; this.age = age; } }
User實體的數據訪問實現(涵蓋了緩存註解)bash
@CacheConfig(cacheNames = "users") public interface UserRepository extends JpaRepository<User, Long> { @Cacheable User findByName(String name); }
下面開始改造這個項目:網絡
第一步:爲須要同步的緩存對象實現Serializable
接口app
@Entity @Data @NoArgsConstructor public class User implements Serializable { @Id @GeneratedValue private Long id; private String name; private Integer age; public User(String name, Integer age) { this.name = name; this.age = age; } }
注意:若是沒有作這一步,後續緩存集羣經過過程當中,由於要傳輸User對象,會致使序列化與反序列化相關的異常
第二步:從新組織ehcache的配置文件。咱們嘗試手工組建集羣的方式,不一樣實例在網絡相關配置上會產生不一樣的配置信息,因此咱們創建不一樣的配置文件給不一樣的實例使用。好比下面這樣:異步
實例1,使用ehcache-1.xml
socket
<ehcache xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="ehcache.xsd"> <cache name="users" maxEntriesLocalHeap="200" timeToLiveSeconds="600"> <cacheEventListenerFactory class="net.sf.ehcache.distribution.RMICacheReplicatorFactory" properties="replicateAsynchronously=true, replicatePuts=true, replicateUpdates=true, replicateUpdatesViaCopy=false, replicateRemovals=true "/> </cache> <cacheManagerPeerProviderFactory class="net.sf.ehcache.distribution.RMICacheManagerPeerProviderFactory" properties="hostName=10.10.0.100, port=40001, socketTimeoutMillis=2000, peerDiscovery=manual, rmiUrls=//10.10.0.101:40001/users" /> </ehcache>
實例2,使用ehcache-2.xml
<ehcache xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="ehcache.xsd"> <cache name="users" maxEntriesLocalHeap="200" timeToLiveSeconds="600"> <cacheEventListenerFactory class="net.sf.ehcache.distribution.RMICacheReplicatorFactory" properties="replicateAsynchronously=true, replicatePuts=true, replicateUpdates=true, replicateUpdatesViaCopy=false, replicateRemovals=true "/> </cache> <cacheManagerPeerProviderFactory class="net.sf.ehcache.distribution.RMICacheManagerPeerProviderFactory" properties="hostName=10.10.0.101, port=40001, socketTimeoutMillis=2000, peerDiscovery=manual, rmiUrls=//10.10.0.100:40001/users" /> </ehcache>
配置說明:
cache
標籤中定義名爲users的緩存,這裏咱們增長了一個子標籤訂義cacheEventListenerFactory
,這個標籤主要用來定義緩存事件監聽的處理策略,它有如下這些參數用來設置緩存的同步策略:
新增了一個cacheManagerPeerProviderFactory
標籤的配置,用來指定組建的集羣信息和要同步的緩存信息,其中:
|
鏈接第三步:打包部署與啓動。打包沒啥大問題,主要緩存配置內容存在必定差別,因此在指定節點的模式下,須要單獨拿出來,而後使用啓動參數來控制讀取不一樣的配置文件。好比這樣:
-Dspring.cache.ehcache.config=classpath:ehcache-1.xml -Dspring.cache.ehcache.config=classpath:ehcache-2.xml
第四步:實現幾個接口用來驗證緩存的同步效果
@RestController static class HelloController { @Autowired private UserRepository userRepository; @GetMapping("/create") public void create() { userRepository.save(new User("AAA", 10)); } @GetMapping("/find") public User find() { User u1 = userRepository.findByName("AAA"); System.out.println("查詢AAA用戶:" + u1.getAge()); return u1; } }
驗證邏輯:
/create
接口,建立一條數據/find
接口,實例1緩存User,同時同步緩存信息給實例2,在實例1中會存在SQL查詢語句/find
接口,因爲緩存集羣同步了User的信息,因此在實例2中的此次查詢也不會出現SQL語句上一篇發佈的時候,公衆號上有網友留言問,數據更新以後怎麼辦?
其實當構建了緩存集羣以後,就比較好辦了。好比這裏的例子,須要作兩件事:
save
操做增長@CachePut
註解,讓更新操做完成以後將結果再put到緩存中這樣就能夠防止緩存的髒數據了,可是這種方法還並非很好,由於緩存集羣的同步依然須要時間,會存在短暫的不一致。同時進程內的緩存要在每一個實例上都佔用,若是大量存儲的話始終不那麼經濟。因此,不少時候進程內緩存不會做爲主要的緩存手段。下一篇將具體說說,另外一個更重要的緩存使用!
歡迎關注本系列教程:《Spring Boot 2.x基礎教程》
本文首發: Spring Boot 2.x基礎教程:使用EhCache緩存集羣,轉載請註明出處。
歡迎關注個人公衆號:程序猿DD,得到獨家整理的學習資源和平常乾貨推送。 點擊直達本系列教程目錄。