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