본문 바로가기

Backend Development/Spring boot

[Spring boot] Embedded Tomcat redis session clustering

서버 개발을 할때 여러 was에서 세션 정보를 공유해야할 경우가 있다. 이럴때 세션 저장소로 redis cache 를 많이 활용하게 되는데 Spring boot 내장  embedded tomcat에서는 어떻게 설정을 하면 되는지 확인해 본다.

 

Tomcat cluster redis session 라이브러리는 아래 사이트에 가면 다운로드 받을 수 있다.

 

https://github.com/ran-jit/tomcat-cluster-redis-session-manager/wiki

여러 버전이 있는데 제일 최근 버전인 4.0 버전을 사용해 보기로 한다.

 

다운로드 library maven project에 추가하기

위에서 다운로드 압축파일을 풀면 아래 파일들이 나온다.

 

commons-pool2-2.6.2.jar

jedis-3.0.1.jar

tomcat-cluster-redis-session-manager-4.0.jar

redis-data-cache.properties

 

jar 파일들은 mvn 패키지 빌드시에 포함되어야 하므로 원하는 폴더에 라이브러리를 넣고 아래와 같이 local repository를 라이브러리 path로 잡는다. (아래 예시에서 library local 위치는  ${basedir}/src/main/webapp/WEB-INF/lib)

 

pom.xml

<dependency>
    <groupId>jedis</groupId>
    <artifactId>jedis</artifactId>
    <version>3.0.1</version>
    <scope>system</scope>
    <systemPath>${basedir}/src/main/webapp/WEB-INF/lib/jedis-3.0.1.jar</systemPath>
</dependency>

<dependency>
    <groupId>omcat-cluster-redis-session-manager</groupId>
    <artifactId>omcat-cluster-redis-session-manager</artifactId>
    <version>4.0.0</version>
    <scope>system</scope>
    <systemPath>${basedir}/src/main/webapp/WEB-INF/lib/tomcat-cluster-redis-session-manager-4.0.jar</systemPath>
</dependency>

<dependency>
    <groupId>commons-pool</groupId>
    <artifactId>commons-pool</artifactId>
    <version>2.6.2</version>
    <scope>system</scope>
    <systemPath>${basedir}/src/main/webapp/WEB-INF/lib/commons-pool2-2.6.2.jar</systemPath>
</dependency>

 

Tomcat session clustering 설정 생성

 

Spring boot에서 위에서 사용할 redis session manager를 구동시키기 위해서는 Configuration Bean을 등록해야 한다.

아래와 같이 테스트용 confi를 작성해 본다.

package com.sdp.common.config;

import org.apache.catalina.Context;
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;
import tomcat.request.session.redis.SessionHandlerValve;
import tomcat.request.session.redis.SessionManager;

@Configuration
public class TomcatClusterConfig implements WebServerFactoryCustomizer<TomcatServletWebServerFactory> {

    @Override
    public void customize(TomcatServletWebServerFactory factory) {
        factory.addContextValves(new SessionHandlerValve());
        factory.addContextCustomizers(new TomcatContextCustomizer() {
            @Override
            public void customize(Context context) {
                context.setSessionTimeout(30);
                context.setSessionCookieName("testSession");
                context.setManager(new SessionManager());
            }
        });
    }
}

 

Embedded tomcat basedir 지정

 

다운로드 받은 라이브러리에는 redis-data-cache.properties 파일이 들어있고 여기에는 세션 저장을 위한 redis cache server 정보를 입력하게 된다. Embedded Tomcat에서 아래 설정 파일을 읽어와야 하므로 tomcat basedir를 지정하고{tomcat_base}/conf  폴더에 redis-data-cache.properties를 복사해 둔다.

 

#-- Redis data-cache configuration

# - ${ENV_VARIABLE_NAME}

#- redis hosts. ex: 127.0.0.1:6379, 127.0.0.2:6379, 127.0.0.2:6380, ....
redis.hosts=127.0.0.1:6379

#- redis password.
#redis.password=

#- set true to enable redis cluster mode. (default value: false)
redis.cluster.enabled=false

#- set true to enable redis sentinel mode. (default value: false)
redis.sentinel.enabled=false
# redis sentinel master name. (default value: mymaster)
redis.sentinel.master=mymaster

#- redis database. (default value: 0)
#redis.database=0

#- redis connection timeout. (default value: 2000 ms)
#redis.timeout=2000

#- enable redis and standard session mode. (default value: false)
# If enabled,
#   1. Must be enabled sticky session in your load balancer configuration. Else this manager may not return the updated session values.
#   2. Session values are stored in local jvm and redis.
#   3. If redis is down/not responding, requests uses jvm stored session values to process user requests. Redis comes back the values will be synced.
lb.sticky-session.enabled=false

#- session persistent policies. (default value: DEFAULT) ex: DEFAULT, SAVE_ON_CHANGE
# policies - DEFAULT, SAVE_ON_CHANGE, ALWAYS_SAVE_AFTER_REQUEST
#   1. SAVE_ON_CHANGE: every time session.setAttribute() or session.removeAttribute() is called the session will be saved.
#   2. ALWAYS_SAVE_AFTER_REQUEST: force saving after every request, regardless of whether or not the manager has detected changes to the session.
session.persistent.policies=DEFAULT

#- single-sign-on session timeout. (default value: 0 ms (-infinite))
redis.sso.timeout=0

 

테스트로  tomcat basedir은 .으로 지정하였다.

 

application.properties

server.addr=xxx
server.port=8080
server.tomcat.basedir= .

 

세션 공유 테스트 하기

 

테스트를 위해 세션에다 데이터를 쓰고 읽는 간단한 Rest api를 만들어보고 세션 공유 테스트를 한다.

@ApiOperation(httpMethod = "GET", value = "공개키 생성 - Get public key value of application.properties", produces = "application/json", consumes = "application/json")
@RequestMapping(value = "/api/v1/public-key", method = RequestMethod.GET)
public ResponseEntity<RSAKeyResult> cratePublicKey(HttpServletRequest req) throws Exception {
    RSAKey rsaKey = RSAHelper.generateRSAKeyAndStoreInSession();
    HttpSession session = req.getSession(true);
    session.setAttribute("rsaKey", rsaKey);
    RSAKeyResult result = new RSAKeyResult();
    result.setPublicKeyExponent(rsaKey.getPublicKeyExponent());
    result.setPublicKeyModulus(rsaKey.getPublicKeyModulus());

    return ResponseEntity.ok(result);
}

@ApiOperation(httpMethod = "GET", value = "공개키 조회 - Get public key value of application.properties", produces = "application/json", consumes = "application/json")
@RequestMapping(value = "/api/v1/public-key/read", method = RequestMethod.GET)
public ResponseEntity<RSAKeyResult> getPublicKey(HttpServletRequest req) throws Exception {
    HttpSession session = req.getSession();
    RSAKey rsaKey = (RSAKey) session.getAttribute("rsaKey");
    RSAKeyResult result = new RSAKeyResult();
    result.setPublicKeyExponent(rsaKey.getPublicKeyExponent());
    result.setPublicKeyModulus(rsaKey.getPublicKeyModulus());

    return ResponseEntity.ok(result);
}

 

 Spring boot application 을 포트만 달리해서 2개로 빌드한다. 그리고 각 서버를 실행한다. swagger-ui 를 실행한후 위 rest api를 실행해본다.

 

우선 localhost:8080의 swagger-ui로 들어가서 세션에 데이터를 생성한다. 아래와 같이 api가 정상응답을 주었고 세션에 데이터가 생성 되었을 것이다.

 

세션 클러스터링을 하고 있는 다른 서버 (localhost:8081)를 같은 브라우저의 다른 탭으로 실행 (이때 기 생성된 세션을 넘겨준다.) 하고 테스트 데이터 읽기 rest api를 수행해 본다.

 

위 결과에서 볼수 있듯이 redis에 세션이 저장되어 있기 때문에 세션 공유를 받은 다른 서버에서 바로 세션 데이터를 읽어보면 앞에서 다른 서버에서 설정한 값을 읽어 올수 있게 된다.

 

-- The end --