티스토리 뷰
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의 app에서 붙었을때의 JSESSIONID 와 was2의 app에서 붙었을때 JSESSIONID 가 동일하다면 잘 된것이다.
springboot 1.x와 2.x는 session clustering 부분을 구현할때 차이가 있는데 1.x로는 구현을 해보지 않아서 잘 모르겠지만 분명 구현상의 차이가 있다. 2.x에서는 꼭 위와 같이 WebServerFactoryCustomizer<TomcatServletWebServerFactory> 를 implements 하여 구현을 하여야 한다.
보통은 springboot 사용을 하면서 jwt 등의 token 방식의 인증을 하기에 session은 잘 사용하지 않는 경우가 많은데 꼭 session을 사용해야 하는 경우라면 이렇게 사용하는것도 괜찮을것 같다.
끝!
'Framework > Spring' 카테고리의 다른 글
Springboot + RabbitMQ 연동 및 초간단 샘플 프로젝트 만들기 (8) | 2020.10.14 |
---|---|
springboot 기동 후 바로 종료되는 에러 [extShutdownHook] s.c.a.AnnotationConfigApplicationContext 해결방법 (0) | 2020.10.08 |
Spring properties 파일의 내용 암호화하기 (with Jasypt) (0) | 2020.08.26 |
Spring WebFlux는 무엇인가? 사용법은 어떻게 되나? (0) | 2020.08.25 |
Spring PropertyPlaceholderConfigurer를 통해 불러온 값이 null이 나오는 현상 해결방법 (0) | 2020.07.31 |