谷歌CacheBuilder项目应用

使用cacheBuilder实现函数防抖

  1. 解决接口出现重复请求问题👎连续发送导致的一些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("发送的重复请求被忽略");
      }
  1. 缓存

    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;
}

}

}

```

 

posted @ 2021-01-27 10:15  AlbertXe  阅读(726)  评论(0编辑  收藏  举报