上一篇我們介紹了在Spring Boot中整合EhCache的方法,既然用了ehcache,我們自然要說說它的一些高級功能,不然我們用默認的ConcurrentHashMap就好了,本篇不具體介紹EhCache快取如何落檔案、如何配置各種過期引數等常規細節配置,這部分內容留給讀者自己學習,如果您不知道如何搞,可以看看這里的官方檔案,
那么我們今天具體講什么呢?先思考一個場景,當我們使用了EhCache,在快取過期之前可以有效的減少對資料庫的訪問,但是通常我們將應用部署在生產環境的時候,為了實作應用的高可用(有一臺機器掛了,應用還需要可用),肯定是會部署多個不同的行程去運行的,那么這種情況下,當有資料更新的時候,每個行程中的快取都是獨立維護的,如果這些行程快取同步機制,那么就存在因快取沒有更新,而一直都用已經失效的快取回傳給用戶,這樣的邏輯顯然是會有問題的,所以,本文就來說說當使用EhCache的時候,如果來組建行程內快取EnCache的集群以及配置配置他們的同步策略,
由于下面是組建集群的程序,務必采用多機的方式除錯,避免不必要的錯誤發生,
動手試試
本篇的實作將基于上一篇的基礎工程來進行,先來回顧下上一篇中的程式要素:
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物體的資料訪問實作(涵蓋了快取注解)
@CacheConfig(cacheNames = "users")
public interface UserRepository extends JpaRepository<User, Long> {
@Cacheable
User findByName(String name);
}
下面開始改造這個專案:
第一步:為需要同步的快取物件實作Serializable介面
@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
<ehcache xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:noNamespaceSchemaLocation="ehcache.xsd">
<cache name="users"
maxEntriesLocalHeap="200"
timeToLiveSeconds="600">
<cacheEventListenerFactory
properties="replicateAsynchronously=true,
replicatePuts=true,
replicateUpdates=true,
replicateUpdatesViaCopy=false,
replicateRemovals=true "/>
</cache>
<cacheManagerPeerProviderFactory
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
properties="replicateAsynchronously=true,
replicatePuts=true,
replicateUpdates=true,
replicateUpdatesViaCopy=false,
replicateRemovals=true "/>
</cache>
<cacheManagerPeerProviderFactory
properties="hostName=10.10.0.101,
port=40001,
socketTimeoutMillis=2000,
peerDiscovery=manual,
rmiUrls=//10.10.0.100:40001/users" />
</ehcache>
配置說明:
cache標簽中定義名為users的快取,這里我們增加了一個子標簽定義cacheEventListenerFactory,這個標簽主要用來定義快取事件監聽的處理策略,它有以下這些引數用來設定快取的同步策略:- replicatePuts:當一個新元素增加到快取中的時候是否要復制到其他的peers,默認是true,
- replicateUpdates:當一個已經在快取中存在的元素被覆寫時是否要進行復制,默認是true,
- replicateRemovals:當元素移除的時候是否進行復制,默認是true,
- replicateAsynchronously:復制方式是異步的指定為true時,還是同步的,指定為false時,默認是true,
- replicatePutsViaCopy:當一個新增元素被拷貝到其他的cache中時是否進行復制指定為true時為復制,默認是true,
- replicateUpdatesViaCopy:當一個元素被拷貝到其他的cache中時是否進行復制指定為true時為復制,默認是true,
- 新增了一個
cacheManagerPeerProviderFactory標簽的配置,用來指定組建的集群資訊和要同步的快取資訊,其中:- hostName:是當前實體的主機名
- port:當前實體用來同步快取的埠號
- socketTimeoutMillis:同步快取的Socket超時時間
- peerDiscovery:集群節點的發現模式,有手工與自動兩種,這里采用了手工指定的方式
- rmiUrls:當peerDiscovery設定為manual的時候,用來指定需要同步的快取節點,如果存在多個用
|連接
第三步:打包部署與啟動,打包沒啥大問題,主要快取配置內容存在一定差異,所以在指定節點的模式下,需要單獨拿出來,然后使用啟動引數來控制讀取不同的組態檔,比如這樣:
-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;
}
}
驗證邏輯:
- 啟動通過第三步說的命令引數,啟動兩個實體
- 呼叫實體1的
/create介面,創建一條資料 - 呼叫實體1的
/find介面,實體1快取User,同時同步快取資訊給實體2,在實體1中會存在SQL查詢陳述句 - 呼叫實體2的
/find介面,由于快取集群同步了User的資訊,所以在實體2中的這次查詢也不會出現SQL陳述句
進一步思考
上一篇發布的時候,公眾號上有網友留言問,資料更新之后怎么辦?
其實當構建了快取集群之后,就比較好辦了,比如這里的例子,需要做兩件事:
save操作增加@CachePut注解,讓更新操作完成之后將結果再put到快取中- 保證快取事件監聽的replicateUpdates=true,這樣資料在更新之后可以保證復制到其他節點
這樣就可以防止快取的臟資料了,但是這種方法還并不是很好,因為快取集群的同步依然需要時間,會存在短暫的不一致,同時行程內的快取要在每個實體上都占用,如果大量存盤的話始終不那么經濟,所以,很多時候行程內快取不會作為主要的快取手段,下一篇將具體說說,另一個更重要的快取使用!
歡迎關注本系列教程:《Spring Boot 2.x基礎教程》
參考資料
- EhCache 分布式快取/快取集群
- Java RMI:rmi Connection refused to host: 127.0.0.1例外解決
本文首發:Spring Boot 2.x基礎教程:使用EhCache快取集群,轉載請注明出處,
歡迎關注我的公眾號:程式猿DD,獲得獨家整理的學習資源和日常干貨推送,點擊直達本系列教程目錄,
轉載請註明出處,本文鏈接:https://www.uj5u.com/houduan/141130.html
標籤:Java
