IT_Programming/Dev Libs & Framework

[Spring] Spring 3.1 + MongoDB 를 이용한 Cache

JJun ™ 2015. 7. 9. 00:40



 출처: http://debop.blogspot.kr/2013/03/spring-31-mongodb-cache.html





앞서 EhCache, Memcached 는 일반적으로 메모리를 저장소로 사용합니다만, MongoDB 와 Couchbase 는 정보를
Document 로 관리하는 NoSQL DB라 볼 수 있습니다.


이런 의미로 단순 캐시로 쓰기에는 너무 많은 기능을 가지고 있다고 봐야합니다. 그런만큼 메모리에서만 작동하는 캐시시스템보다는 성능은 느립니다.
하지만 대용량 데이터나 캐시해야 할 양이 엄청 많다면? 그리고 한번 캐시한 거 영구 저장이 되면 좋겠다면? (물론 삭제 기능은 있고...)

에이 그런거라면 차라리 주 저장소를 Document DB로 바꾸는게 낫죠.. 

아 그렇긴 하네요... 근데, 개발자들이 NoSQL을 아주 단순히 쓰는 큰 이유는
RDBMS 처럼 관계형 데이터 처리에 익숙해서 key-value나 Document, grid 에 대한 처리에 난감해 한다는 거지요.  
이 문제는 hibernate-ogm 으로 어떻게 해결하는지 향후 글을 써 보겠습니다.


우선 MongoDB 로도 가능하다는 보여 드리죠.

우선 제가 사용한 라이브러리는 spring-data-mongo, mongo-java-driver 입니다.

1. MongoCacheManager


@Slf4j
public class MongoCacheManager extends AbstractTransactionSupportingCacheManager {
    private MongoTemplate mongoTemplate;
    public MongoCacheManager(MongoTemplate mongoTemplate) {
        Guard.shouldNotBeNull(mongoTemplate, "mongoTemplate");
        this.mongoTemplate = mongoTemplate;
    }
    @Override
    protected Collection<? extends Cache> loadCaches() {
        Collection<Cache> caches = Lists.newArrayList();
        for (String name : getCacheNames()) {
            caches.add(new MongoCache(name, mongoTemplate));
        }
        return caches;
    }
    @Override
    public Cache getCache(String name) {
        synchronized (this) {
            Cache cache = super.getCache(name);
            if (cache == null) {
                cache = new MongoCache(name, mongoTemplate);
                addCache(cache);
            }
            return cache;
        }
    }
}



2. MongoCache


@Slf4j

public class MongoCache implements Cache { private String name; private MongoTemplate mongoTemplate; public MongoCache(String name, MongoTemplate mongoTemplate) { Guard.shouldNotBeEmpty(name, "name"); Guard.shouldNotBeNull(mongoTemplate, "mongoTemplate"); this.name = name; this.mongoTemplate = mongoTemplate; if (log.isDebugEnabled()) log.debug("MongoCache를 생성합니다. name=[{}], mongodb=[{}]", name, mongoTemplate.getDb().getName()); } @Override public String getName() { return name; } @Override public Object getNativeCache() { return mongoTemplate; } @Override public ValueWrapper get(Object key) { Guard.shouldNotBeNull(key, "key"); CacheItem item = mongoTemplate.findOne(new Query(Criteria.where("key").is(key)), CacheItem.class, name); Object result = null; if (item != null) { result = item.getValue(); if (log.isDebugEnabled()) log.debug("캐시 값을 로드했습니다. key=[{}], value=[{}]", key, result); } SimpleValueWrapper wrapper = null; if (result != null) wrapper = new SimpleValueWrapper(result); return wrapper; } @Override public void put(Object key, Object value) { Guard.shouldNotBeNull(key, "key"); if (!mongoTemplate.collectionExists(name)) mongoTemplate.createCollection(name); if (log.isDebugEnabled()) log.debug("캐시에 값을 저장합니다. key=[{}], value=[{}]", key, value); if (get(key) == null) mongoTemplate.insert(new CacheItem(key, value), name); else mongoTemplate.upsert(new Query(Criteria.where("key").is(key)), Update.update("value", value), name); } @Override public void evict(Object key) { Guard.shouldNotBeNull(key, "key"); mongoTemplate.remove(new Query(Criteria.where("key").is(key)), name); } @Override public void clear() { mongoTemplate.dropCollection(name); } @Getter @Setter public static class CacheItem implements Serializable { private Object key; private Object value; public CacheItem() { this(null, null); } public CacheItem(Object key, Object value) { this.key = key; this.value = value; } @Override public String toString() { return "CacheItem@{key=" + key + ", value=" + value + "}"; } } }



MongoCacheManager 는 별 내용 없구요...
MongCache는 Spring 의 MongoTemplate 를 적극 이용하여, 저장/검색을 수행하였습니다.

상세 내용은 Spring Data Mongo 매뉴얼을 보시면 되겠습니다.


3. MongoCacheConfiguration


@Configuration
@EnableCaching
@ComponentScan(basePackageClasses = {UserRepository.class})
@Slf4j
public class MongoCacheConfiguration extends AbstractMongoConfiguration {
    @Override
    protected String getDatabaseName() {
        return "debop4j_core_cache";
    }
    @Override
    @Bean
    public Mongo mongo() throws Exception {
        return new Mongo("localhost");
    }
    @Bean
    public MongoCacheManager mongoCacheManager() throws Exception {
        return new MongoCacheManager(mongoTemplate());
    } 

}



너무 간단하죠? 나머지 테스트 코드는 앞 EhCache, Memcached 의 예와 똑같아서 생략하겠습니다.
MongoDB의 경우 Database를 지정해주고, 각 캐시명을 Collection 명으로 매핑합니다.
그럼 이만...