2013-08-16 50 views
1

我有一個有趣的任務,我需要緩存我的方法,這與春季緩存抽象很簡單春天緩存,TTL unles服務關閉

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

restService.getValue的結果()以REST服務爲目標,如果終點停止,該服務可以回答或不回答。

我需要爲緩存值設置特定的TTL,可以說5分鐘,但如果服務器關閉,我需要返回最後一個值,即使延長了5分鐘。

我在考慮讓第二個cachable方法沒有TTL並且總是返回最後一個值,如果restService沒有返回任何東西,它會從getValue調用,但也許有更好的方法嗎?

回答

1

我一直在高速緩存的名稱的靜態方法對此也有興趣。對不起,說,我還沒有找到任何這樣做的微不足道的方法。 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; import net.sf.ehcache.event.CacheEventListenerFactory; import java.util.Properties;

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

僞實施 進口net.sf.ehcache.CacheException; import net.sf.ehcache.Ehcache; import 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來存儲數據。但是這意味着內存使用量會翻一番。這可能是可以接受的,取決於你的結果大小。

0

也許你可以使用spel來更改已使用的緩存(一個使用ttl,第二個不使用),如果條件(服務是?)是真的還是假的,我從來沒有用過這種方法(我用過改變基於一些請求參數的關鍵),但我認爲它可以工作

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

其中checkService()是返回應使用

+0

這聽起來像是目標服務器需要2個調用,一個用於確定服務器是否啓動,另一個用於獲取實際數據。 2次調用意味着服務器可能在中間停機。 – zibi

+0

也許使用該方法內的一些自定義邏輯可以幫助很多,但最終它不是一個大的改進尊重你建議作爲你的問題的主要選擇:( –