谷歌CacheBuilder项目应用
-
解决接口出现重复请求问题👎连续发送导致的一些bug
public static final Cache<String, Boolean> cahceBuilder =
CacheBuilder.newBuilder().expireAfterWrite(5, TimeUnit.SECONDS).build();
//获取请求报文的唯一键,或者使用报文中的某些能请求重复的属性拼装
final String key=JSON.toJSONString(request)
//封装对象,用于判断是否命中缓存
final AtomicBoolean flag=new AtomicBoolean(false);
//从缓存中获取该键,如果没有找到,则执行业务方法
cahceBuilder.get(key, new Callable<Boolean>() {
@Override
public Boolean call() throws Exception {
flag.set(true);
//未命中,则执行业务方法
return true;
}
});
if(flag.get()){
logger.warn("发送的重复请求被忽略");
}
-
缓存
LoadingCache<Integer, Object> localcache = CacheBuilder.newBuilder().expireAfterWrite(32, TimeUnit.DAYS).maximumSize(64)
.refreshAfterWrite(5, TimeUnit.SECONDS).build(new CacheLoader<Integer, Object>() {
@Override
public Object load(Integer o) throws Exception {
// 具体的加载数据到内存逻辑
return null;
}
@Override
public ListenableFuture reload(Integer key, final Object oldValue) throws Exception {
ListenableFutureTask task = ListenableFutureTask.create(() -> {
System.out.println(oldValue);
return oldValue;
});
// 异步维护重新加载逻辑
threadpool.submit(task);
return task;
}
});
### 3 3级缓存使用
```
package com.service;
import com.alibaba.fastjson.JSON;
import com.fasterxml.jackson.databind.DeserializationFeature;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.datatype.jdk8.Jdk8Module;
import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule;
import com.fasterxml.jackson.module.paramnames.ParameterNamesModule;
import com.google.common.cache.Cache;
import com.google.common.cache.CacheBuilder;
import com.google.common.cache.CacheLoader;
import com.google.common.cache.LoadingCache;
import lombok.EqualsAndHashCode;
import lombok.Getter;
import lombok.SneakyThrows;
import org.apache.commons.lang3.RandomUtils;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
import javax.annotation.PostConstruct;
import java.lang.reflect.Type;
import java.text.SimpleDateFormat;
import java.time.LocalDateTime;
import java.time.temporal.ChronoUnit;
import java.util.HashMap;
import java.util.Map;
import java.util.UUID;
import java.util.concurrent.Callable;
import java.util.concurrent.TimeUnit;
@Service
public class CacheService {
private Map<String, String> jedisPool;
@Value("local.cache.maxSize:20480")
private int localCacheMaxSize;
private static final ObjectMapper objectMapper;
private LoadingCache<LocalCacheKey, Cache<String, Object>> localCacheMap;
static {
objectMapper = new ObjectMapper();
objectMapper.registerModule(new ParameterNamesModule())
.registerModule(new Jdk8Module())
.registerModule(new JavaTimeModule());
objectMapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
objectMapper.configure(DeserializationFeature.READ_UNKNOWN_ENUM_VALUES_AS_NULL, true);
objectMapper.setDateFormat(new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"));
}
@PostConstruct
public void init() {
jedisPool = new HashMap<>();
localCacheMap = CacheBuilder.newBuilder().expireAfterAccess(6, TimeUnit.HOURS).maximumSize(16).build(
// 缓存加载sql ,这个是从第二缓存中拿
new CacheLoader<LocalCacheKey, Cache<String, Object>>() {
@Override
public Cache<String, Object> load(LocalCacheKey key) throws Exception {
return CacheBuilder.newBuilder().expireAfterWrite(key.getTime(), TimeUnit.SECONDS).maximumSize(localCacheMaxSize).build();
}
});
}
public <T> T get(String key, Class<T> clz) {
String result = jedisPool.get(key);
if (result == null) {
return null;
}
return JSON.parseObject(result, clz);
}
public <T> void set(String key, T value, int timeout) {
jedisPool.put(key, JSON.toJSONString(value));
}
@SneakyThrows
public <T> T getCache(String key, Type type) {
String result = jedisPool.get(key);
if (result == null) {
return null;
}
return objectMapper.readValue(result, objectMapper.constructType(type));
}
public String lock(String key, int expires) {
String token = UUID.randomUUID().toString();
jedisPool.put(key, token);
return token;
}
public void unlock(String key, String token) {
String t = jedisPool.get(key);
if (t.equals(token)) {
jedisPool.remove(key);
}
}
public String get(String key) {
return jedisPool.get(key);
}
@SneakyThrows
public <T> T getOrLoadLocalCache(String key, Class<T> clz, int timeout, Callable<T> loader, int lockTimeout, long localtimeMillis) {
Object result;
if (true) {
// 只走本地不走DB
LocalCacheKey localCacheKey = new LocalCacheKey(clz, localtimeMillis);
Cache<String, Object> cache = localCacheMap.get(localCacheKey);
result = cache.get(key, () -> {
return loadLocalVaue(key, loader);
});
}else {
// local - redis -db
LocalCacheKey localCacheKey = new LocalCacheKey(clz, localtimeMillis);
Cache<String, Object> cache = localCacheMap.getUnchecked(localCacheKey);
result = cache.get(key,()->{
return getOrLoad(key,clz,timeout,loader,lockTimeout,false);
});
}
if (result == null) {
return null;
}
return (T) result;
}
private <T> T getOrLoad(String key, Class<T> clz, int timeout, Callable<T> loader, int lockTimeout, boolean loadIfFail) {
// 1 从缓存中取 取到结束
// 2 取不到 上锁成功 DB查询放入缓存
// 上锁失败
T t = get(key, clz);
if (t != null) {
return t;
}
String lockKey = key + "_lock";
String token = lock(lockKey, lockTimeout);
if (token != null) {
t = loaderValue(key, loader);
set(key, t, timeout);
return t;
}else {
// 轮询
t = queryCacheAndWait(key, clz, timeout);
if (t != null ){
return t;
}
if (!loadIfFail){
// Error
}
}
if (token != null) {
unlock(key, token);
}
t = loaderValue(key, loader);
set(key, t, timeout);
return t;
}
private <T> T queryCacheAndWait(String key, Class<T> clz, int timeout) {
LocalDateTime till = LocalDateTime.now().plus(timeout, ChronoUnit.MILLIS);
do {
T t = get(key, clz);
if (t != null) {
return t;
}
try {
Thread.sleep(RandomUtils.nextInt(10, 30));
} catch (InterruptedException e) {
}
} while (LocalDateTime.now().isAfter(till));
return null;
}
@SneakyThrows
private <T> T loaderValue(String key, Callable<T> loader) {
return loader.call();
}
@SneakyThrows
private <T> Object loadLocalVaue(String key, Callable<T> loader) {
return loader.call();
}
@Getter
@EqualsAndHashCode
private static class LocalCacheKey {
private final Class<?> clz;
private final long time;
public LocalCacheKey(Class<?> clz, long time) {
this.clz = clz;
this.time = time;
}
}
}
```