16 Eylül 2020 Çarşamba

CacheLoader Sınıfı - Kullanmayın Caffeine Daha İyi

Giriş
Şu satırı dahil ederiz.
import com.google.common.cache.CacheBuilder;
import com.google.common.cache.CacheLoader;
import com.google.common.cache.LoadingCache;
Kullanım
Şöyle yaparız. Burada CacheLoader'ın null döndürebileceği kontrol edilmiyor. Normalde etmek lazım.
LoadingCache<String, Plan> something = 
   CacheBuilder.newBuilder()
     .concurrencyLevel(10)
     .maximumSize(1000) // Maximum of 1000 records can be cached
     .expireAfterWrite(60, TimeUnit.MINUTES) // Cache will expire after 60 minutes
     .build(new CacheLoader<String, Plan>() { // Build the CacheLoader
       @Override
       public Plan load(String key) throws Exception{
        Plan record = getGraphFromDB(key);
        return record;
      }
});
asyncReloading metodu
Bir Executor tarafından çalıştırılan reload() metodunu kolayca tanımlamayı sağlar.
Örnek
Şöyle yaparız.
LoadingCache<String, Graph> cache1 = CacheBuilder.newBuilder()
  .refreshAfterWrite(15, TimeUnit.MINUTES)
 .build(CacheLoader.asyncReloading(CacheLoader.from(this::getGraphFromDB),
    Executors.newSingleThreadExecutor()));
Örnek
Şöyle yaparız
LoadingCache<String, Graph> loadingCacheSuccint = CacheBuilder.newBuilder()
  .refreshAfterWrite(15, TimeUnit.MINUTES)
  .recordStats()
  .build(CacheLoader.asyncReloading(CacheLoader.from(this::getGraphFromDB),
         Executors.newSingleThreadExecutor())
);
load metodu
İmzası şöyle.
public V load(K key);
Açıklaması şöyle. Yani null dönemez
Returns:
  the value associated with key; must not be null
Throws:
  Exception - if unable to load the result
Eğer load işlemi null dönerse exception fırlatmak gerekir. Şöyle yaparız
new CacheLoader<ObjectId, User>() {
  @Override
  public User load(ObjectId k) throws Exception {
    User u = DataLoader.datastore.find(User.class).field("_id").equal(k).get();
    if (u != null) {
      return u;
    } else {
      throw new UserNotFoundException();
    }
  }
}
LoadingCache sınıfının getUnchecked() metodu çağrılınca tetiklenir. Şöyle yaparız.
String v = myCache.getUnchecked(key);
Örnek
Şöyle yaparız.
new CacheLoader<String, Graph>() {
  @Override
  public Graph load(String key) {
    return ...;
  }
  ...
});
Örnek
Şöyle yaparız.
new CacheLoader<Long, String>() {

  public String load(Long key) {
    System.out.println("load() " + key);
    return "INITIAL";
  }
  ...
});
loadAll metodu
İmzası şöyle.
public Map<K, V> loadAll(Iterable<? extends K> ids);
LoadingCache sınıfının getAll() metodu çağrılınca tetiklenir.
Örnek
Şöyle yaparız.
this.cache = CacheBuilder.newBuilder().maximumSize(1000).build(
  new CacheLoader<String, NamedEntity>() {

    @Override
    public Map<String, NamedEntity> loadAll(Iterable<? extends String> keys)
    throws Exception {
      Map<String, NamedEntity> map = new ConcurrentHashMap<>();
      ...
      return map;
  }
});


try {
  entities = cache.getAll(keys);
} catch (Exception e) {
  e.printStackTrace();
}
reload metodu
Açıklaması şöyle.
CacheLoader.reload() takes a key and old value and returns a ListenableFuture.
Eğer bu metodu override etmezsek load() metodunu çağırır.Açıklaması şöyle.
... default implementation calls load() synchronously, 
LoadingCache sınıfının refresh() metodu çağrılınca tetiklenir. Şöyle yaparız.
myCache.refresh(key);
Örnek - asenkron çalışma
Şöyle yaparız. Burada işi kendi ExecutorService nesnemize havale ediyoruz
LoadingCache<String, Graph> cache = CacheBuilder.newBuilder()
  .refreshAfterWrite(2, TimeUnit.MINUTES)
  .recordStats()           
   .build(new CacheLoader<String, Graph>() {
    @Override
    public Graph load(String key) {
      return ...;
    }

    public ListenableFuture<Graph> reload(final String key, Graph prev) {
      ListenableFutureTask<Graph> task = ListenableFutureTask.create(
      new Callable<Graph>() {
        public Graph call() {
          Graph graph = ...
          ...
          return graph;
        }
      });
      executor.execute(task);
      return task;
  }
});
Örnek - asenkron çalışma
Şöyle yaparız. Burada işi kendi ExecutorService nesnemize havale ediyoruz
new CacheLoader<Long, String>() {

  ExecutorService executor = Executors.newFixedThreadPool(10);

  public ListenableFuture<String> reload(final Long key, String prevString) {
    // asynchronous!
    ListenableFutureTask<String> task = ListenableFutureTask.create(() -> {
      ...
      return "Calculated value for " + key;
    });
    executor.execute(task);
    return task;
  }  
});
Örnek
Şöyle yaparız. Burada kendi ExecutorService sınıfımız yerine redis kullanılıyor.
@Override
public ListenableFuture<Object> reload(RedisKey<?> key, Object oldValue)
  throws Exception {
  CompletionStage<byte[]> f = redisClusterClient.asyncGet(key.getActualKey())
    .asCompletionStage();
  ListenableFutureTask<Object> task = ListenableFutureTask.create(
    () -> f.toCompletableFuture().get());
  f.thenRun(task::run);
  return task;
}
Açıklaması şöyle.
My RedisClient has a asyncGet() function, so I wanted to utilize it instead of maintaining a executorservice of my own and submitting it a ListenableFutureTask


Hiç yorum yok:

Yorum Gönder