특히 단순한 캐시 시스템의 경우, 캐시 시스템을 데이터 소스와는 별개로 다뤄야 하는 귀찮은 점이 있는데, 아주 간단하지만, 아예 "캐시 시스템 네가 필요할 때 데이터 소스를 직접 조회해서 캐시에 담아 놓으렴", 그럼 난 그냥 그걸 사용할께... 이런 목적에 딱 맞게 제공되는 클래스가 LoadingCache 라는 게 있습니다.
아래 코드는 특정 URL 의 컨텐츠를 메모리 상에서 캐시하고, 제공할 수 있도록 해주는 클래스인데, Google Guava의 LoadingCache를 이용하여 구현하였습니다.
package kr.kth.commons.caching.repository;
import com.google.common.cache.CacheBuilder;
import com.google.common.cache.CacheLoader;
import com.google.common.cache.LoadingCache;
import kr.kth.commons.caching.CacheRepositoryBase;
import kr.kth.commons.tools.StringTool;
import lombok.extern.slf4j.Slf4j;
import org.apache.http.HttpResponse;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.impl.nio.client.DefaultHttpAsyncClient;
import org.apache.http.nio.client.HttpAsyncClient;
import org.apache.http.util.EntityUtils;
import java.nio.charset.Charset;
import java.util.Arrays;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.Future;
/**
 * Google Guava 의 {@link LoadingCache} 를 이용하여, 캐시 값을 구하는 방법을 미리 지정하여, 쉽게 캐시를 운영할 수 있도록 캐시입니다.
 * User: sunghyouk.bae@gmail.com
 * Date: 12. 12. 5.
 */
@Slf4j
public class FutureWebCacheRepository extends CacheRepositoryBase {
 private final LoadingCache cache;
 public FutureWebCacheRepository() {
  cache =CacheBuilder.newBuilder().build(getCacheLoader());
 }
 @Override
 public Object get(String key) {
  try {
   return cache.get(key);
  } catch (ExecutionException e) {
   throw new RuntimeException(e);
  }
 }
 @Override
 public void set(String key, Object value, long validFor) {
  String str = (value != null) ? value.toString() : "";
  cache.put(key, str);
 }
 @Override
 public void remove(String key) {
  cache.invalidate(key);
 }
 @Override
 public void removes(String... keys) {
  cache.invalidateAll(Arrays.asList(keys));
 }
 @Override
 public boolean exists(String key) {
  return cache.getIfPresent(key) != null;
 }
 @Override
 public void clear() {
  cache.cleanUp();
 }
 private static CacheLoader getCacheLoader() {
  return
   new CacheLoader() {
    @Override
    public String load(String key) throws Exception {
     if (FutureWebCacheRepository.log.isDebugEnabled())
      FutureWebCacheRepository.log.debug("URI=[{}] 의 웹 컨텐츠를 비동기 방식으로 다운로드 받아 캐시합니다.", key);
     String responseStr = "";
     HttpAsyncClient httpClient = new DefaultHttpAsyncClient();
     try {
      httpClient.start();
      HttpGet request = new HttpGet(key);
      Future future = httpClient.execute(request, null);
      HttpResponse response = future.get();
      responseStr = EntityUtils.toString(response.getEntity(), Charset.forName("UTF-8"));
      if (log.isDebugEnabled())
       log.debug("URI=[{}]로부터 웹 컨텐츠를 다운로드 받았습니다. responseStr=[{}]",
                 key, StringTool.ellipsisChar(responseStr, 255));
     } finally {
      httpClient.shutdown();
     }
     return responseStr;
    }
   };
 }
}
    
코드를 보시면 아시겠지만, 처음 필요할 때 웹에서 컨텐츠를 다운받고, 그것을 캐시하여 사용하도록 하고 있습니다.
이 코드에서는 Expiry 에 대한 정책은 지정할 수 없게 되어 있지만, 직접 구현하던가, Guava에서 지원하는지 조사해서 지원한다면 그걸 사용하는 게 정신 건강에 좋겠죠^^
이번 글은 java 에서 apache commons 가 기본 중의 기본 라이브러리지만, google guava 도 이제 기본 중에 기본이 될 것입니다.
그 이외에도 아주 많은 유용한 기능이 있으니 공부해 보시기 바랍니다.^^
 
 
댓글 없음:
댓글 쓰기