특히 단순한 캐시 시스템의 경우, 캐시 시스템을 데이터 소스와는 별개로 다뤄야 하는 귀찮은 점이 있는데, 아주 간단하지만, 아예 "캐시 시스템 네가 필요할 때 데이터 소스를 직접 조회해서 캐시에 담아 놓으렴", 그럼 난 그냥 그걸 사용할께... 이런 목적에 딱 맞게 제공되는 클래스가 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 LoadingCachecache; 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 도 이제 기본 중에 기본이 될 것입니다.
그 이외에도 아주 많은 유용한 기능이 있으니 공부해 보시기 바랍니다.^^
댓글 없음:
댓글 쓰기