SpringBoot TomcatClusterContextCustomizer ( session 클러스터링 ) 사용하기

신규 프로젝트를 진행하면서 한개의 웹서버에 여러개의 was 를 두어 동시접속자가 많을 경우에 대한 분산처리를 대비하는 경우가 있습니다.

L4 장비가 있고, 서버의 대수가 많아 하드웨어가 여유롭다면 세션서버를 따로 두고 여러 서버에서 세션을 공유하며 사용하는 방법도 있습니다.


한개의 웹서버, 여러개의 was를 한개의 서버에서 사용하는 경우 was끼리 세션을 공유하며 사용하는 방법을 알아보겠습니다.

개발환경은 SpringBoot 2.2.4, Tomcat 9.0.43, nginx 1.8.x, openjdk8 입니다.


1. SpringBoot Gradle에 tomcat-catalina-ha 라는 dependency를 추가합니다.

  1. compile group: 'org.apache.tomcat', name: 'tomcat-catalina-ha', version: '9.0.43'


2. TomcatClusterUtil Class를 생성합니다.

- component-scan이 가능한 @Configuration 어노테이션이 인식 가능한 위치에 생성해주면 됩니다.

- 아래 Util에는 server.xml 에서 설정했던 세션 클러스터링 설정임을 알 수 있고 이것을 java 에서 상속받아 사용합니다.

- membership, reveiver 등에 들어가있는 address나 port등은 변경하지 않고 사용하며 변경할 경우 각 was마다 같은 값을 가지고 있도록 설정합니다.

  1. @Configuration
  2. public class TomcatClusterUtil implements WebServerFactoryCustomizer<TomcatServletWebServerFactory> {
  3. @Override
  4. public void customize(final TomcatServletWebServerFactory factory) {
  5. factory.addContextCustomizers(new TomcatClusterContextCustomizer());
  6. }
  7. }
  8. class TomcatClusterContextCustomizer implements TomcatContextCustomizer {
  9. @Override
  10. public void customize(final Context context) {
  11. //web.xml <distribute/>
  12. context.setDistributable(true);
  13. //manager setting - BackupManager 사용해도 됨
  14. DeltaManager manager = new DeltaManager();
  15. manager.setExpireSessionsOnShutdown(false);
  16. manager.setNotifyListenersOnReplication(true);
  17. context.setManager(manager);
  18. configureCluster((Engine) context.getParent().getParent());
  19. }
  20. private void configureCluster(Engine engine) {
  21. //cluster setting
  22. SimpleTcpCluster cluster = new SimpleTcpCluster();
  23. cluster.setChannelSendOptions(6);
  24. //channel setting
  25. GroupChannel channel = new GroupChannel();
  26. //membership setting
  27. McastService mcastService = new McastService();
  28. mcastService.setAddress("228.0.0.4");
  29. mcastService.setPort(45564);
  30. mcastService.setFrequency(500);
  31. mcastService.setDropTime(3000);
  32. channel.setMembershipService(mcastService);
  33. //receiver setting
  34. NioReceiver receiver = new NioReceiver();
  35. receiver.setAddress("auto");
  36. receiver.setMaxThreads(6);
  37. receiver.setPort(5000);
  38. channel.setChannelReceiver(receiver);
  39. //sender setting
  40. ReplicationTransmitter sender = new ReplicationTransmitter();
  41. sender.setTransport(new PooledParallelSender());
  42. channel.setChannelSender(sender);
  43. //interceptor setting
  44. channel.addInterceptor(new TcpPingInterceptor());
  45. channel.addInterceptor(new TcpFailureDetector());
  46. channel.addInterceptor(new MessageDispatchInterceptor());
  47. cluster.addValve(new ReplicationValve());
  48. cluster.addValve(new JvmRouteBinderValve());
  49. cluster.setChannel(channel);
  50. cluster.addClusterListener(new ClusterSessionListener());
  51. engine.setCluster(cluster);
  52. }
  53. }


테스트 방법은, was1의 application에서 호출되었던 JSESSIONID를 확인하고 was1를 재기동 한 상태에서 was2를 호출합니다.

두개의 JSESSIONID가 동일하다면 잘 적용된 것으로 테스트를 마무리하면 됩니다.


session을 꼭 사용해야 하는 경우 이 방법을 사용할 수 있고,

jwt, redis 등 세션 클러스터링 방법은 다양하게 있습니다.

* 파트너스 활동을 통해 일정액의 수수료를 제공받을 수 있음
작성자 소개
초이 프로필
WrapUp 블로거

초이

반려견을 좋아하고, 차를 좋아하고, 여행을 좋아하고, 맛집을 찾아 즐기는 웹 개발자 입니다^^