通过自定义ehcache缓存策略+自定义缓存事件来实现ehcahce中一个cache指定不同时间,且支持设定总缓存大小或数量。
引入依赖
<dependency>
<groupId>org.ehcache</groupId>
<artifactId>ehcache</artifactId>
<version>3.9.1</version>
</dependency>
版本实测过3.9.1和3.10.x低版本没有widthService方法
指定缓存大小
protected static final ResourcePools RESOURCE_POOLS = ResourcePoolsBuilder.newResourcePoolsBuilder()
.heap(10, MemoryUnit.MB)
.offheap(20, MemoryUnit.MB)
.build();
注意:配置了offheap则所有value必须是实现了
Serializable
接口的对象否则报错Seeing this exception and having no other serialization related issues is a red flag!
完整示例:
[user]
import cn.hutool.core.convert.Convert;
import cn.hutool.core.lang.Assert;
import cn.hutool.core.lang.Console;
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.core.util.StrUtil;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import org.ehcache.Cache;
import org.ehcache.CacheManager;
import org.ehcache.config.CacheRuntimeConfiguration;
import org.ehcache.config.ResourcePools;
import org.ehcache.config.builders.CacheConfigurationBuilder;
import org.ehcache.config.builders.CacheEventListenerConfigurationBuilder;
import org.ehcache.config.builders.CacheManagerBuilder;
import org.ehcache.config.builders.ResourcePoolsBuilder;
import org.ehcache.config.units.MemoryUnit;
import org.ehcache.event.CacheEvent;
import org.ehcache.event.CacheEventListener;
import org.ehcache.event.EventType;
import org.ehcache.expiry.ExpiryPolicy;
import java.io.Serializable;
import java.time.Duration;
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;
import java.util.function.Supplier;
public class EhCacheProvider {
protected static final CacheManager CACHE_MANAGER = CacheManagerBuilder.newCacheManagerBuilder().build(true);
protected static final ResourcePools RESOURCE_POOLS = ResourcePoolsBuilder.newResourcePoolsBuilder()
.heap(10, MemoryUnit.MB)
.offheap(20, MemoryUnit.MB)
.build();
/**
* access 缓存 会话参考
*/
protected static final String TTL_CACHE_NAME = "TIME_TO_LIVE_CACHE_NAME";
/**
* 指定时间缓存
*/
protected static final String TTI_CACHE_NAME = "TIME_TO_IDLE_DEFAULT_CACHE";
/**
* 获取缓存值
* @param key 缓存key
* @return 值
*/
public static String getString(String key){
return get(key,String.class);
}
public static Object get(String key){
Object object = getCache(false).get(key);
if (Objects.isNull(object)){
object = getCache(true).get(key);
}
if (Objects.nonNull(object)){
CacheData<Object> cacheData=(CacheData<Object>) object;
return cacheData.getData();
}
return object;
}
/**
* 获取缓存值 指定返回类型
* @param key 缓存key
* @return 值
*/
public static <V> V get(String key,Class<V> type){
Object result = get(key);
return Optional.ofNullable(result)
.map(elem -> Convert.convert(type, elem))
.orElse(null);
}
/**
* 设置缓存
* @param key string
* @param value 数据
* @param expireMs 过期/毫秒
* @param <K> k类型
*/
public static <K extends Serializable> void set(String key, Object value, Long expireMs) {
set(key, value, expireMs,false);
}
/**
* 设置缓存
* @param key string
* @param value 数据
* @param <K> k类型
*/
public static <K extends Serializable> void set(String key, Object value) {
set(key, value,null,false);
}
/**
* 删除缓存
*/
public static void remove(Serializable key) {
getCache(false).remove(key);
getCache(true).remove(key);
}
public static <K extends Serializable> boolean exists(K key){
boolean containsKey = getCache(false).containsKey(key);
if (!containsKey){
containsKey = getCache(true).containsKey(key);
}
return containsKey;
}
/**
* 设置缓存
* @param key string
* @param value 数据
* @param expireMs 过期/毫秒
* @param <K> k类型
*/
public static <K extends Serializable> void set(K key, Object value, Long expireMs,boolean live) {
if (Objects.isNull(value)){
return;
}
Cache<? super Serializable, ? super Serializable> cache = getCache(live);
//另外一个相同的key移除
getCache(!live).remove(key);
if (expireMs != null && expireMs >= 0) {
CacheRuntimeConfiguration runtimeConfiguration = cache.getRuntimeConfiguration();
CustomExpiryPolicy expiryPolicy = (CustomExpiryPolicy) runtimeConfiguration.getExpiryPolicy();
expiryPolicy.setExpire(key, Duration.ofMillis(expireMs));
}
CacheData<Object> cacheData = new CacheData<>(value);
cache.put(key, cacheData);
}
/**
* 销毁全部容器
*/
public static void close() {
CACHE_MANAGER.close();
}
/**
* 缓存数据封装
* @param <T>
*/
@NoArgsConstructor
@AllArgsConstructor
@Data
public static class CacheData<T> implements Serializable{
T data;
}
/**
* 获取tti cache
* @return cache
*/
public static Cache<? super Serializable, ? super Serializable> getCache(boolean live) {
String cacheName=live?TTL_CACHE_NAME:TTI_CACHE_NAME;
Cache<? super Serializable, ? super Serializable> cache = CACHE_MANAGER.getCache(cacheName,Serializable.class,Serializable.class);
if(cache == null) {
synchronized (EhCacheProvider.class) {
cache = CACHE_MANAGER.getCache(cacheName,Serializable.class,Serializable.class);
if(cache == null) {
//缓存过期策略
CustomExpiryPolicy<Serializable,Serializable> customExpiryPolicy;
if (live){
customExpiryPolicy = new CustomTTLExpiryPolicy<>();
}else{
customExpiryPolicy = new CustomTTIExpiryPolicy<>();
}
//事件监听
CacheEventListenerConfigurationBuilder cacheEventListenerConfiguration = CacheEventListenerConfigurationBuilder
.newEventListenerConfiguration(new CustomCacheEventListener<>(cacheName), EventType.CREATED, EventType.values())
.ordered().asynchronous();
return CACHE_MANAGER.createCache(cacheName, CacheConfigurationBuilder.newCacheConfigurationBuilder(
Serializable.class,
Serializable.class,
RESOURCE_POOLS)
.withExpiry(customExpiryPolicy)
.withService(cacheEventListenerConfiguration));
}else {
return cache;
}
}
}else {
return cache;
}
}
/**
* keyValue自定义过期时间策略
* <p>
*
* @param <K>
* @param <V>
*/
private abstract static class CustomExpiryPolicy<K, V> implements ExpiryPolicy<K, V> {
private final ConcurrentHashMap<K, Duration> keyExpireMap = new ConcurrentHashMap<>();
public Duration setExpire(K key, Duration duration) {
return keyExpireMap.put(key, duration);
}
public Duration getExpireByKey(K key) {
return Optional.ofNullable(keyExpireMap.get(key))
.orElse(null);
}
public Duration removeExpire(K key) {
return keyExpireMap.remove(key);
}
@Override
public Duration getExpiryForCreation(K key, V value) {
return Optional.ofNullable(getExpireByKey(key))
.orElse(Duration.ofNanos(Long.MAX_VALUE));
}
@Override
public Duration getExpiryForAccess(K key, Supplier<? extends V> value) {
return getExpireByKey(key);
}
@Override
public Duration getExpiryForUpdate(K key, Supplier<? extends V> oldValue, V newValue) {
return getExpireByKey(key);
}
}
/**
* time to live 策略 类session机制
* @param <K>
* @param <V>
*/
private static class CustomTTLExpiryPolicy<K, V> extends CustomExpiryPolicy<K, V>{
@Override
public Duration getExpiryForAccess(K key, Supplier<? extends V> value) {
return super.getExpiryForAccess(key, value);
}
}
/**
* time to idle 策略
* @param <K>
* @param <V>
*/
private static class CustomTTIExpiryPolicy<K, V> extends CustomExpiryPolicy<K, V>{
@Override
public Duration getExpiryForAccess(K key, Supplier<? extends V> value) {
return null;
}
}
/**
* 移除过期/积压/删除缓存
*
* @param <K> key类型
* @param <V> 值类型
*/
private static class CustomCacheEventListener<K, V> implements CacheEventListener<K, V> {
String cacheName;
Cache<Serializable,Serializable> cache;
CustomExpiryPolicy customExpiryPolicy;
public CustomCacheEventListener(String cacheName) {
this.cacheName = cacheName;
}
@Override
public void onEvent(CacheEvent<? extends K, ? extends V> event) {
Console.log("事件触发:{}======{}========{}", cacheName, event.getType(), event.getKey());
this.cache = ObjectUtil.defaultIfNull(cache, CACHE_MANAGER.getCache(cacheName, Serializable.class, Serializable.class));
this.customExpiryPolicy = ObjectUtil.defaultIfNull(customExpiryPolicy, (CustomExpiryPolicy) this.cache.getRuntimeConfiguration().getExpiryPolicy());
if (StrUtil.equalsAnyIgnoreCase(event.getType().name(), EventType.EXPIRED.name(), EventType.EVICTED.name(), EventType.REMOVED.name())) {
customExpiryPolicy.removeExpire(event.getKey());
}
}
}
public static void main(String[] args) {
List<String> list = Arrays.asList("A", "B");
CacheData<List<String>> listCacheData = new CacheData<>();
listCacheData.setData(list);
set("list",list,5*1000L);
Object object = get("list");
System.out.println(1);
}
public static void t1(){
String key= "key";
String key2= "key2";
String key3="key3";
String data= "data";
String data2= "data2";
set(key,data,3*1000L);
set(key2,data2,5*1000L);
set("key3",data2,7*1000L);
while (Objects.nonNull(get(key2))){
try {
Thread.sleep(2000);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
System.out.println(get(key2));
}
System.out.println(exists(key));
System.out.println(exists("key3"));
System.out.println("缓存已过期");
System.out.println(get(key));
System.out.println(get(key2));
System.out.println(get("key3"));
try {
Thread.sleep(10000);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
}
}
[/user]
http://blog.xqlee.com/article/2404181615302031.html