為監控而生的多級快取框架 layering-cache
簡介
layering-cache是在Spring Cache基礎上擴充套件而來的一個快取框架,主要目的是在使用註解的時候支援配置過期時間。layering-cache其實是一個兩級快取,一級快取使用Caffeine作為本地快取,二級快取使用redis作為集中式快取。並且基於redis的Pub/Sub做快取的刪除,所以它是一個適用於分散式環境下的一個快取系統。
支援
- 支援快取監控統計
- 支援快取過期時間在註解上直接配置
- 支援二級快取的自動重新整理(當快取命中並發現快取將要過期時會開啟一個非同步執行緒重新整理快取)
- 重新整理快取分為強重新整理和軟重新整理,強重新整理直接呼叫快取方法,軟重新整理直接改快取的時間
- 快取Key支援SpEL表示式
- 新增FastJsonRedisSerializer,KryoRedisSerializer序列化,重寫String序列化。
- 支援同一個快取名稱設定不同的過期時間
- 輸出INFO級別的監控統計日誌
整合
整合 Spring 4.x
- 引入layering-cache
- maven 方式
<dependency>
<groupId>com.github.xiaolyuh</groupId>
<artifactId>layering-cache-aspectj</artifactId>
< version>${layering.version}</version>
</dependency>
- gradle 方式
compile 'com.github.xiaolyuh:layering-cache:${layering.version}'
-
宣告CacheManager和LayeringAspect
/**
* 多級快取配置
*
* @author yuhao.wang3
*/
@Configuration
@EnableAspectJAutoProxy
public class CacheConfig {
@Bean
public CacheManager cacheManager(RedisTemplate<String, Object> redisTemplate) {
return new LayeringCacheManager(redisTemplate);
}
@Bean
public LayeringAspect layeringAspect() {
return new LayeringAspect();
}
}
整合 Spring Boot
引入layering-cache 就可以了
<dependency>
<groupId>com.github.xiaolyuh</groupId>
<artifactId>layering-cache-starter</artifactId>
<version>${layering.version}</version>
</dependency>
使用
註解形式
直接在需要快取的方法上加上Cacheable、CacheEvict、CachePut註解。
- Cacheable註解
@Cacheable(value = "user:info", depict = "使用者資訊快取",
firstCache = @FirstCache(expireTime = 4, timeUnit = TimeUnit.SECONDS),
secondaryCache = @SecondaryCache(expireTime = 10, preloadTime = 3, forceRefresh = true, timeUnit = TimeUnit.SECONDS))
public User getUser(User user) {
logger.debug("呼叫方法獲取使用者名稱稱");
return user;
}
- CachePut註解
@CachePut(value = "user:info", key = "#userId", depict = "使用者資訊快取",
firstCache = @FirstCache(expireTime = 4, timeUnit = TimeUnit.SECONDS),
secondaryCache = @SecondaryCache(expireTime = 10, preloadTime = 3, forceRefresh = true, timeUnit = TimeUnit.SECONDS))
public User putUser(long userId) {
User user = new User();
user.setUserId(userId);
user.setAge(31);
user.setLastName(new String[]{"w", "y", "h"});
return user;
}
- CacheEvict註解
@CacheEvict(value = "user:info", key = "#userId")
public void evictUser(long userId) {
}
@CacheEvict(value = "user:info", allEntries = true)
public void evictAllUser() {
}
直接使用API
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(classes = {CacheConfig.class})
public class CacheCoreTest {
private Logger logger = LoggerFactory.getLogger(CacheCoreTest.class);
@Autowired
private CacheManager cacheManager;
@Test
public void testCacheExpiration() {
FirstCacheSetting firstCacheSetting = new FirstCacheSetting(10, 1000, 4, TimeUnit.SECONDS, ExpireMode.WRITE);
SecondaryCacheSetting secondaryCacheSetting = new SecondaryCacheSetting(10, 4, TimeUnit.SECONDS, true);
LayeringCacheSetting layeringCacheSetting = new LayeringCacheSetting(firstCacheSetting, secondaryCacheSetting);
String cacheName = "cache:name";
String cacheKey = "cache:key1";
LayeringCache cache = (LayeringCache) cacheManager.getCache(cacheName, layeringCacheSetting);
cache.get(cacheKey, () -> initCache(String.class));
cache.put(cacheKey, "test");
cache.evict(cacheKey);
cache.clear();
}
private <T> T initCache(Class<T> t) {
logger.debug("載入快取");
return (T) "test";
}
}
文件
@Cacheable
表示用的方法的結果是可以被快取的,當該方法被呼叫時先檢查快取是否命中,如果沒有命中再呼叫被快取的方法,並將其返回值放到快取中。
名稱 | 預設值 | 說明 |
---|---|---|
value | 空字串陣列 | 快取名稱,cacheNames的別名 |
cacheNames | 空字串陣列 | 快取名稱 |
key | 空字串 | 快取key,支援SpEL表示式 |
depict | 空字串 | 快取描述(在快取統計頁面會用到) |
ignoreException | true | 是否忽略在操作快取中遇到的異常,如反序列化異常 |
firstCache | 一級快取配置 | |
secondaryCache | 二級快取配置 | |
key生成器,暫時不支援配置 |
@FirstCache
一級快取配置項
名稱 | 預設值 | 說明 |
---|---|---|
initialCapacity | 10 | 快取初始Size |
maximumSize | 5000 | 快取最大Size |
expireTime | 9 | 快取有效時間 |
timeUnit | TimeUnit.MINUTES | 時間單位,預設分鐘 |
expireMode | ExpireMode.WRITE | 快取失效模式,ExpireMode.WRITE:最後一次寫入後到期失效,ExpireMode.ACCESS:最後一次訪問後到期失效 |
@SecondaryCache
二級快取配置項
名稱 | 預設值 | 說明 |
---|---|---|
expireTime | 5 | 快取有效時間 |
preloadTime | 1 | 快取主動在失效前強制重新整理快取的時間,建議是 expireTime * 0.2 |
timeUnit | TimeUnit.HOURS | 時間單位,預設小時 |
forceRefresh | false | 是否強制重新整理(直接執行被快取方法) |
@CachePut
將資料放到快取中
名稱 | 預設值 | 說明 |
---|---|---|
value | 空字串陣列 | 快取名稱,cacheNames的別名 |
cacheNames | 空字串陣列 | 快取名稱 |
key | 空字串 | 快取key,支援SpEL表示式 |
depict | 空字串 | 快取描述(在快取統計頁面會用到) |
ignoreException | true | 是否忽略在操作快取中遇到的異常,如反序列化異常 |
firstCache | 一級快取配置 | |
secondaryCache | 二級快取配置 | |
key生成器,暫時不支援配置 |
@CacheEvict
刪除快取
名稱 | 預設值 | 說明 |
---|---|---|
value | 空字串陣列 | 快取名稱,cacheNames的別名 |
cacheNames | 空字串陣列 | 快取名稱 |
key | 空字串 | 快取key,支援SpEL表示式 |
allEntries | false | 是否刪除快取中所有資料,預設情況下是隻刪除關聯key的快取資料,當該引數設定成 true 時 key 引數將無效 |
ignoreException | true | 是否忽略在操作快取中遇到的異常,如反序列化異常 |
key生成器,暫時不支援配置 |
開啟監控統計功能
Layering Cache 的監控統計功能預設是開啟的
Spring 4.x
直接在宣告CacheManager Bean的時候將stats設定成true。
/**
* 多級快取配置
*
* @author yuhao.wang3
*/
@Configuration
@EnableAspectJAutoProxy
public class CacheConfig {
@Bean
public CacheManager cacheManager(RedisTemplate<String, Object> redisTemplate) {
LayeringCacheManager layeringCacheManager = new LayeringCacheManager(redisTemplate);
// 預設開啟統計功能
layeringCacheManager.setStats(true);
return layeringCacheManager;
}
...
}
Spring Boot
在application.properties檔案中新增以下配置即可
layering-cache.stats=true
開啟內建的監控頁面
Layering Cache內建提供了一個LayeringCacheServlet用於展示快取的統計資訊。
這個LayeringCacheServlet的用途包括:
- 提供監控資訊展示的html頁面
- 提供監控資訊的JSON API
日誌格式:
Layering Cache 統計資訊:{"cacheName":"people1","depict":"查詢使用者資訊1","firstCacheMissCount":3,"firstCacheRequestCount":4575,"hitRate":99.9344262295082,"internalKey":"4000-15000-8000","layeringCacheSetting":{"depict":"查詢使用者資訊1","firstCacheSetting":{"allowNullValues":true,"expireMode":"WRITE","expireTime":4,"initialCapacity":10,"maximumSize":5000,"timeUnit":"SECONDS"},"internalKey":"4000-15000-8000","secondaryCacheSetting":{"allowNullValues":true,"expiration":15,"forceRefresh":true,"preloadTime":8,"timeUnit":"SECONDS","usePrefix":true},"useFirstCache":true},"missCount":3,"requestCount":4575,"secondCacheMissCount":3,"secondCacheRequestCount":100,"totalLoadTime":142}
- 如果專案集成了ELK之類的日誌框架,那我們可以直接基於以上日誌做監控和告警。
- 統計資料每隔一分鐘採集一次
配置 web.xml
配置Servlet
LayeringCacheServlet是一個標準的javax.servlet.http.HttpServlet,需要配置在你web應用中的WEB-INF/web.xml中。
<servlet>
<servlet-name>layeringcachestatview</servlet-name>
<servlet-class>com.github.xiaolyuh.tool.servlet.layeringcacheservlet</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>layeringcachestatview</servlet-name>
<url-pattern>/layering-cache/*</url-pattern>
</servlet-mapping>
配置監控頁面訪問密碼
需要配置Servlet的 loginUsername 和 loginPassword這兩個初始引數。
示例如下:
<!-- 配置監控資訊顯示頁面 -->
<servlet>
<servlet-name>LayeringCacheStatView</servlet-name>
<servlet-class>com.github.xiaolyuh.tool.servlet.LayeringCacheServlet</servlet-class>
<init-param>
<!-- 使用者名稱 -->
<param-name>loginUsername</param-name>
<param-value>admin</param-value>
</init-param>
<init-param>
<!-- 密碼 -->
<param-name>loginPassword</param-name>
<param-value>admin</param-value>
</init-param>
</servlet>
<servlet-mapping>
<servlet-name>LayeringCacheStatView</servlet-name>
<url-pattern>/layering-cache/*</url-pattern>
</servlet-mapping>
配置黑白名單
LayeringCacheStatView展示出來的監控資訊比較敏感,是系統執行的內部情況,如果你需要做訪問控制,可以配置allow和deny這兩個引數。比如:
<servlet>
<servlet-name>LayeringCacheStatView</servlet-name>
<servlet-class>com.github.xiaolyuh.tool.servlet.LayeringCacheServlet</servlet-class>
<!--配置白名單-->
<init-param>
<param-name>allow</param-name>
<param-value>128.242.127.1/24,128.242.128.1</param-value>
</init-param>
<!--配置黑名單-->
<init-param>
<param-name>deny</param-name>
<param-value>128.242.127.4</param-value>
</init-param>
</servlet>
<servlet-mapping>
<servlet-name>LayeringCacheStatView</servlet-name>
<url-pattern>/layering-cache/*</url-pattern>
</servlet-mapping>
判斷規則
- deny優先於allow,如果在deny列表中,就算在allow列表中,也會被拒絕。
- 如果allow沒有配置或者為空,則允許所有訪問
ip配置規則
配置的格式128.242.127.1,128.242.127.1/24
/24表示,前面24位是子網掩碼,比對的時候,前面24位相同就匹配。
不支援IPV6
由於匹配規則不支援IPV6,配置了allow或者deny之後,會導致IPV6無法訪問。
關閉更新資料許可權
需要配置Servlet的 enableUpdate引數。如果設定成false,那麼將不能重置統計資料和刪除快取。
示例如下:
<!-- 配置監控資訊顯示頁面 -->
<servlet>
<servlet-name>LayeringCacheStatView</servlet-name>
<servlet-class>com.github.xiaolyuh.tool.servlet.LayeringCacheServlet</servlet-class>
<init-param>
<!-- 是否開啟更新資料許可權 -->
<param-name>enableUpdate</param-name>
<param-value>false</param-value>
</init-param>
</servlet>
<servlet-mapping>
<servlet-name>LayeringCacheStatView</servlet-name>
<url-pattern>/layering-cache/*</url-pattern>
</servlet-mapping>
Spring Boot
#是否開啟快取統計預設值true
spring.layering-cache.stats=true
#是否啟用LayeringCacheServlet預設值true
spring.layering-cache.layering-cache-servlet-enabled=true
spring.layering-cache.url-pattern=/layering-cache/*
#使用者名稱
spring.layering-cache.login-username=admin
#密碼
spring.layering-cache.login-password=admin
#是否允許更新資料
spring.layering-cache.enable-update=true
# IP白名單(沒有配置或者為空,則允許所有訪問)
spring.layering-cache.allow=127.0.0.1,192.168.163.1/24
# IP黑名單 (存在共同時,deny優先於allow)
spring.layering-cache.deny=192.168.1.73
重要提示
- layering-cache支援同一個快取名稱設定不同的過期時間,但是一定要保證key唯一,否則會出現快取過期時間錯亂的情況
- 刪除快取的時候會將同一個快取名稱的不同的過期時間的快取都刪掉
- 在整合layering-cache之前還需要新增以下的依賴,主要是為了減少jar包衝突。
<dependency>
<groupId>org.springframework.data</groupId>
<artifactId>spring-data-redis</artifactId>
<version>1.8.3.RELEASE</version>
</dependency>
<dependency>
<groupId>redis.clients</groupId>
<artifactId>jedis</artifactId>
<version>2.9.0</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-core</artifactId>
<version>4.3.18.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-aop</artifactId>
<version>4.3.18.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>4.3.18.RELEASE</version>
</dependency>
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>fastjson</artifactId>
<version>1.2.31</version>
</dependency>
<dependency>
<groupId>com.esotericsoftware</groupId>
<artifactId>kryo-shaded</artifactId>
<version>3.0.3</version>
</dependency>
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjweaver</artifactId>
<version>1.8.10</version>
</dependency>