【问题标题】:spring cache, TTL unles service is down弹簧缓存,TTL,除非服务关闭
【发布时间】:2013-08-16 20:25:40
【问题描述】:

我有一个有趣的任务,我需要缓存我的方法的结果,这对于 Spring 缓存抽象来说非常简单

@Cachable(...)
public String getValue(String key){
    return restService.getValue(key);
}

restService.getValue() 以 REST 服务为目标,如果端点关闭,该服务可以响应或不响应。

我需要为缓存值设置一个特定的 TTL,比如说 5 分钟,但如果服务器关闭,我需要返回最后一个值,即使它延长了 5 分钟。

我在考虑有第二个没有 TTL 并且总是返回最后一个值的可缓存方法,如果 restService 什么都不返回,它将从 getValue 调用,但也许有更好的方法?

【问题讨论】:

    标签: java spring caching


    【解决方案1】:

    我也对这样做有一段时间感兴趣。很抱歉,我还没有找到任何简单的方法来做到这一点。 Spring不会为你做这件事,更多的问题是spring封装了什么缓存实现能不能做到。我假设您正在使用 EhCache 实现。不幸的是,据我所知,此功能并未开箱即用。

    根据您的问题,有多种方法可以实现类似的目标

    1) 使用一个永恒的缓存时间,并有一个第二类线程,它定期循环缓存数据并刷新它。我还没有完全做到这一点,但是 Thread 类需要看起来像这样:

    @Autowired    
    EhCacheCacheManager ehCacheCacheManager; 
    ...
    //in the infinite loop
                List keys = ((Ehcache) ehCacheCacheManager.getCache("test").getNative                Cache()).getKeys();
                for (int i = 0; i < keys.size(); i++) {
                    Object o = keys.get(i);
                    Ehcache ehcache = (Ehcache)ehCacheCacheManager.getCache("test").getNativeCache()
                    Element item = (ehcache).get(o);
    
                    //get the data based on some info in the value, and if no exceptions         
                    ehcache.put(new Element(element.getKey(), newValue));
    
                }
    
    • 好处是这对于 @Cacheable 调用者来说非常快,缺点是您的服务器可能会获得比必要更多的命中

    2) 你可以制作一个 CacheListener 来监听驱逐事件,临时存储数据。如果服务器调用失败,请使用该数据并从方法返回。

    ehcache.xml

        <cacheEventListenerFactory class="caching.MyCacheEventListenerFactory"/>
    
      </cache>
    </ehcache>
    

    工厂: 导入 net.sf.ehcache.event.CacheEventListener; 导入 net.sf.ehcache.event.CacheEventListenerFactory; 导入 java.util.Properties;

    public class MyCacheEventListenerFactory extends CacheEventListenerFactory {
      @Override
      public CacheEventListener createCacheEventListener(Properties properties) {
        return new CacheListener();
      } 
    }
    

    伪实现 导入 net.sf.ehcache.CacheException; 导入 net.sf.ehcache.Ehcache; 导入 net.sf.ehcache.Element; 导入 net.sf.ehcache.event.CacheEventListener;

    import java.util.concurrent.ConcurrentHashMap;
    
    public class CacheListener implements CacheEventListener  {
       //prob bad practice to use a global static here - but its just for demo purposes
       public static ConcurrentHashMap myMap = new ConcurrentHashMap();
    
       @Override
       public void notifyElementPut(Ehcache ehcache, Element element) throws CacheException {
         //we can remove it since the put happens after a method return
         myMap.remove(element.getKey());
       }
    
       @Override
       public void notifyElementExpired(Ehcache ehcache, Element element) {
        //expired item, we should store this
        myMap.put(element.getKey(), element.getValue());
        }
     //....
    }
    
    • 这里的一个挑战是密钥不是很有用,您可能需要在返回值中存储一些关于密钥的内容,以便在服务器调用失败时能够获取它。这感觉有点骇人听闻,我还没有确定这是否真的是防弹的。它可能需要一些测试。

    3) 很多努力但有效:

    @Cacheable("test")
    public MyObject getValue(String data) {
        try {
            MyObject result = callServer(data);
            storeResultSomewhereLikeADatabase(result);
        } catch (Exception ex) {
            return getStoredResult(data);
        }
    }
    

    这里的优点是它可以在服务器重新启动之间工作,您可以简单地扩展它以允许集群服务器之间共享缓存。 我在 12 个集群环境中有一个版本,每个集群首先检查数据库,看看是否有任何其他集群首先获得了“昂贵”的数据 然后重用它而不是进行服务器调用。

    一个轻微的变体也是使用第二个@Cacheable 方法和@CachePut 而不是DB 来存储数据。但这将意味着内存使用量增加一倍。这可能是可以接受的,具体取决于您的结果大小。

    【讨论】:

      【解决方案2】:

      如果条件(服务是否启动?)是真或假,也许您可​​以使用 spel 更改使用的缓存(一个使用 ttl,第二个不使用),我从来没有以这种方式使用过 spel(我用它来根据一些请求参数更改密钥)但我认为它可以工作

      @Cacheable(value = "T(com.xxx.ServiceChecker).checkService()",...)
      

      其中 checkService() 是一个静态方法,返回应该使用的缓存名称

      【讨论】:

      • 这听起来好像需要对目标服务器进行 2 次调用,一次确定服务器是否已启动,第二次获取实际数据。 2 次调用意味着服务器可能会在中途停机。
      • 也许在该方法中使用一些自定义逻辑可能会有很大帮助,但最后它并不是一个很大的改进,尊重您在问题中建议的主要替代方案:(
      猜你喜欢
      • 1970-01-01
      • 2013-11-10
      • 2012-07-07
      • 2019-10-25
      • 2014-03-03
      • 2023-04-11
      • 2010-10-21
      • 2018-04-27
      • 2019-05-17
      相关资源
      最近更新 更多