1.Ehcache介紹java
Ehcache是一個用Java實現的簡單、高速、線程安全的緩存管理類庫。具體快速、簡單、低消耗、依賴性小、擴展性強、支持對象或序列化緩存、支持緩存或元素的失效、提供LRU/LFU/FIFO緩存策略、支持內存緩存及磁盤緩存、採用分佈式緩存機制等特色。redis
2.引入依賴spring
在項目的pom.xml中添加下面這三個依賴:數據庫
< dependency>api
< groupId>javax.cache < /groupId>瀏覽器
< artifactId>cache-api < /artifactId>緩存
< /dependency>安全
< dependency>服務器
< groupId>org.ehcache< /groupId>app
< artifactId>ehcache< /artifactId>
< version>3.7.0< /version>
< /dependency>
< dependency>
< groupId>org.springframework.boot< /groupId>
< artifactId>spring-boot-starter-cache< /artifactId>
< /dependency>
cache-api是JSR-107 Cache的規範,定義了一列接口規範(可是這只是一種規範,須要使用它的實現,例如ehcache3.x、Hazelcast等)。 ehcache是ehcache的功能包。
springBoot要支持第三方緩存的話,還須要引入spring-boot-starter-cache。
3.配置屬性
3.1 配置application.properties
在application.properties添加以下配置:
可選,配置了spring.cache.jcache.config屬性會自動裝配JCacheCacheManager
spring.cache.type=jcache
指定ehcache的配置文件所在的位置
spring.cache.jcache.config=classpath:ehcache-3.x.xml
3.2配置ehcache-3.x.xml
在resources文件夾下新建ehcache-3.x.xml,添加以下內容:
< ?xml version="1.0" encoding="UTF-8" ?>
< eh:config
xmlns:xsi='www.w3.org/2001/XMLSch…'
xmlns:eh='www.ehcache.org/v3'
xsi:schemaLocation="www.ehcache.org/v3 www.ehcache.org/schema/ehca…">
< !--指定緩存目錄-->
< eh:persistence directory="${java.io.tmpdir}/cache-data"/>
< !--緩存模板-->
< eh:cache-template name="default">
< eh:expiry>
< eh:ttl unit="seconds">600< /eh:ttl>
< /eh:expiry>
< eh:resources>
< !--堆內內存能夠放2000個條目,超出部分堆外100MB-->
< eh:heap unit="entries">2000< /eh:heap>
< eh:offheap unit="MB">100< /eh:offheap>
< /eh:resources>
< /eh:cache-template>
< !--實際的緩存區間,繼承了default緩存模板,sample徹底使用模板默認-->
< eh:cache alias="sample" uses-template="default">< /eh:cache>
< !--下面兩個繼承了default緩存模板,但覆蓋了緩存的過時時間--> < eh:cache alias="authority_service" uses-template="default">
< eh:expiry>
< eh:ttl unit="hours">1< /eh:ttl>
< /eh:expiry>
< /eh:cache>
< eh:cache alias="shop_service" uses-template="default">
< eh:expiry>
< eh:ttl unit="hours">24< /eh:ttl>
< /eh:expiry>
< /eh:cache>
< /eh:config>
更多配置知識,請參考ehcache官網:ehcache XML配置
4.使用示例
4.1 準備一個controller
該controller只有一個方法,得到某類商品的列表:
@RestController
@Validated
@RequestMapping("/v1.0/api/shop")
public class ShopController {
@Autowired
ShopService shopService;
@RequestMapping(value = {"/commodity"}, method = RequestMethod.GET)
public List listCommodity (@RequestParam String type) {
System.out.println("ShopController: type is " + type);
return shopService.listCommodity(type);
}
}
4.2 準備一個service
在須要使用緩存的Bean上面添加@EnableCaching註解,那該bean具備緩存功能。 在須要使用緩存的方法上添加@Cacheable註解,那該方法具備緩存功能(前提是該bean具備緩存的功能)。 注意: 1和2配合起來才能使某個bean的某個方法具備緩存的功能。
package com.example.demo.service;
import org.springframework.cache.annotation.Cacheable;
import org.springframework.cache.annotation.EnableCaching;
import org.springframework.stereotype.Service;
import java.util.ArrayList;
import java.util.List;
@Service
@EnableCaching
public class ShopService {
// value:使用叫作'shop_service'的緩存器
// key: 緩存的key等於#type,即傳入的key值
// condition:緩存的條件,當#type等於phone時,才進行緩存
@Cacheable(cacheNames = "shop_service", key = "#type", condition = "#type == 'phone'")
public List listCommodity(String type) {
System.out.println("ShopService: 調用了listCommodity");
List commodities = new ArrayList<>();
if (type.equals("phone")) {
commodities.add("Apple");
commodities.add("HuaWei");
} else {
commodities.add("others");
}
return commodities;
}
}
4.3 啓動springBoot,進行測試
使用瀏覽器,發送4個請求:
http://localhost:8080/v1.0/api/shop/commodity?type=phone
http://localhost:8080/v1.0/api/shop/commodity?type=phone
http://localhost:8080/v1.0/api/shop/commodity?type=computer
http://localhost:8080/v1.0/api/shop/commodity?type=computer
後臺打印的日誌以下:
ShopController: type is phone
ShopService: 調用了listCommodity
ShopController: type is phone
ShopController: type is computer
ShopService: 調用了listCommodity
ShopController: type is computer
ShopService: 調用了listCommodity
4.4 結果分析
第一次發送請求,符合緩存的條件,因爲沒有緩存,因而執行了service的邏輯,並將結果緩存到了ehcache中。
第二次發送請求,符合緩存的條件,因爲已經緩存告終果,直接從ehcache中拿取緩存的結果返回,沒有進入到service的邏輯。
第三次和第四次都不符合緩存的條件,須要進入到service的邏輯計算結果。
5.Ehcache使用場景
使用的過程當中,根據優勢和缺點進行權衡後再應用到項目中去,Ehcache緩存也是如此,在實際工做有不少使用場景,一般將Ehcache做爲Redis的二次緩存使用。
5.1 Ehcache的適用場景
(1) 比較少的更新數據表的狀況下
Ehcache做爲Hibernate的緩存時,在進行修改表數據(save、update、delete等)的時候,Ehcache會自動把緩存中關於此表的全部緩存所有刪除掉,這樣作只是能達到同步,但對於數據常常修改的表來講,可能就失去了緩存的意義了。
(2)對一致性要求不高的狀況下
由於Ehcache本地緩存的特性,目前沒法很好的解決不一樣服務器緩存同步的問題,因此在一致性要求高的場合下,建議使用Redis、Memcached等集中式緩存。
5.2Ehcache的缺陷
(1) 緩存漂移
每一個應用節點只管理本身的緩存,在更新某個節點的時候,不會影響到其餘的節點,這樣數據之間可能就不一樣步了。
(2) 數據庫瓶頸
對於單實例的應用來講,緩存能夠保護數據庫的讀風暴;可是在集羣的環境下,每個應用節點都要按期保存數據更新,節點越多,要維持這樣的狀況對數據庫的開銷也越大。
5.3 Ehcache的正確打開方式
咱們在項目中使用集中式緩存(Redis或Memcached等)一般都是檢查緩存中是否存在指望的數據,若是存在直接將數據返回,若是不存在就查詢數據庫而後再將數據緩存,然後將結果返回。這時候若是緩存系統由於某些緣由宕機,形成服務沒法訪問,那麼大量的請求將直接穿透到數據庫,對數據庫形成巨大的壓力。
針對上述狀況,咱們有多種可行的解決方法,其中一種方案是將Ehcache做爲集中式緩存的二級本地緩存,這樣當緩存系統宕機後,服務器應用的本地緩存還能繼續抗住大量請求。
使用了Ehcache做爲本地緩存後,可能會出現本地緩存與緩存系統之間出現數據不一致的狀況,由於本地緩存是在服務器應用中存在,在實際生產環境中一定是多臺服務器分別部署,如何可以在更新緩存系統數據的同時,也可以更新Ehcache的緩存數據,以及保證不一樣服務器間Ehcache本地緩存數據的同步問題。
通常有兩種解決方案可供參考:
第一種:定時輪詢
每臺應用服務器定時輪詢Redis緩存,更新本地的Ehcache緩存。
第二種:主動通知
每臺應用服務器的Ehcache同步偵聽MQ消息,經過MQ推送的方式,將redis中更新的緩存數據推送到每臺應用服務器中。
針對上述的分析,可造成以下的緩存方案: