티스토리 뷰

앞서 다음과 같이 EhCache를 사용하는 방법과 설정하는 방법에 대해 살펴보았다. 

 

Springboot + Ehcache 초간단 설정 및 사용방법

cache는 거의 변경될 여지가 없는 DB 조회 결과값을 메모리 혹은 디스크에 보관하고 있다가 다시 호출되었을때는 DB 조회를 하지 않고 메모리에 있는 결과값을 그대로 쓰고 싶을때 사용한다. 많은

oingdaddy.tistory.com

 

 

EhCache xml config를 java config로 변환하기

spring의 version이 올라가면서 모든 설정은 java config로 하기를 권장하고 있다. 이에 따라서 관련된 글도 포스팅도 여러개 했었다. Spring 설정 Xml Config에서 Java Config로 바꾸는 방법 (요령) spring 프로..

oingdaddy.tistory.com

이번에 다룰 내용은 EhCache를 이용해 분산캐시(Cache Clustering)를 하는 방법에 대해 알아보겠다.

 

분산캐시의 목적어플리케이션이 여러개의 노드에 올라가있는 상황에서 한 노드의 캐시에 변화가 생기면 나머지 노드에 그 변경 내용을 전달하여 같은 상태로 유지하는 것이다.

각 노드간 캐시 데이터 전송은 자바에서 기본적으로 제공하는 원격 메커니즘인 RMI를 통해 이루어진다.

RMI를 하기 위해서는 노드를 발견해야 하는데 이 글에서는 멀티캐스트 방식을 통해서 다른 노드를 발견하도록 하였다. 

멀티캐스트 방식은 지정한 멀티캐스트 IP(224.0.0.1~239.255.255.255)와 포트에 참여하는 노드를 자동으로 발견하는 것으로써 지정한 IP와 Port에 참여한 노드는 자기 자신을 다른 노드에 통지하므로 클러스터에 동적으로 노드를 추가하거나 제거살 수 있다. 

 

이것을 어떤 방식으로 설정하고 사용하는지 살펴보도록 하자. 


분산캐시 설정

import org.springframework.cache.annotation.EnableCaching;
import org.springframework.cache.ehcache.EhCacheCacheManager;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

import net.sf.ehcache.Cache;
import net.sf.ehcache.config.CacheConfiguration;
import net.sf.ehcache.config.CacheConfiguration.CacheEventListenerFactoryConfiguration;
import net.sf.ehcache.config.CacheConfiguration.TransactionalMode;
import net.sf.ehcache.config.DiskStoreConfiguration;
import net.sf.ehcache.config.FactoryConfiguration;
import net.sf.ehcache.config.PersistenceConfiguration;

@EnableCaching
@Configuration
public class CacheConfig {
    
    private net.sf.ehcache.CacheManager createCacheManager() {
        
        net.sf.ehcache.config.Configuration configuration = new net.sf.ehcache.config.Configuration();
        configuration.diskStore(new DiskStoreConfiguration().path("java.io.tmpdir"));
        // Cluster 설정
        this.setClusterConfig(configuration);
        
        return net.sf.ehcache.CacheManager.create(configuration);
    }
    
    // Cluster 설정
    private void setClusterConfig(net.sf.ehcache.config.Configuration configuration) {
        // multicast 설정
        FactoryConfiguration factoryConfig = new FactoryConfiguration()
            .className("net.sf.ehcache.distribution.RMICacheManagerPeerProviderFactory")
            .properties("peerDiscovery=automatic,multicastGroupAddress=230.0.0.1,multicastGroupPort=44444,timeToLive=32")
            .propertySeparator(",");
        
        // 다른 노드에서 발생한 이벤트 리스너 
        FactoryConfiguration listenerFactoryConfig = new FactoryConfiguration()
            .className("net.sf.ehcache.distribution.RMICacheManagerPeerListenerFactory")
            .properties("socketTimeoutMillis=2000")
            .propertySeparator(",");
        
        configuration.addCacheManagerPeerProviderFactory(factoryConfig);
        configuration.addCacheManagerPeerListenerFactory(listenerFactoryConfig);
    }
    
    @Bean
    public EhCacheCacheManager ehCacheCacheManager() {
        
        net.sf.ehcache.CacheManager manager = this.createCacheManager();

        // 캐시 별 변경 내역을 전송 설정
        CacheEventListenerFactoryConfiguration  eventListenerFactoryConfig = new CacheEventListenerFactoryConfiguration()
                .className("net.sf.ehcache.distribution.RMICacheReplicatorFactory")
                .properties("replicateAsynchronously=true,replicatePuts=false,replicateUpdates=true,replicateUpdatesViaCopy=false,replicateRemovals=true")
                .propertySeparator(",");
       
        Cache getMenuCache = new Cache(new CacheConfiguration()
                .maxEntriesLocalHeap(1000)           
                .maxEntriesLocalDisk(10000)
                .eternal(false)                     
                .timeToIdleSeconds(1800)            
                .timeToLiveSeconds(1800)            
                .memoryStoreEvictionPolicy("LRU") 
                .transactionalMode(TransactionalMode.OFF)
                .persistence(new PersistenceConfiguration().strategy(PersistenceConfiguration.Strategy.LOCALTEMPSWAP))
                .name("getMenu")
                .cacheEventListenerFactory(eventListenerFactoryConfig) // Cluster 설정
        );
        manager.addCache(getMenuCache);
        
        return new EhCacheCacheManager(manager);
    }
}

기본적인 캐시 설정에서 주석으로 명시한 부분이 분산 캐시를 위해 추가된 부분이라고 보면 된다. 

 

23 line : CacheManager에 Cluster 설정을 넣는다. 분산 캐시를 하지 않으려면 이 부분만 주석처리하면 된다. 

31 line : 멀티캐스트 설정을 하는 부분이다. (RMICacheManagerPeerProviderFactory)

property desc
peerDiscovery  automatic = 멀티캐스트 방식
multicastGroupAddress  멀티캐스트 IP
multicastGroupPort  포트 번호

37 line : 다른 노드에서 발생한 이벤트에 대한 리스너 설정을 하는 부분이다.  (RMICacheManagerPeerListenerFactory)

property desc
port  메시지를 수신할 때 사용되는 포트
socketTimeoutMillis  이 노드에 메시지를 보냈을 때 메시지 전송을 기다리는 시간. 기본값은 2000ms.

52 line : 캐시별 변경내용에 대한 전송 설정 부분이다. (RMICacheReplicatorFactory)

property desc default value
replicatePuts 캐시에 새로운 요소가 추가됐을 때 다른 노드에 복사할지의 여부 true
replicateUpdates 캐시 요소의 값이 변경되었을 때 다른 노드에 값을 복사할지의 여부 true
replicateRemovals 캐시 요소가 삭제되었을 때 다른 노드에 반영할지의 여부 true
replicateAsynchronously 비동기로 값을 복사할지의 여부 true
replicateUpdatesViaCopy 새로운 요소를 다른 노드에 복사할지 아니면 삭제 메시지를 보낼지의 여부 true
asynchronousReplicationIntervalMillis 비동기 방식을 사용할 때 변경 내역을 다른 노드에 통지하는 주기 1000

 

즉 분산 캐시를 하기 위해서는 RMICacheManagerPeerProviderFactory, RMICacheManagerPeerListenerFactory, RMICacheReplicatorFactory 에 대한 설정이 필요하다. 

 

이렇게 설정을 하고 각각 다른 서버에서 동일한 어플리케이션을 기동해보자. EhCache 관련 설정들이 쭉 로딩되는것이 보이고 다 기동이 되면 다른 서버에 올라간 어플리케이션과 연결된것을 확인할 수 있다. 

[13:37:35.990][DEBUG][net.sf.ehcache.distribution.MulticastKeepaliveHeartbeatReceiver.processPayload:line148] - rmiUrls received //xxx.xxx.xxx.xxx:55755/getMenu
[13:37:35.990][DEBUG][net.sf.ehcache.distribution.RMICacheManagerPeerProvider.lookupRemoteCachePeer:line126] - Lookup URL //xxx.xxx.xxx.xxx:55755/getMenu

끝!

 

참고 : https://javacan.tistory.com/entry/133

댓글
최근에 올라온 글
최근에 달린 댓글
«   2024/05   »
1 2 3 4
5 6 7 8 9 10 11
12 13 14 15 16 17 18
19 20 21 22 23 24 25
26 27 28 29 30 31