티스토리 뷰

springboot는 내장 톰캣을 사용하기에 server.xml 이라던지 context.xml 등등의 xml 설정파일이 없다. 하지만 이런 설정파일을 java config로 대체할 수 있다. 기존에 server.xml 에서 tomcat 간 session clustering을 했던것을 토대로 java config를 작성해본다. 기존 글과 비교해가면서 보면 이해가 쉽다. 


필자의 환경은 springboot 2.1.15.RELEASE, tomcat 9.0.36, openjdk 11 version 을 사용중이다. 

 

pom.xml

<dependency>
    <groupId>org.apache.tomcat</groupId>
    <artifactId>tomcat-catalina-ha</artifactId>
    <version>${tomcat.version}</version>
</dependency>

일단은 tomcat-catalina-ha 라는 dependency를 추가해줘야한다. 

 

 

TomcatClusterUtil.java

import org.apache.catalina.Context;
import org.apache.catalina.Engine;
import org.apache.catalina.ha.session.ClusterSessionListener;
import org.apache.catalina.ha.session.DeltaManager;
import org.apache.catalina.ha.session.JvmRouteBinderValve;
import org.apache.catalina.ha.tcp.ReplicationValve;
import org.apache.catalina.ha.tcp.SimpleTcpCluster;
import org.apache.catalina.tribes.group.GroupChannel;
import org.apache.catalina.tribes.group.interceptors.MessageDispatchInterceptor;
import org.apache.catalina.tribes.group.interceptors.TcpFailureDetector;
import org.apache.catalina.tribes.group.interceptors.TcpPingInterceptor;
import org.apache.catalina.tribes.membership.McastService;
import org.apache.catalina.tribes.transport.ReplicationTransmitter;
import org.apache.catalina.tribes.transport.nio.NioReceiver;
import org.apache.catalina.tribes.transport.nio.PooledParallelSender;
import org.springframework.boot.web.embedded.tomcat.TomcatContextCustomizer;
import org.springframework.boot.web.embedded.tomcat.TomcatServletWebServerFactory;
import org.springframework.boot.web.server.WebServerFactoryCustomizer;
import org.springframework.context.annotation.Configuration;

@Configuration
public class TomcatClusterUtil implements WebServerFactoryCustomizer<TomcatServletWebServerFactory> {
	
    @Override
    public void customize( final TomcatServletWebServerFactory factory ) {
        factory.addContextCustomizers( new TomcatClusterContextCustomizer() );
    }
}

class TomcatClusterContextCustomizer implements TomcatContextCustomizer {

    @Override
    public void customize( final Context context ) {  
        //web.xml <distribute/>
        context.setDistributable(true);
        
        //manager setting - BackupManager 사용해도 됨
        DeltaManager manager = new DeltaManager();
        manager.setExpireSessionsOnShutdown(false);
        manager.setNotifyListenersOnReplication(true);        
        context.setManager(manager);
        
        configureCluster( (Engine)context.getParent().getParent() );
    }

    private void configureCluster(Engine engine) {	
        //cluster setting
        SimpleTcpCluster cluster = new SimpleTcpCluster();
        cluster.setChannelSendOptions(6);
        
        //channel setting
        GroupChannel channel = new GroupChannel();   
        
        //membership setting
        McastService mcastService = new McastService();
        mcastService.setAddress("228.0.0.4");
        mcastService.setPort(45564);
        mcastService.setFrequency(500);
        mcastService.setDropTime(3000);
        channel.setMembershipService(mcastService);
        
        //receiver setting
        NioReceiver receiver = new NioReceiver();
        receiver.setAddress("auto");
        receiver.setMaxThreads(6);
        receiver.setPort(5000);        
        channel.setChannelReceiver(receiver);
        
        //sender setting
        ReplicationTransmitter sender = new ReplicationTransmitter();
        sender.setTransport(new PooledParallelSender());        
        channel.setChannelSender(sender);
        
        //interceptor setting
        channel.addInterceptor(new TcpPingInterceptor());
        channel.addInterceptor(new TcpFailureDetector());
        channel.addInterceptor(new MessageDispatchInterceptor());
        
        cluster.addValve(new ReplicationValve());
        cluster.addValve(new JvmRouteBinderValve());
        cluster.setChannel(channel);
        cluster.addClusterListener(new ClusterSessionListener());        
        engine.setCluster(cluster);
    }
}

이 파일 하나만 component-scan이 가능한(@Configuration이 인식 가능한) 적당한 위치에 생성해주면 된다. 자세히 들여다보면 server.xml 파일에서 설정했던 session clustering 설정임을 알수 있고 이것을 java config 로 전환을 한 것이다. membership, receiver 등에 들어가있는 address나 port 등을 변경하지 않는다. 변경해도 되지만 was1와 was2가 같은 값을 가지고 있도록 해줘야한다. 그 외 모든 설정들은 프로젝트의 상황에 따라 변경을 해준다. 


테스트

 

그냥 톰캣끼리 클러스터링 테스트를 할수도 있지만 필자는 조금더 운영환경에 가깝도록 nginx를 써서 was1, was2를 로드밸런싱을 설정했다. 테스트 방법은 다음과 같다. 

 

  • was1을 기동시킨다.
  • session을 생성하고 JSESSIONID를 확인한다.
  • was2를 기동시킨다.
  • was1을 종료시킨다. 
  • 아무 액션이나 수행한 후 JSESSIONID를 확인한다. 
  • 두개의 JSESSIONID 이 일치한다면 session clustering 성공

JSESSIONID는 크롬으로 치면 f12를 눌러서 나오는 개발자도구에서 Network > Headers > Request Headers > Cookie 에서 확인을 할 수 있다. 

 

WAS1과 WAS2의 JSESSIONID가 동일하다. 

즉 was1의 app에서 붙었을때의 JSESSIONID 와 was2의 app에서 붙었을때 JSESSIONID 가 동일하다면 잘 된것이다.


springboot 1.x와 2.x는 session clustering 부분을 구현할때 차이가 있는데 1.x로는 구현을 해보지 않아서 잘 모르겠지만 분명 구현상의 차이가 있다. 2.x에서는 꼭 위와 같이 WebServerFactoryCustomizer<TomcatServletWebServerFactory> 를 implements 하여 구현을 하여야 한다. 

 

보통은 springboot 사용을 하면서 jwt 등의 token 방식의 인증을 하기에 session은 잘 사용하지 않는 경우가 많은데 꼭 session을 사용해야 하는 경우라면 이렇게 사용하는것도 괜찮을것 같다. 

 

끝!

 

댓글
최근에 올라온 글
최근에 달린 댓글
«   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