1

メソッドの結果をキャッシュする必要がある興味深いタスクがあります。これは、Spring キャッシュの抽象化を使用すると非常に簡単です。

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

restService.getValue ()は、エンドポイントがダウンしている場合に応答するかどうかにかかわらず、REST サービスを対象としています。

キャッシュ値に特定の TTL を設定する必要があります。たとえば 5 分ですが、サーバーがダウンしている場合は、5 分延長されたとしても最後の値を返す必要があります。

私は TTL を持たず、常に最後の値を返す 2 番目のキャッシュ可能なメソッドを持つことを考えていました。restService が何も返さない場合は getValue から呼び出されますが、もっと良い方法があるでしょうか?

4

2 に答える 2

1

私もしばらくの間、これを行うことに興味がありました。申し訳ありませんが、これを行う簡単な方法は見つかりませんでした。Spring はこれを行いません。それは、Spring がラップしているキャッシュ実装がそれを実行できるかどうかという問題です。EhCache 実装を使用していると思います。残念ながら、私が知る限り、この機能はすぐには利用できません。

問題に応じて、同様のことを達成できるさまざまな方法があります

1)永遠のキャッシュ時間を使用し、キャッシュされたデータを定期的にループしてリフレッシュする第2クラスのスレッドを用意します。私はこれを正確には行っていませんが、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>

ファクトリー: import 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();
  } 
}

疑似実装 import 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 のクラスター化された環境で、それぞれが最初にデータベースをチェックして、他のクラスターが最初に「高価な」データを取得したかどうかを確認し、サーバーを呼び出すのではなくそれを再利用するバージョンを持っていました。

わずかな変形として、DB ではなく @CachePut と一緒に 2 番目の @Cacheable メソッドを使用してデータを保存することもできます。しかし、これはメモリ使用量が 2 倍になることを意味します。結果のサイズによっては、それが許容される場合があります。

于 2013-08-17T21:06:42.740 に答える
0

条件(サービスは稼働していますか?)がtrueまたはfalseの場合、spelを使用して使用済みキャッシュ(ttlを使用するものと使用しないもの)を変更できます。この方法でspelを使用したことはありません(キーを変更するために使用しました)いくつかのリクエストパラメーターに基づいています)しかし、私はそれがうまくいくと思います

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

checkService() は、使用するキャッシュの名前を返す静的メソッドです。

于 2013-08-16T20:55:34.243 に答える