Tomcat Session Clustering 구성
안녕하세요:)
오늘은 톰캣 세션 클러스터링에 대해 블로깅을 해보려 합니다.
- Tomcat Cluster란?
– 두 개 이상의 톰캣 WAS 인스턴스가 사용자의 세션 정보를 서로 공유하여 동기화하는 기술을 말합니다.
이 기술을 사용해서 클라이언트에게 끊김 없는 서비스를 제공하고, 시스템의 안정성을 확보 할 수 있습니다.
예를들어,
톰캣 서버 1번과 2번이 있다고 했을 때
사용자 A가 톰캣 1번 서버에 로그인하고 세션 정보(로그인 상태)를 저장했습니다.
만약 갑자기 톰캣 1번 서버에 장애가 발생하여 다운될 시 로드 밸런서는 사용자 A의 다음 요청을 톰캣 2번 서버로 보냅니다.
그러나 톰캣 2번 서버에는 사용자 A의 세션 정보가 없기 때문에, 사용자 A는 로그아웃되거나 장바구니 내용이 사라지는 등의 불편을 겪게 됩니다.
이 때문에 세션 클러스터링을 사용하면
사용자 A가 톰캣 1번 서버에 로그인 했을 때 세션 정보가 톰캣 2번 서버에도 실시간으로 복사되어 저장됩니다.
그리고 톰캣 1번 서버에 장애가 발생해도, 톰캣 2번 서버는 이미 사용자 A의 세션 정보를 가지고 있기 때문에 서비스는 끊김 없이 계속 이어집니다.

- 톰캣 세션 클러스터링의 분류
톰캣 세션 클러스터링을 다음과 같이 분류 할 수 있습니다.
1) 세션 저장 및 공유 아키텍처
① 세션 복제 (Replication) : 세션 데이터를 각 톰캣 서버의 메모리에 저장하고, 변경 사항만 다른 서버로 복사(동기화)합니다. 소규모 클러스터 또는 톰캣 자체 기능만을 사용할 경우 적합합니다.
② 세션 공유 (Session Sharing) : 세션 데이터를 Redis, Memcached 등 외부 중앙 저장소에 저장하고, 모든 톰캣 서버가 이 중앙 저장소에서 읽고 씁니다. 대규모 클러스터, 클라우드 환경, 높은 확장성이 필요할 경우 적합합니다

③ Sticky Session : 세션 복제/공유는 아니며, 로드 밸런서가 특정 클라이언트의 요청을 항상 특정 WAS 서버로만 보내도록 고정하는 트래픽 분산 기술입니다. 모든 클러스터 환경에서 기본적으로 권장됩니다.
위에서 세션을 저장하는 아키텍처가 Replication으로 정해졌다면 이 아키텍처를 어떻게 실행할지 구체적인 수단이 바로 매니저들입니다.
2) 세션 복제 (Replication 선택 시)
① DeltaManager : 전체 복제 (All-to-All): 변경분을 클러스터 모든 노드에 복제합니다.
노드 수 증가에 따라 복제 트래픽이 기하급수적으로 증가합니다. (작은 클러스터 적합)

② BackupManager : 단일 백업 (Primary-Backup): 변경분을 단 하나의 백업 노드에만 복제합니다. Primary/Backup 노드 모두 다운 시 세션이 유실될 수 있습니다. (대규모 클러스터 적합)

3) 클러스터 멤버십
① 멀티캐스트
톰캣 인스턴스(개인 라디오)가 클러스터(방송국)에 참여할 때, 정해진 라디오 주파수(
멀티캐스트 주소 228.0.x.x:45564)를 켜기만 하면 됩니다.
장점으로 인스턴스가 늘어나도 따로 명단을 고칠 필요 없이, 주파수만 맞추면 그룹에 합류됩니다.
방송국에서 새로운 증정품(WAR 파일)이 생겼다고 발표하면, 그룹의 모든 멤버에게 자동으로 배포되는 기능까지 포함하고 있습니다.
그러나 라디오 주파수(멀티캐스트)가 잘 안 터지는 지역(특정 네트워크 환경이나 클라우드)에서는 통신이 불안정해질 수 있습니다.
② 유니캐스트
클러스터(방송국)에 참여해서 방송을 들으려면, 사전에 모든 멤버의 연락처(IP와 Port)가 적힌 명단을 가지고 있어야 합니다.
새로운 서버를 추가할 때마다 모든 서버의 명단에 그 톰캣 인스턴스의 주소(192.168.0.xx:3200)를 직접 수기로 적어줘야 합니다
다중 방송(멀티캐스트)이 아닌 , 명단에 적힌 서버에게만 1:1로 직접 통신(유니캐스트)하여 세션 정보를 복사합니다.

세션 복제와 클러스터 맴버쉽의 역할이 헷갈릴 수 있는데요,
비유를 해서 설명하자면
회사에서 회의를 하는 상황을 가정해 봤을때.
세션 복제는 톰캣 Instance(회의 참석자)들이 세션 데이터(회의 내용)를 어떻게 주고받을지 (DeltaManager로 모두에게 복사할지, BackupManager로 한 명에게만 백업할지) 결정합니다.
클러스터 멤버십은 톰캣 Instance(회의 참석자)들이 누가 그룹에 있고 (명단), 각자 어떻게 서로에게 전달할지 (멀티캐스트로 외칠지, 정적 주소로 1:1로 대화 할지) 결정합니다.
그중에서도 클라우드 환경에서 사용이 적합한 DeltaManager를 이용한 유니케스트 세션클러스터링을 구현해 볼 것입니다.
첫번째로
필요한건 2개의 노드에 톰캣 장비 하나씩 구성합니다.
1) 각 노드에 톰캣구성
tomcat1
tomcat2
2) server.xml 에 톰캣 세션 클러스터링 설정 추가
$ vi [TOMCAT_HOME]/conf/server.xml

3) tomcat2에도 tomcat1과 같이 설정 추가 (Sender IP 와 StaticMember IP 를 변경해 주어야 함)

4) 세션 클러스터링 작동확인
- 재기동 시 맴버의 톰캣 로그를 확인합니다.
tomcat1 기동
tomcat2 로그에 클러스터 맴버가 추가되었다는 로그가 출력 되는것을 확인합니다.
위와같이 tomcat2를 재기동했을때 tomcat1의 로그도 확인합니다.
다음으로 Tomcat 클러스터링 설정문 요소별로 설명 드리겠습니다.
<Cluster channelSendOptions="8"
channelStartOptions="3"
className="org.apache.catalina.ha.tcp.SimpleTcpCluster">
- className=”org.apache.catalina.ha.tcp.SimpleTcpCluster”
– 클러스터 엔진 정의 : TCP 기반(SimpleTcpCluster) 클러스터 사용합니다. - channelSendOptions=”8″
– 세션 복제 시 ACK 기반 안정적 전송합니다.
| 상수명 | 값 | 의미 |
|---|---|---|
| Channel.SEND_OPTIONS_DEFAULT | 0 | 기본 (동기 전송, 오류 발생 시 예외 발생) |
| Channel.SEND_OPTIONS_ASYNCHRONOUS | 2 | 비동기 전송 — 요청 스레드를 블로킹하지 않음 |
| Channel.SEND_OPTIONS_SYNCHRONIZED_ACK | 4 | 동기 전송 + ACK 대기 — 응답(acknowledge)을 기다림 |
| Channel.SEND_OPTIONS_USE_ACK | 8 | ACK(응답) 사용 — 수신 측에서 패킷 수신 확인을 요구함 |
- channelStartOptions=”3″
| 상수 | 값 | 설명 |
|---|---|---|
| START_NONE | 0 | 아무 것도 시작하지 않음 |
| START_MEMBERSHIP | 1 | Membership (노드 탐색) 시작 |
| START_PING | 2 | Ping 인터셉터 시작 |
| START_ALL | 3 | Membership + Ping 둘 다 시작 |
<Manager
className="org.apache.catalina.ha.session.DeltaManager"
expireSessionsOnShutdown="false"
notifyListenersOnReplication="true"
enableStatistics="false"
/>
- className=”org.apache.catalina.ha.session.DeltaManager”
– 변경부분을 클러스터 모든 노드에 복제합니다.
- expireSessionsOnShutdown=”false”
– 톰캣이 종료될 때 세션을 만료시키지 않습니다.
- notifyListenersOnReplication=”true”
– 세션 복제 시 HttpSessionListener 이벤트도 같이 호출.
– HttpSessionListener는 톰캣이 세션의 생성과 종료를 감지해서 알려주는 리스너입니다.
- enableStatistics=”false”
– 통계 수집 비활성화
<Channel className="org.apache.catalina.tribes.group.GroupChannel">
– GroupChannel란 클러스터 내 통신을 담당하는 핵심 객체입니다.
– 내부적으로 Sender / Receiver / Interceptor 등을 연결합니다.
<Sender className="org.apache.catalina.tribes.transport.ReplicationTransmitter">
<Transport className="org.apache.catalina.tribes.transport.nio.PooledParallelSender" />
</Sender>
- className=”org.apache.catalina.tribes.transport.ReplicationTransmitter”
– 세션 복제나 클러스터 메시지를 실제로 네트워크로 보내는 전송 관리자입니다.
– 세션 정보를 클러스터링 된 맴버 톰캣으로 보내는 일을 하는 전달자 역할입니다.
- Transport className=”org.apache.catalina.tribes.transport.nio.PooledParallelSender”
– Tomcat이 제공하는 고성능 NIO(Non-blocking I/O) 기반 송신기 입니다.
– 클러스터의 여러 톰캣 인스턴스 서버들에게 세션 데이터를 비동기 + 병렬 + 재사용 연결 방식으로 빠르게 보내주는 송신 방식입니다.
<Receiver
address="192.168.xx.28"
autoBind="0"
className="org.apache.catalina.tribes.transport.nio.NioReceiver"
maxThreads="6"
port="3200"
selectorTimeout="5000"
/>
- address=”192.168.xx.28″
– 현재 톰캣 인스턴스의 IP
- port=”3200″
– 클러스터 통신용 포트 (다른 노드와 중복 금지)
- autoBind=”0″
– Tomcat이 지정한 포트에서 실패하면 자동으로 다른 포트를 찾아 바인드할지의 동작 범위지정
범위가 0이므로 딱 지정한 port(여기서는 3200) 만 시도하고 다른 포트로 자동 이동하지 않습니다.
- maxThreads=”6″
– 수신 처리용 스레드 수
- selectorTimeout=”500”
– NIO selector 타임아웃(ms)
<Interceptor
className="org.apache.catalina.tribes.group.interceptors.TcpPingInterceptor" staticOnly="true"/>
– 클러스터 멤버 상태를 주기적으로 Ping하여 확인.
– staticOnly=”true”이면 StaticMember만 대상으로 Ping 수행.
<Interceptor
className="org.apache.catalina.tribes.group.interceptors.TcpFailureDetector" />
– 통신 실패(응답 없음) 시 해당 멤버를 클러스터에서 자동 제거.
<Interceptor
className="org.apache.catalina.tribes.group.interceptors.StaticMembershipInterceptor">
<Member className="org.apache.catalina.tribes.membership.StaticMember"
port="3100"
host="192.168.xx.27"
uniqueId="{0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,2}" />
</Interceptor>
- Interceptor
className=”org.apache.catalina.tribes.group.interceptors.StaticMembershipInterceptor”
– 클러스터 멤버를 수동(static)으로 등록하는 역할을 합니다.
- port=”3100″
– 상대 톰캣 인스턴스 클러스터의 Receiver가 열고 있는 포트
- host=”192.168.xx.27″
– 상대 톰캣 인스턴 서버의 IP 주소
- uniqueId=”{0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,2}”
– 클러스터 내에서 각 톰캣 인스턴스를 구분하기 위한 16바이트 식별자(ID)입니다.
– Tomcat이 내부적으로 바이트 배열로 멤버를 구분할 때 사용합니다.
– 반드시 각 서버마다 달라야 합니다.
<Interceptor
className="org.apache.catalina.tribes.group.interceptors.MessageDispatchInterceptor" />
- 클러스터 메시지를 개별 스레드로 분리(dispatch) 하여 처리 속도를 높여주는 비동기 메시지 디스패처(Dispatcher) 역할을 합니다.
<Valve
className="org.apache.catalina.ha.tcp.ReplicationValve"
filter=".*.gif;.*.js;.*.jpg;.*.png;.*.htm;.*.html;.*.css;.*.txt;"
/>
- className=”org.apache.catalina.ha.tcp.ReplicationValve”
Tomcat 클러스터링 기능 중 세션 복제(Replication)를 담당합니다.
- filter=”.*.gif;.*.js;.*.jpg;.*.png;.*.htm;.*.html;.*.css;.*.txt;”
지정된 패턴과 일치하는 요청은 세션 복제를 수행하지 않습니다.
<ClusterListener
className="org.apache.catalina.ha.session.ClusterSessionListener" />
- 클러스터 세션 이벤트를 모니터링. 디버깅용 로그를 기록합니다.
<Valve className="org.apache.catalina.ha.session.JvmRouteBinderValve"/>
– 클러스터 환경에서 로드밸런서와 톰캣 세션을 올바르게 연결(bind) 시켜주는 역할을 합니다.
– Tomcat의 server.xml에서 설정합니다.
<Engine name=”Catalina” defaultHost=”localhost” jvmRoute=”tomcat1″>
위의 내용은 톰캣 클러스터링 설정을 이해하는데 참고하면 좋을것 같습니다.
========================================================================================================
| <Cluster channelSendOptions=”8″
channelStartOptions=”3″ className=”org.apache.catalina.ha.tcp.SimpleTcpCluster”> <Manager className=”org.apache.catalina.ha.session.DeltaManager” expireSessionsOnShutdown=”false” notifyListenersOnReplication=”true” enableStatistics=”false” /> <Channel className=”org.apache.catalina.tribes.group.GroupChannel”> <Sender className=”org.apache.catalina.tribes.transport.ReplicationTransmitter”> <Transport className=”org.apache.catalina.tribes.transport.nio.PooledParallelSender” /> </Sender> <Receiver address=”192.168.0.27″ autoBind=”0″ className=”org.apache.catalina.tribes.transport.nio.NioReceiver” maxThreads=”6″ port=”3100″ selectorTimeout=”5000″ /> <!– server1 information –> <!– <Interceptor className=”com.dm.tomcat.interceptor.DisableMulticastInterceptor” /> –> <Interceptor className=”org.apache.catalina.tribes.group.interceptors.TcpPingInterceptor” staticOnly=”true”/> <Interceptor className=”org.apache.catalina.tribes.group.interceptors.TcpFailureDetector” /> <Interceptor className=”org.apache.catalina.tribes.group.interceptors.StaticMembershipInterceptor”> <Member className=”org.apache.catalina.tribes.membership.StaticMember” port=”3200″ host=”192.168.xx.28″ uniqueId=”{0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,1}” /> </Interceptor> <Interceptor className=”org.apache.catalina.tribes.group.interceptors.MessageDispatchInterceptor” /> </Channel> <Valve className=”org.apache.catalina.ha.tcp.ReplicationValve” filter=”.*.gif;.*.js;.*.jpg;.*.png;.*.htm;.*.html;.*.css;.*.txt;” /> <ClusterListener className=”org.apache.catalina.ha.session.ClusterSessionListener” /> <Valve className=”org.apache.catalina.ha.session.JvmRouteBinderValve”/> </Cluster> |
=========================================================================================================
기존의 온프레미스 환경에서는 멀티캐스트 기반의 Tomcat 세션 클러스터링이 잘 동작 되었습니다.
그리고 다수의 톰캣 인스턴스 서버 클러스터링 시에 멀티캐스트 방식이 더 편리하기 때문에 멀티캐스트 방식을 더 선호한다고 알고 있습니다.
그러나
AWS, Azure, GCP 같은 대부분의 클라우드 환경에서는 멀티캐스트 트래픽을 지원하지 않습니다.
각각 지원하는 통신 네트워크 계층이 다르기 때문입니다.
점점 클라우드 환경에서의 서버운영 및 어플리케이션 배포가 늘어나고 있습니다.
클라우드 환경에서 Tomcat 세션 클러스터링이 필요할 때 , 블로깅한 내용을 참고하여 설정 해보면 좋을 것 같습니다.
그럼 이상으로 , 글을 마치겠습니다.
봐주셔서 감사합니다:)
자유롭게 댓글을 달아주세요! 언제나 환영합니다.
기타 문의: info@neoclova.co.kr
네오클로바 기술블로그 홈 바로가기: https://neoclova.net
네오클로바 홈페이지: http://neoclova.co.kr
