前端时间看了一个guava教程, 然后项目中正好用到一个缓存的场景,于是用它的缓存重构了原来的代码。
背景
业务背景如下:有一个配置项rootStaff,一个耗时的运算从rootStaff下拿到一堆关联的staff,得到一个staffList。rootStaff可配置可更改,staffList可能会因为员工入职离职而变化,所以每天要更新一下计算结果。
根据以上业务背景,我们需要缓存rootStaff和staffList, 如果rootStaff和缓存不一样或者时间过期了就需要重新计算。
Guava缓存简析
guava缓存主要参考了这篇文章,这里就不再复述。
实现
在设计怎么构造和调用缓存时我还是想把它写成一个缓存工具类和一个业务类,虽然目前还没有第二个地方调用工具类,只是那样看上去会更清晰一些。
对缓存工具类,由于我想做成更业务无关些,所以打算采用get(K, Callable<V>)
方法而不是build时候传入方法。然后缓存策略默认写死一天过期以后也可以方便的改。
另外由于我们缓存可能会因为外部配置项rootStaff改变而改变,我加了一个cleanCache的方法。
cache工具类实现如下:
public class StaffCacheUtil {
public static StaffCacheUtil create() {
return new StaffCacheUtil();
}
private Cache<String, Object> staffCache;
private StaffCacheUtil() {
staffCache = CacheBuilder.newBuilder()
.expireAfterWrite(1, TimeUnit.DAYS)
.build();
cleanCache();
}
public void cleanCache() {
staffCache.cleanUp();
}
public Object get(String key, Callable<Object> callable) throws ExecutionException {
return staffCache.get(key, callable);
}
public void put(String key, Object value) throws ExecutionException {
staffCache.put(key, value);
}
}
回到业务代码,先贴一下原来的实现:
String rootStaffId = diamondVariableManager.getVariable("topUserRemoveRootStaffId");
List<StaffInfoDO> userDList = new ArrayList<StaffInfoDO>();
if(rootStaffId != null){
if(staffCache.get(activeIndex).get("rootStaffId") != null){
long now = System.currentTimeMillis();
long diff = now - time.get();
if(!rootStaffId.equals(staffCache.get(activeIndex).get("rootStaffId")) || diff > 86400*1000){
if(time.compareAndSet(now - diff, now)){
this.writeCache(rootStaffId);
}
}
} else {
this.writeCache(rootStaffId);
}
userDList = readCache();
}
List<String> userStaffIds = new ArrayList<String>();
这里staffCache是个map,readCache和writeCache去读和写map里某个key。
而用guava的代码是:
String rootStaffFromCache = (String)staffCacheUtil.get(ROOT_STAFF, new Callable<Object>() {
@Override
public Object call() {
staffCacheUtil.cleanCache();
return rootStaffId;
}
});
if (!rootStaffId.equals(rootStaffFromCache) ) {
staffCacheUtil.cleanCache();
staffCacheUtil.put(ROOT_STAFF, rootStaffId);
}
Object obj = staffCacheUtil.get(STAFF_KEY, new Callable<Object>() {
@Override
public Object call() {
return getStaffData(rootStaffId);
}
});
if (obj instanceof List && ((List)obj).get(0) instanceof StaffInfoDO) {
userDList = (List<StaffInfoDO>)obj;
} else {
logger.error("object from cache is not List<StaffInfoDO>");
}
这里用get(KEY, Callable<>)的方法使得代码看上去更符合_如果有缓存则返回;否则运算、缓存、然后返回_的模式,也省去了writecache和readcache的实现,对我来说习惯了匿名类的话逻辑会更清晰一些。
总结
这里主要用guava缓存做了一次尝试,感觉它能很方便的让我们设置缓存策略,而它的_如果有缓存则返回;否则运算、缓存、然后返回_也让我很涨姿势。