Spring Session Redis Cluster

레디스 개발자 교육 신청 레디스 정기점검/기술지원
Redis Technical Support
레디스 엔터프라이즈 서버
Redis Enterprise Server

Java Program Project Configuration

구성 소프트웨어 버전

Spring Boot: 3.1.6
Spring: 6.0.14
Srping-session-data-redis:3.1.3
Lettuce: 6.2.7
Redis: 7.0.10
Java: 17

Project 생성: start.spring.io

build.gradle

dependencies에 spring-session-data-redis 가 있는지 확인한다.

dependencies {
    implementation 'org.springframework.boot:spring-boot-starter-data-redis'
    implementation 'org.springframework.boot:spring-boot-starter-web'
    implementation 'org.springframework.session:spring-session-data-redis'
    compileOnly 'org.projectlombok:lombok'
    annotationProcessor 'org.projectlombok:lombok'
    testImplementation 'org.springframework.boot:spring-boot-starter-test'
}

application.properties

Standalone 설정에서 추가된 항목에서 중요한 것은 replicas.host, port와 readFrom이 추가되었다.
readFrom은 조회 명령을 복제(replica) 서버에서 실행하게 해서 부하분산을 하는 기능입니다.
Lettuce 설정에 있습니다. 여기서는 replicaPreferred으로 설정했습니다.
자세한 내용은 여기(readFrom)를 보세요.

참고

설정에서 spring.session.store-type:redis 이나 @EnableRedisHttpSession이 필요하다는 글도 있으나 현재 테스트 버전에서는 필요하지 않았다.


Java Program

Main Class: SpringSessionRedisClusterApplication

Class RedisClusterInfo

application.properties 설정 값을 읽어오는 클래스

Class RedisClusterConfig

master.host, port, replicas.host, port, readFrom 등의 정보로 redisConnectionFactory를 만든다.
application.properties에 readFrom이 없을 경우 ANY로 설정하게 했다.

Class SessionController

  • hello1(): session.setAttribute("hello1","Charlie");
  • hello2(): session.setAttribute(); 2회 실행
  • hello3(): ① 속성명 지정해서 값 가져오기. ② 모든 속성명, 값 가져오기. ③ Session ID, 생성일시, 마지막 액세스(접근) 일시 가져오기
  • hello4(): Redis HGETALL 명령으로 세션 데이터 가져오기. 메서드 명은 redisHash.entries()


Redis Cluster 구성도

    cluster config


Redis Server에서 실행되는 명령과 클라이언트 로그

Redis Server에서 실행되는 명령 확인은 redis-cli의 monitor 기능을 사용했습니다.
방법: redis-cli -h ip -p port -a password monitor

http://localhost:8080/hello1

hello1() source

레디스 서버에서 실행되는 명령

처음 실행: 새 브라우저에서 처음 실행할 때

Master1 (마스터1)

  • 총 4 개 명령 실행: 레디스 서버 접속 관련 명령 4개 실행
  • 세션 관련 명령은 마스터3에서 실행되었다.
1701545234.433648 [Client:7438] "HELLO" "3" "AUTH" "(redacted)" "(redacted)" "SETNAME" "redisgate"
1701545234.510113 [Client:7438] "CLIENT" "SETNAME" "lettuce#ClusterTopologyRefresh"
1701545234.518091 [Client:7438] "CLUSTER" "NODES"
1701545234.522056 [Client:7438] "INFO"

Master2 (마스터2)

  • 총 6 개 명령 실행: 레디스 서버 접속 관련 명령 6개 실행
1701545234.433343 [Client:7435] "HELLO" "3" "AUTH" "(redacted)" "(redacted)" "SETNAME" "redisgate"
1701545234.509916 [Client:7435] "CLIENT" "SETNAME" "lettuce#ClusterTopologyRefresh"
1701545234.521738 [Client:7435] "CLUSTER" "NODES"
1701545234.525619 [Client:7435] "INFO"
1701545234.736944 [Client:7442] "HELLO" "3" "AUTH" "(redacted)" "(redacted)" "SETNAME" "redisgate"
1701545234.750749 [Client:7442] "CLUSTER" "MYID"

Master3 (마스터3)

  • 총 7 개 명령 실행: 레디스 서버 접속 관련 명령 5개 실행, Spring Session 명령 2개 실행.
  • 세선 키가 Master3에 할당되었다.
  • HMSET 명령은 4개 필드 lastAccessedTime, maxInactiveInterval, creationTime, sessionAttr를 저장한다.
  • 가능한 한 줄에 보이게 하려고 'spring:session:sessions:c6c7eb8c-ddbe-4ce3-a9f4-65323791222e' 에서 'spring:session:sessions:' 부분을 삭제했다.
1701545234.433676 [Client:7439] "HELLO" "3" "AUTH" "(redacted)" "(redacted)" "SETNAME" "redisgate"
1701545234.510245 [Client:7439] "CLIENT" "SETNAME" "lettuce#ClusterTopologyRefresh"
1701545234.522136 [Client:7439] "CLUSTER" "NODES"
1701545234.526024 [Client:7439] "INFO"
1701545234.821145 [Client:7444] "HELLO" "3" "AUTH" "(redacted)" "(redacted)" "SETNAME" "redisgate"
1701545235.000680 [Client:7444] "HMSET" "c6c7eb8c-ddbe-4ce3-a9f4-65323791222e"
    "lastAccessedTime" "\xac\xed\x00\x05sr\x00\x0ejava.lang.Long;"
    "maxInactiveInterval" "\xac\xed\x00\x05sr\x00\x11java.lang.Integer"
    "creationTime" "\xac\xed\x00\x05sr\x00\x0ejava.lang.Long;"
    "sessionAttr:hello1" "\xac\xed\x00\x05t\x00\aCharlie"
1701545235.027756 [Client:7444] "PEXPIREAT" "c6c7eb8c-ddbe-4ce3-a9f4-65323791222e" "1701547034787"

Replica1 (복제1)

  • 총 11 개 명령 실행: 레디스 서버 접속 관련 명령 6개 실행, 조회 명령 3개, 마스터에서 전파된 Spring Session 명령 2개 실행.
  • Client에서 실행된 명령 3개는 조회 명령은 복제서버에서 실행되도록 readFrom을 설정했기 때문이다.
1701545234.433300 [Client:7437] "HELLO" "3" "AUTH" "(redacted)" "(redacted)" "SETNAME" "redisgate"
1701545234.509846 [Client:7437] "CLIENT" "SETNAME" "lettuce#ClusterTopologyRefresh"
1701545234.521756 [Client:7437] "CLUSTER" "NODES"
1701545234.525600 [Client:7437] "INFO"
1701545234.821181 [Client:7443] "HELLO" "3" "AUTH" "(redacted)" "(redacted)" "SETNAME" "redisgate"
1701545234.836141 [Client:7443] "READONLY"
1701545234.848561 [Client:7443] "HGETALL" "e4a29219-8bf2-4d26-9777-52863fd41f7d"
1701545235.000908 [Master:18532] "HMSET" "c6c7eb8c-ddbe-4ce3-a9f4-65323791222e"
    "lastAccessedTime" "\xac\xed\x00\x05sr\x00\x0ejava.lang.Long;"
    "maxInactiveInterval" "\xac\xed\x00\x05sr\x00\x11java.lang.Integer"
    "creationTime" "\xac\xed\x00\x05sr\x00\x0ejava.lang.Long;"
    "sessionAttr:hello1" "\xac\xed\x00\x05t\x00\aCharlie"
1701545235.027867 [Master:18532] "PEXPIREAT" "c6c7eb8c-ddbe-4ce3-a9f4-65323791222e" "1701547034787"
1701545235.038734 [Client:7443] "HGETALL" "e4a29219-8bf2-4d26-9777-52863fd41f7d"
1701545235.064990 [Client:7443] "EXISTS" "c6c7eb8c-ddbe-4ce3-a9f4-65323791222e"

Replica2 (복제2)

  • 레디스 서버 접속 관련 명령 4개 실행
1701545234.433437 [Client:7436] "HELLO" "3" "AUTH" "(redacted)" "(redacted)" "SETNAME" "redisgate"
1701545234.509968 [Client:7436] "CLIENT" "SETNAME" "lettuce#ClusterTopologyRefresh"
1701545234.521914 [Client:7436] "CLUSTER" "NODES"
1701545234.525757 [Client:7436] "INFO"

Replica3 (복제3)

  • 레디스 서버 접속 관련 명령 4개 실행
1701545234.433760 [Client:7440] "HELLO" "3" "AUTH" "(redacted)" "(redacted)" "SETNAME" "redisgate"
1701545234.510247 [Client:7440] "CLIENT" "SETNAME" "lettuce#ClusterTopologyRefresh"
1701545234.522165 [Client:7440] "CLUSTER" "NODES"
1701545234.526026 [Client:7440] "INFO"

두 번째 실행

Master (마스터)

No Data

Master2 (마스터2)

No Data

Master3 (마스터3)

  • 세선 키가 Master3에 할당되었다.
1701545399.167362 [Client:7444] "HMSET" "c6c7eb8c-ddbe-4ce3-a9f4-65323791222e"
    "lastAccessedTime" "\xac\xed\x00\x05sr\x00\x0ejava.lang.Long;"
    "sessionAttr:hello1" "\xac\xed\x00\x05t\x00\aCharlie"
1701545399.174801 [Client:7444] "PEXPIREAT" "c6c7eb8c-ddbe-4ce3-a9f4-65323791222e" "1701547199006"

Replica1 (복제1)

  • 조회 명령 4개와 마스터에서 전파된 2개 명령이 실행되었다.
1701545399.137927 [Client:7443] "HGETALL" "c6c7eb8c-ddbe-4ce3-a9f4-65323791222e"
1701545399.154885 [Client:7443] "EXISTS" "c6c7eb8c-ddbe-4ce3-a9f4-65323791222e"
1701545399.167507 [Master:18532] "HMSET" "c6c7eb8c-ddbe-4ce3-a9f4-65323791222e"
    "lastAccessedTime" "\xac\xed\x00\x05sr\x00\x0ejava.lang.Long;"
    "sessionAttr:hello1" "\xac\xed\x00\x05t\x00\aCharlie"
1701545399.174887 [Master:18532] "PEXPIREAT" "c6c7eb8c-ddbe-4ce3-a9f4-65323791222e" "1701547199006"
1701545399.188864 [Client:7443] "HGETALL" "c6c7eb8c-ddbe-4ce3-a9f4-65323791222e"
1701545399.205779 [Client:7443] "EXISTS" "c6c7eb8c-ddbe-4ce3-a9f4-65323791222e"

Replica2 (복제2)

No Data

Replica3 (복제3)

No Data

명령이 마스터와 복제에서 분산 실행되므로 문제가 발생할까?

마스터3과 복제1에서 분산되어서 실행되는 명령을 시간 순으로 살펴보자.

  1. 137ms: 복제1 HGETALL 명령 실행
  2. 154ms: 복제1 EXISTS 명령 실행, 17ms 차이
  3. 167ms: 마스터3 HMSET 명령 실행, 13ms 차이
  4. 167ms: 복제1 HMSET 명령 실행, 0ms 차이
  5. 174ms: 마스터3 PEXPIREAT 명령 실행, 7ms 차이
  6. 174ms: 복제1 PEXPIREAT 명령 실행, 0ms 차이
  7. 188ms: 복제1 HGETALL 명령 실행, 14ms 차이
  8. 205ms: 복제1 EXISTS 명령 실행, 17ms 차이

명령 사이의 시간 간격은 약 10ms이고, 마스터에서 복제서버로 전파되는 시간은 1ms 이내이다.
그러므로 일련의 명령이 복제서버와 마스터에서 나누어서(분산) 실행되어도 순서가 바뀌어서 문제가 될 확률은 거의 없다.
(이 측정치는 부하(load) 정도, 서버나 네트워크 상황에 따라서 다를 수 있습니다)


http://localhost:8080/hello2

hello2() source

레디스 서버에서 실행되는 명령

Master1 (마스터1)

No Data

Master2 (마스터2)

No Data

Master3 (마스터3)

  • 마스터에서는 2개 명령이 실행된다. 마스터 서버의 부담이 줄었다.
  • setAttribute()로 인해서 HMSET 명령에 sessionAttr:hello2-2, sessionAttr:hello2-1 필드와 값이 추가되었다.
1701545499.175272 [Client:7444] "HMSET" "c6c7eb8c-ddbe-4ce3-a9f4-65323791222e"
    "lastAccessedTime" "\xac\xed\x00\x05sr\x00\x0ejava.lang.Long;"
    "sessionAttr:hello2-2" "\xac\xed\x00\x05t\x00\x06Kwon-2"
    "sessionAttr:hello2-1" "\xac\xed\x00\x05t\x00\x06Kwon-1"
1701545499.200095 [Client:7444] "PEXPIREAT" "c6c7eb8c-ddbe-4ce3-a9f4-65323791222e" "1701547299014"

Replica1 (복제1)

  • 부하분산을 위한 readFrom 설정 효과로 조회 명령이 복제1에서 실행되었다.
  • 클라이언트로 부터 조회 명령 4개와 마스터에서 전파된 2개 명령이 실행되었다.
1701545499.060739 [Client:7443] "HGETALL" "c6c7eb8c-ddbe-4ce3-a9f4-65323791222e"
1701545499.165486 [Client:7443] "EXISTS" "c6c7eb8c-ddbe-4ce3-a9f4-65323791222e"
1701545499.175451 [Master:18532] "HMSET" "c6c7eb8c-ddbe-4ce3-a9f4-65323791222e"
    "lastAccessedTime" "\xac\xed\x00\x05sr\x00\x0ejava.lang.Long;"
    "sessionAttr:hello2-2" "\xac\xed\x00\x05t\x00\x06Kwon-2"
    "sessionAttr:hello2-1" "\xac\xed\x00\x05t\x00\x06Kwon-1"
1701545499.200206 [Master:18532] "PEXPIREAT" "c6c7eb8c-ddbe-4ce3-a9f4-65323791222e" "1701547299014"
1701545499.218511 [Client:7443] "HGETALL" "c6c7eb8c-ddbe-4ce3-a9f4-65323791222e"
1701545499.234359 [Client:7443] "EXISTS" "c6c7eb8c-ddbe-4ce3-a9f4-65323791222e"

Replica2 (복제2)

No Data

Replica3 (복제3)

No Data

http://localhost:8080/hello3

hello3() source

레디스 서버에서 실행되는 명령

Master1 (마스터1)

No Data

Master2 (마스터2)

No Data

Master3 (마스터3)

  • 마스터에서는 2개 명령이 실행된다. 마스터 서버의 부담이 줄었다.
1701545591.102412 [Client:7444] "HMSET" "c6c7eb8c-ddbe-4ce3-a9f4-65323791222e"
    "lastAccessedTime" "\xac\xed\x00\x05sr\x00\x0ejava.lang.Long;"
1701545591.112114 [Client:7444] "PEXPIREAT" "c6c7eb8c-ddbe-4ce3-a9f4-65323791222e" "1701547390936"

Replica1 (복제1)

  • 부하분산을 위한 readFrom 설정 효과로 조회 명령이 복제1에서 실행되었다.
  • 클라이언트로 부터 조회 명령 4개와 마스터에서 전파된 2개 명령이 실행되었다.
1701545591.067685 [Client:7443] "HGETALL" "c6c7eb8c-ddbe-4ce3-a9f4-65323791222e"
1701545591.089592 [Client:7443] "EXISTS" "c6c7eb8c-ddbe-4ce3-a9f4-65323791222e"
1701545591.102585 [Master:18532] "HMSET" "c6c7eb8c-ddbe-4ce3-a9f4-65323791222e"
    "lastAccessedTime" "\xac\xed\x00\x05sr\x00\x0ejava.lang.Long;"
1701545591.112224 [Master:18532] "PEXPIREAT" "c6c7eb8c-ddbe-4ce3-a9f4-65323791222e" "1701547390936"
1701545591.124533 [Client:7443] "HGETALL" "c6c7eb8c-ddbe-4ce3-a9f4-65323791222e"
1701545591.134004 [Client:7443] "EXISTS" "c6c7eb8c-ddbe-4ce3-a9f4-65323791222e"

Replica2 (복제2)

No Data

Replica3 (복제3)

No Data

http://localhost:8080/hello4

hello4() source

레디스 서버에서 실행되는 명령

Master1 (마스터1)

No Data

Master2 (마스터2)

No Data

Master3 (마스터3)

1701545629.184128 [Client:7444] "HMSET" "c6c7eb8c-ddbe-4ce3-a9f4-65323791222e"
    "lastAccessedTime" "\xac\xed\x00\x05sr\x00\x0ejava.lang.Long;"
1701545629.196038 [Client:7444] "PEXPIREAT" "c6c7eb8c-ddbe-4ce3-a9f4-65323791222e" "1701547428992"

Replica1 (복제1)

  • 부하분산을 위한 readFrom 설정 효과로 조회 명령이 복제2에서 실행되었다.
  • 조회 명령 5개와 마스터에서 전파된 명령 2개가 순서대로 실행되었다.
  • 소스 "Map entries = redisHash.entries(springSessionId);" 의 결과로 두 번째 HGETALL이 실행되었다.
1701545629.126106 [Client:7443] "HGETALL" "c6c7eb8c-ddbe-4ce3-a9f4-65323791222e"
1701545629.160376 [Client:7443] "HGETALL" "c6c7eb8c-ddbe-4ce3-a9f4-65323791222e"
1701545629.175723 [Client:7443] "EXISTS" "c6c7eb8c-ddbe-4ce3-a9f4-65323791222e"
1701545629.184254 [Master:18532] "HMSET" "c6c7eb8c-ddbe-4ce3-a9f4-65323791222e"
    "lastAccessedTime" "\xac\xed\x00\x05sr\x00\x0ejava.lang.Long;"
1701545629.196132 [Master:18532] "PEXPIREAT" "c6c7eb8c-ddbe-4ce3-a9f4-65323791222e" "1701547428992"
1701545629.209443 [Client:7443] "HGETALL" "c6c7eb8c-ddbe-4ce3-a9f4-65323791222e"
1701545629.219376 [Client:7443] "EXISTS" "c6c7eb8c-ddbe-4ce3-a9f4-65323791222e"

Replica2 (복제2)

No Data

Replica3 (복제3)

No Data

<< Session Sentinel Session Cluster >>

Email 답글이 올라오면 이메일로 알려드리겠습니다.