Cache 서비스
1. 개요
전자정부 프레임워크에서 Cache Service는 EhCache를 선정하여 가이드한다. 변경이 자주 일어나지 않지만 사용빈도가 높고 생성하는데 비용이 많이 드는 객체일 경우, Cache를 이용하면 다음과 같은 장점을 얻을 수 있다.
-자주 접근하는 데이터를 매번 데이터베이스로부터 fetch할 필요가 없으므로 오버헤드가 줄어든다.
-객체를 매번 생성하지 않기 때문에 메모리를 효율적으로 사용할 수 있다.
2. 설명
Cache를 이용하기 위한 기본 설정 및 기본 사용법에 대해서 설명한다.
Bookstrap Source
Cache를 사용하기 위해서 Cache Manager를 생성하는 방법을 샘플을 통해서 설명한다.
URL url = getClass().getResource("/ehcache-default.xml");
manager = new CacheManager(url);
위에서 getResource를 통해서 읽어들이는 /ehcache-default.xml의 파일 내용은 다음과 같다.
Configuration
<ehcache>
<diskStore path="user.dir/second"/>
<cache name="sampleMem"
maxElementsInMemory="3"
eternal="false"
timeToIdleSeconds="360"
timeToLiveSeconds="1000"
overflowToDisk="false"
diskPersistent="false"
memoryStoreEvictionPolicy="LRU">
</cache>
</ehcache>
Basic Usage Source
위에서 정의한 Cache Manager에서 Cache를 얻어서 기본적인 쓰고 읽고 지우는 것에 대한 샘플은 다음과 같다.
Cache cache = manager.getCache("sampleMem");
// 1. Cache에 데이터 입력
cache.put(new Element("key1", "value1"));
// 2. Cache로부터 입력한 데이터 읽기
Element value = cache.get("key1");
// 3. Cache에서 데이터 삭제
cache.remove("key1");
-manager.getCache: Manager를 이용하여 해당하는 Cache 얻기.
-cache.put : Cache에 자료 입력(같은 키 값에 대해서 다른 value를 입력하면 수정처리됨)
-cache.get : Cache에서 자료 읽기
-cache.remove : Cache에서 자료 삭제.
3. Cache Algorithm
Cache는 메모리를 이용하는 것을 기본으로 하기에 꼭 필요한 자료만을 관리하도록 보관 사이즈를 지정하고 보관 사이즈를 넘어설 경우 불필요한 자료부터 삭제처리하는데 필요한 자료에 대한 판단은 알고리즘을 통해서 한다. 지원되는 알고리즘은 LRU, FIFO, LFU 세가지 이고 각각에 대한 설정 및 사용법은 아래와 같다.
1) LRU Algorithm
최근에 이용한 것을 남기는 알고리즘으로 설정은 아래와 같이 한다.
Configuration
maxElementsInMemory="3"
...
memeryStoreEvictionPolicy="LRU">
위의 설정에서 최대 보관 엔트리 갯수(maxElementsInMemory)는 3으로 알고리즘 LRU로 설정된 것을 확인할 수 있다. 이 설정을 이용하여 설정에 대한 결과를 확인하는 소스는 다음과 같다.
Sample Source
...
cache.get("key2");
cache.get("key2");
cache.get("key1");
cache.get("key1");
cache.get("key3");
// 4. Put New element in cache.
cache.put(new Element("key4", "value4"));
// 5. get key2 but can't key2
assertNull("Can't get key2",cache.get("key2"));
먼저 입력된것을 제거하는 알고리즘으로 설정은 아래와 같이 한다.
maxElementsInMemory="3"
...
memoryStoreEvictionPolicy="FIFO">
위의 설정에서 최대 보관 엔트리 갯수(maxElementsInMemory)는 3으로 알고리즘은 FIFO 로 설정된 것을 확인할 수 있다. 이 설정을 이용하여 설정에 대한 결과를 확인하는 소스는 다음과 같다.
cache.put(new Element("key1", "value1"));
cache.put(new Element("key2", "value2"));
cache.put(new Element("key3", "value3"));
...
cache.get("key2");
cache.get("key2");
cache.get("key1");
cache.get("key1");
cache.get("key3");
// 4. Put New element in cache.
cache.put(new Element("key4", "value4"));
// 5. get key1 but can't key1
assertNull("Can't get key1",cache.get("key1"));
maxElementsInMemory="3"
...
memoryStoreEvictionPolicy="LFU">
위의 설정에서 최대 보관 엔트리 갯수(maxElementsInMemory)는 3으로 알고리즘은LFU 로 설정된 것을 확인할 수 있다. 이 설정을 이용하여 설정에 대한 결과를 확인하는
소스는 다음과 같다.
Sample Source
cache.put(new Element("key1", "value1"));
cache.put(new Element("key2", "value2"));
cache.put(new Element("key3", "value3"));
...
cache.get("key2");
cache.get("key2");
cache.get("key1");
cache.get("key1");
cache.get("key3");
// 4. Put New element in cache.
cache.put(new Element("key4", "value4"));
// 5. get key2 but can't key3
assertNull("Can't get key3",cache.get("key3"));
Cache에서 관리해야 하는 정보의 사이즈 설정 및 저장 디바이스 관련 설정을 할 수 있다.
1) Cache Device
디바이스 관련 세팅은 메모리 관리 캐쉬의 디스크 관리로의 이동관련 설정으로 메모리 관리 오브젝트 수가 넘었을때 Disk 로 이동여부, flush 호출시 파일로 저장 여부에대한 세팅이 있다. 이에 대한 세팅은 다음과 같다.
overflowToDisk="true"
diskPersistent="true"
...>
</cache>
-overflowToDisk : 메모리 관리 오브젝트 넘었을때 Disk로 이동 여부 ( true,false )
-diskPersistent : flush시에 파일로 저장 여부 ( trun , false )
이 설정을 이용하여 설정에 대한 결과를 확인하는 소스는 다음과 같다
// 1. Put a content in Cache.
cache.put(new Element("key1", "value1"));
// 2. Get that item from Cache.
Element value = cache.get("key1");
assertEquals("value1", value.getValue().toString());
// 3. flush 를 한다.
cache.flush();
File dataFile = new File(manager.getDiskStorePath()+ File.separator + "sampleDisk.data");
// 4. 파일로 저장 확인
assertTrue("File exists", dataFile.exists());
위의 샘플에서 flush 수행시 파일로 저장되는 것을 확인 할 수 있고, 메모리 관리 오브젝트 Disk이동 여부는 아래의 Cache Size의 예에서 확인한다.
2) Cache Size
사이즈 관련 세팅은 메모리에서 관리해야 할 최대 오브젝트 수, 디스크에서 관리하는 최대 오브젝트 수에 대한 세팅이 있다. 이에 대한 세팅은 다음과 같다.
maxElementsInMemory="3"
maxElementsOnDisk="2"
overflowToDisk="true"
...>
</cache>
-maxElementsInMemory : 메모리에서 관리하는 최대 오브젝트 수
-maxElementsOnDisk : 디스크에서 관리하는 최대 오브젝트 수
이 설정을 이용하여 설정에 대한 결과를 확인하는 소스는 다음과 같다
// 1. Put 3 contents in Cache.
cache.put(new Element("key1", "value1"));
cache.put(new Element("key2", "value2"));
cache.put(new Element("key3", "value3"));
// 2. Put Fourth content in Cache.
cache.put(new Element("key4", "value4"));
assertEquals(3, cache.getMemoryStoreSize()); //Memory에는 세개 유지함
assertEquals(1, cache.getDiskStoreSize()); //Disk로 하나 넘어감.
// 3. Put 5~7 contens in Cache.
cache.put(new Element("key5", "value5"));
cache.put(new Element("key6", "value6"));
cache.put(new Element("key7", "value7"));
// Disk Max Size에 관계 없이 모두 넘어감
assertEquals(3, cache.getMemoryStoreSize()); //Memory에는 세개 유지함
assertEquals(4, cache.getDiskStoreSize()); //Disk에는 2개를 넘어서서 4개 유지함.
// 메모리것을 디스크로 이동시킴
cache.flush();
// Disk의 Max Size 대로 변경됨.
assertEquals(0, cache.getMemoryStoreSize()); //Memory에는 없어짐.
assertEquals(2, cache.getDiskStoreSize()); //Disk에는 DiskMaxSize 대로 2개만 남김.
위의 예를 보면 cache.put에 의해서는 Disk의 MaxSize에 관계없이 계속 Memory에서 Disk로 넘어가지만 flush를 수행하면 최대 디스크 보관수만을 남기는 것을 확인 할 수 있다.
Ehcache는 Distributed Cache를 지원하는 방법으로 RMI,JGROUP,JMS등을 지원한다. 그 중에서 JGROUP와 ActiveMQ를 이용한 JMS 지원 설정 및 사용 방법을 설명한다. 자세한 설명은 EhcacheUserGuide 참고.
JGroups는 multicast 기반의 커뮤니케이션 툴킷으로 그룹을 생성하고 그룹멤버간에 메세지를 주고 받을 수 있도록 지원한다. 관련한 자세한 정보는 사이트참조
properties="connect=UDP(mcast_addr=224.10.10.10;mcast_port=5555;ip_ttl=32;
mcast_send_buf_size=150000;mcast_re-cv_buf_size=80000):
PING(timeout=2000;num_initial_members=6):
MERGE2(min_interval=5000;max_interval=10000):
FD_SOCK:VERIFY_SUSPECT(timeout=1500):
pbcast.NAKACK(gc_lag=10;retransmit_timeout=3000):
UNICAST(timeout=5000):
pbcast.STABLE(desired_avg_gossip=20000):
FRAG:
pbcast.GMS(join_timeout=5000;join_retry_timeout=2000;shun=false;print_local_addr=false)"
propertySeparator="::"/>
<cache name="cacheSync"
maxElementsInMemory="1000"
eternal="false"
timeToIdleSeconds="1000"
timeToLiveSeconds="1000"
overflowToDisk="false">
<cacheEventListenerFactory class="net.sf.ehcache.distribution.jgroups.JGroupsCacheReplicatorFactory"
properties="replicateAsynchronously=false, replicatePuts=true,
replicateUpdates=true, replicateUpdatesViaCopy=false,
replicateRemovals=true"/>
</cache>
이 설정을 이용하여 설정에 대한 결과를 확인하는 소스는 다음과 같다
URL url = this.getClass().getResource("/ehcache-distributed-jgroups.xml");
// 두개의 Cache Manager 생성
manager1 = new CacheManager(url);
manager2 = new CacheManager(url);
for (int i = 0; i < 10 ; i++) {
manager1.getEhcache(CACHE_SYNC).put(new Element(new Integer(i), "value"));
}
// 리플리케이션을 위한 시간 필요함.
Thread.currentThread().sleep(100);
// 리플리케이션이 되어 manager2에도 동일한 Cache 정보 입력됨 확인.
assertTrue(manager1.getEhcache(CACHE_SYNC).getKeys().size() == manager2.getEhcache(CACHE_SYNC).getKeys().size() );
ActiveMQ는 JMS 메세징 서비스를 제공하는 오픈 소스이다. 관련한 자세한 정보는 사이트참조
class="net.sf.ehcache.distribution.jms.JMSCacheManagerPeerProviderFactory"
properties="initialContextFactoryName=egovframework.rte.fdl.cache.distribu-te.TestActiveMQInitialContextFactory,
providerURL=tcp://localhost:61616,
replicationTopicConnectionFactoryBindingName=topicConnectionFactory,
getQueueConnectionFactoryBindingName=queueConnectionFactory,
replicationTopicBindingName=ehcache,
getQueueBindingName=ehcacheGetQueue"
propertySeparator=","
/>
<cache name="CacheAsync"
maxElementsInMemory="1000"
eternal="false"
timeToIdleSeconds="1000"
timeToLiveSeconds="1000"
overflowToDisk="false">
<cacheEventListenerFactory class="net.sf.ehcache.distribution.jms.JMSCacheReplicatorFactory"
properties="replicateAsynchronously=true,
replicatePuts=true,
replicateUpdates=true,
replicateUpdatesViaCopy=true,
replicateRemovals=true,
asynchronousReplicationIntervalMillis=1000"
propertySeparator=","/>
</cache>
이 설정을 이용하여 설정에 대한 결과를 확인하는 소스는 다음과 같다
manager1 = new CacheManager(url);
manager2 = new CacheManager(url);
cacheName = "CacheAsync";
for (int i = 0; i < 10 ; i++) {
manager1.getEhcache("CacheAsync").put(new Element(new Integer(i), "value"));
}
// replication 되는데 일정 시간이 필요함.
Thread.currentThread().sleep(1000);
assertTrue(manager1.getEhcache("CacheAsync").getKeys().size() == manager2.getEhcache("CacheAsync").getKeys().size() );
Spring에서 Ehcache를 이용하는 방법에 대해서 설정 및 설정을 이용한 기본 Cache 서비스에 대해서 설명한다.
Configuration - Spring Application Context
<property name="cacheManager">
<bean class="org.springframework.cache.ehcache.EhCacheManagerFactoryBean">
<property name="configLocation" value="classpath:ehcache-default.xml"/>
</bean>
</property>
</bean>
위와 같이 설정하면 Manager를 통하지 않고서 cache를 얻을 수 있다. ConfigLocation에 정의된 ehcache-default.xml은 Cache Basic의 Configuration에 설명한 설정파일과 동일한 것이다.
Ehcache gCache ;
// cache Name을 가지고 cache 찾기
Ehcache cache = gCache.getCacheManager().getCache("sampleMem");
cache.put(new Element("key1", "value1"));
Element value = cache.get("key1");
위의 예를 보면 ehcache를 이용하여 Ehcache를 가지고 오고 getCacheManager()를 통해서 이름을 통한 cache 정보를 읽어오는 것을 확인할 수 있다. 그 이후에는 위에서 설명한 것과 동일한 방식으로 사용하는 것을 확인 할 수 있다.