Java ExpireAfterWrite似乎不起作用

Java ExpireAfterWrite似乎不起作用,java,caching,guava,cache-expiration,evict,Java,Caching,Guava,Cache Expiration,Evict,我正在尝试使用谷歌的番石榴缓存,我只是用expireAfterWrite每隔1分钟测试一次。在我正在开发的应用程序中,有数百个用户。所以我的理解是,当第一个人访问缓存时,我们从数据库中填充它,然后一分钟的计时器应该开始倒计时。现在缓存已满。在这一分钟内,我们将始终从缓存中获取它。一分钟后,它将过期,我们将再次从数据库中获取它 但是,每次我在这一分钟内访问缓存时,计时器都会重置。为什么呢?如果我没有在一分钟内访问缓存,缓存将过期并正常工作 代码: Cache<Long, List<s

我正在尝试使用谷歌的番石榴缓存,我只是用expireAfterWrite每隔1分钟测试一次。在我正在开发的应用程序中,有数百个用户。所以我的理解是,当第一个人访问缓存时,我们从数据库中填充它,然后一分钟的计时器应该开始倒计时。现在缓存已满。在这一分钟内,我们将始终从缓存中获取它。一分钟后,它将过期,我们将再次从数据库中获取它

但是,每次我在这一分钟内访问缓存时,计时器都会重置。为什么呢?如果我没有在一分钟内访问缓存,缓存将过期并正常工作

代码:

 Cache<Long, List<someObj>> cacheMap = CacheBuilder.newBuilder().maximumSize(maxSize).expireAfterWrite(maxTime, TimeUnit.MINUTES).removalListener(new RemovalListener<Long, List<someObj>>() {
    public void onRemoval(RemovalNotification<Long, List<someObj>> notification) {
        if (notification.getCause() == RemovalCause.EXPIRED) {
            logger.info("Value " + notification.getValue() + " has been expired");
        } else {
            logger.info("Just removed for some reason");
        }
    }
}).build();

...
//Convert to ConcurrentHashMap
cacheMap = someCache.asMap();

...


public List<someObj> getAllData(final Long orgId, final User user) {
    // Get requests from cache
    List<Request> cacheList = overdueSlaMap.get(orgId);

    // Fill cached requests if null
    cacheList = fillCacheIfNecessary(orgId, cacheList, overdueSlaMap);  

    return cacheList;
}

//Strategy for reading and writing to cache
//This method is fine. On first load, cache is empty so we fill it from database. Wait one minute, value expires and cache is empty again so we get from cache. 
//However, everytime I refresh the page the one minute timer starts again. Surely, the one minute timer within the Guava Cache should not refresh regardless of how many times I access the cache within that one minute period. 
 private List<someObj> fillCacheIfNecessary(List<someObj> cacheList, ConcurrentMap<Long, List<someObj>> cacheMap) {

        // If the cache is empty then we will try to fill it from the database.
        if (cacheList == null) {
            logger.info("populating cache");
            List<someObj> objList = new ArrayList<someObj>();

            // if bla bla
            if (such and such) {
                objList = service.getFromDatabase(someArg);
            } else {
                objList = service.getFromDatabase(anotherarg);
            }

            // Concurrently lock the new cacheList retrieved from the database.
            List<someObj> newValue = Collections.unmodifiableList(objList);

            // Only insert if new value does not exist in cache map
            List<someObj> oldValue = cacheMap.putIfAbsent(orgId, newValue);

            // If old value already exists then we use the old value otherwise we use the new value
            cacheList = ((oldValue != null && !oldValue.isEmpty()) ? oldValue : newValue);
        }
        return cacheList;

    }
Cache cacheMap=CacheBuilder.newBuilder().maximumSize(maxSize).expireAfterWrite(maxTime,TimeUnit.MINUTES).removalListener(new removalListener()){
移除时的公共无效(移除通知){
if(notification.getCause()==RemovalCause.EXPIRED){
logger.info(“Value”+notification.getValue()+“已过期”);
}否则{
logger.info(“出于某种原因刚刚删除”);
}
}
}).build();
...
//转换为ConcurrentHashMap
cacheMap=someCache.asMap();
...
公共列表getAllData(最终长orgId,最终用户){
//从缓存获取请求
List cacheList=overdueSlaMap.get(orgId);
//如果为null,则填充缓存请求
cacheList=FillCacheIfEssential(orgId、cacheList、overdueSlaMap);
返回缓存列表;
}
//读写缓存策略
//这个方法很好。在第一次加载时,缓存是空的,所以我们从数据库中填充它。等一分钟,值过期,缓存再次为空,所以我们从缓存中获取。
//但是,每次我刷新页面时,一分钟计时器就会再次启动。当然,Guava缓存中的一分钟计时器不应该刷新,无论我在这一分钟内访问缓存多少次。
私有列表FillCacheIfEssential(列表缓存列表、ConcurrentMap缓存映射){
//如果缓存是空的,那么我们将尝试从数据库中填充它。
if(cacheList==null){
logger.info(“填充缓存”);
List objList=new ArrayList();
//如果布拉布拉
如(某某){
objList=service.getFromDatabase(someArg);
}否则{
objList=service.getFromDatabase(另一个参数);
}
//同时锁定从数据库检索的新缓存列表。
List newValue=集合。不可修改列表(objList);
//仅当缓存映射中不存在新值时插入
List oldValue=cacheMap.putIfAbsent(orgId,newValue);
//如果旧值已经存在,则使用旧值,否则使用新值
cacheList=((oldValue!=null&&!oldValue.isEmpty())?oldValue:newValue);
}
返回缓存列表;
}
编辑

我正在从控制器调用缓存:

public ModelAndView getNotifications(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse) throws Exception {
    User user = getCurrentUser();
    CacheManager cacheManager = new CacheManager();
    List<someObj> objList = new ArrayList<Request>();

    // Get from database
    if (bla bla) {
        objList = getFromDatabase(user);
    }
    // Get from cache
    else {
        Long orgId = user.getUserOrganisation().getId();
        objList = cacheManager.getAllData(orgId, user);      
    }

    return new ModelAndView(SOMEVIEW);
}
publicmodelandview getNotifications(HttpServletRequest-HttpServletRequest,HttpServletResponse-HttpServletResponse)引发异常{
User=getCurrentUser();
CacheManager CacheManager=新建CacheManager();
List objList=new ArrayList();
//从数据库获取
如果(布拉布拉){
objList=getFromDatabase(用户);
}
//从缓存中获取
否则{
Long orgId=user.getUserOrganization().getId();
objList=cacheManager.getAllData(orgId,用户);
}
返回新的ModelAndView(SOMEVIEW);
}

它将所需的有用数据传递给方法,这些方法将数据传递给调用数据库的方法。但是,我现在尝试重构代码以使用LoadingCache,但这需要重写load()方法,该方法只使用一个作为键的参数。我需要的不仅仅是密钥,还需要在控制器中调用cacheManager,以便它调用适当的方法。什么是加载方法?什么时候用?什么时候打电话,多久打一次?它会使我的其他方法无效吗?

我相信您的问题是您正在使用从
asMap
返回的
Map
作为缓存
asMap
记录为
返回存储在此缓存中的条目的视图,作为线程安全映射
。这里没有任何东西建议您可以使用
映射
作为缓存。我建议更新您的代码以使用
缓存
API,并直接在缓存上调用
put
get(key,Callable)

你可能想考虑使用<代码>加载缓存>代码>,因为我发现API更简单。

编辑

因此,首先,仅当缓存中不存在密钥时才调用load方法。缓存调用load方法获取应该为其键返回的值,然后缓存该值以供下次使用。使用
CacheLoader
Callable
的一个好处是,检索数据的调用是线程安全的,因为每个键只调用一次数据,即使对同一键的多个调用同时发生

如果需要传递更多信息,请创建如下包装器对象:

 public class MyKey{
       final Long orgId; // true key value
       final User user; // other values needed

       // implement equals and hashCode to ONLY use orgId. This will ensure that
       // the same cached value is returned for all requests with the same
       // orgId which is the ACTUAL key.
  }
您还可以使用常规的
缓存
执行相同的操作,并创建一个
可调用的
来包装所需的值


我建议
CacheLoader
在必要时使
fillcacheif
无效。对
cache.get()
的简单调用将在内部确定该值是否必须检索或已存在于缓存中。它正在执行
get,检查是否为null,retrieve
为您工作。

但它在JavaDocs中也说,“对映射的修改直接影响缓存。”对不起,您是对的。但是,请尝试一下LoadingCache或
get(key,Callable)