출처: http://debop.blogspot.kr/2013/03/spring-31-memcached-cache.html
Memcached 는 캐시들을 구분하는 개념이 없다는 게 하나의 특징이고, 분산 환경을 지원하므로, 직렬화/역직렬화를 통해 저장/로드 됩니다.
이것 때문에 당연히 In-Proc 인 ehcache 보다야 속도가 느리지만, HA 구성 시에는 캐시 서버로 좋은 선택이 될 수 있습니다.
그럼 먼저 MemcachedCacheManager 를 정의하면
@Slf4j public class MemcachedCacheManager extends AbstractTransactionSupportingCacheManager { @Getter private MemcachedCache memcachedCache; //protected MemcachedCacheManager() {} public MemcachedCacheManager(MemcachedClient memcachedClient) { Guard.shouldNotBeNull(memcachedClient, "memcachedClient"); memcachedCache = new MemcachedCache(memcachedClient); } public MemcachedCacheManager(MemcachedClient memcachedClient, int expireSeconds) { Guard.shouldNotBeNull(memcachedClient, "memcachedClient"); memcachedCache = new MemcachedCache(memcachedClient, expireSeconds); } @Override protected Collection<? extends Cache> loadCaches() { super.getCacheNames(); return Sets.newHashSet(memcachedCache); } @Override public Cache getCache(String name) { return memcachedCache; } } |
과 같습니다.
실제 캐시에 데이터를 저장/로드하는 Cache 는 다음과 같습니다.
@Slf4j public class MemcachedCache implements Cache { public static final int EXP_TIME = 100000; @Getter private String name = "default"; @Getter private MemcachedClient nativeCache = null; @Getter private int expireSeconds; // protected MemcachedCache() {} public MemcachedCache(MemcachedClient client) { this(client, EXP_TIME); } public MemcachedCache(MemcachedClient client, int expireSeconds) { Guard.shouldNotBeNull(client, "client"); this.nativeCache = client; this.expireSeconds = expireSeconds; if (log.isDebugEnabled()) log.debug("MemcachedCache를 생성했습니다"); } @Override public ValueWrapper get(Object key) { Guard.shouldNotBeNull(key, "key"); GetFuture<Object> result = nativeCache.asyncGet(key.toString()); SimpleValueWrapper wrapper = null; try { if (result.get() != null) wrapper = new SimpleValueWrapper(result.get()); } catch (Exception ignored) {} return wrapper; } @Override public void put(Object key, Object value) { Guard.shouldNotBeNull(key, "key"); if (!(key instanceof String)) { log.error("Invalid key type: " + key.getClass()); return; } OperationFuture<Boolean> setOp = null; setOp = nativeCache.set(key.toString(), expireSeconds, value); if (log.isInfoEnabled()) { if (setOp.getStatus().isSuccess()) { log.info("객체를 캐시 키[{}]로 저장했습니다.", key); } else { log.info("객체를 캐시 키[{}]로 저장하는데 실패했습니다. operation=[{}]", key, setOp); } } } @Override public void evict(Object key) { if (key != null) nativeCache.delete(key.toString()); } @Override public void clear() { nativeCache.flush(); } } |
캐시 저장 시, 이미 있다면 update 되도록 set() 메소드를 사용합니다.
다음으로는 Spring 환경 설정을 보겠습니다. 뭐 특별한 것은 없고, MemcachedClient 를 생성해서 제공해 주면 됩니다.
@Configuration @EnableCaching @ComponentScan(basePackageClasses = {UserRepository.class}) @Slf4j public class MemcachedCacheConfiguration { @Bean public Transcoder<Object> transcoder() { SerializingTranscoder transcoder = new SerializingTranscoder(Integer.MAX_VALUE); transcoder.setCompressionThreshold(1024); return transcoder; } /** * MemcachedClient 를 제공해야 합니다 */ @Bean public MemcachedClient memcachedClient() { try { // 설정항목 : https://code.google.com/p/spymemcached/wiki/SpringIntegration MemcachedClientFactoryBean bean = new MemcachedClientFactoryBean(); bean.setServers("localhost:11211"); // servers="host1:11211,host2:11211"; bean.setProtocol(ConnectionFactoryBuilder.Protocol.BINARY); bean.setTranscoder(transcoder()); bean.setOpTimeout(1000); // 1000 msec bean.setHashAlg(DefaultHashAlgorithm.KETAMA_HASH); bean.setLocatorType(ConnectionFactoryBuilder.Locator.CONSISTENT); return (MemcachedClient) bean.getObject(); } catch (Exception ignored) { throw new RuntimeException(ignored); } } @Bean public MemcachedCacheManager memcachedCacheManager() { int timeoutInSeconds = 300; return new MemcachedCacheManager(memcachedClient(), timeoutInSeconds); } } |
마지막으로 테스트 코드는 ehcache 예와 같습니다.
@Slf4j @RunWith(SpringJUnit4ClassRunner.class) @ContextConfiguration(classes = {MemcachedCacheConfiguration.class}) public class MemcachedCacheTest { @Autowired MemcachedCacheManager cacheManager; @Autowired UserRepository userRepository; @Test public void getUserFromCache() { Stopwatch sw = new Stopwatch("initial User"); sw.start(); User user1 = userRepository.getUser("debop", 100); sw.stop(); sw = new Stopwatch("from Cache"); sw.start(); User user2 = userRepository.getUser("debop", 100); sw.stop(); Assert.assertEquals(user1, user2); } @Test public void componentConfigurationTest() { Assert.assertNotNull(cacheManager); Cache cache = cacheManager.getCache("user"); Assert.assertNotNull(cache); Assert.assertNotNull(userRepository); } } |
어떻습니까? 캐시와 관련해서 개발자가 최소한의 설정만으로 캐시를 효과적으로 사용할 수 있게 되었습니다.
물론 다양한 캐시를 쓸 수 있어, 용도에 맞게 사용하면 더 좋을 듯 합니다.
'IT_Programming > Dev Libs & Framework' 카테고리의 다른 글
[Spring] Spring 3.1 + MongoDB 를 이용한 Cache (0) | 2015.07.09 |
---|---|
[Spring] Spring 3.1 + Couchbase 를 이용하여 Cache 만들기 (0) | 2015.07.09 |
[Spring] Spring 3.1 + EhCache 를 이용하여 캐시 사용하기 (0) | 2015.07.09 |
Rx (Reactive Extensions) (0) | 2015.06.29 |
[펌] java. google Guava 라이브러리를 사용해서 함수형 프로그래밍(Functional Programming) 시도하기 (0) | 2015.02.23 |