spring_session_redis_cluster
Spring Session Redis Cluster
레디스 개발자 교육 신청 |
레디스 정기점검/기술지원 Redis Technical Support |
레디스 엔터프라이즈 서버 Redis Enterprise Server |
---|
Java Program Project Configuration
구성 소프트웨어 버전
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 가 있는지 확인한다.
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 구성도
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.510113 [Client:7438] "CLIENT" "SETNAME" "lettuce#ClusterTopologyRefresh"
1701545234.518091 [Client:7438] "CLUSTER" "NODES"
1701545234.522056 [Client:7438] "INFO"
Master2 (마스터2)
- 총 6 개 명령 실행: 레디스 서버 접속 관련 명령 6개 실행
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.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.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.509968 [Client:7436] "CLIENT" "SETNAME" "lettuce#ClusterTopologyRefresh"
1701545234.521914 [Client:7436] "CLUSTER" "NODES"
1701545234.525757 [Client:7436] "INFO"
Replica3 (복제3)
- 레디스 서버 접속 관련 명령 4개 실행
1701545234.510247 [Client:7440] "CLIENT" "SETNAME" "lettuce#ClusterTopologyRefresh"
1701545234.522165 [Client:7440] "CLUSTER" "NODES"
1701545234.526026 [Client:7440] "INFO"
두 번째 실행
Master (마스터)
Master2 (마스터2)
Master3 (마스터3)
- 세선 키가 Master3에 할당되었다.
"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.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)
Replica3 (복제3)
명령이 마스터와 복제에서 분산 실행되므로 문제가 발생할까?
마스터3과 복제1에서 분산되어서 실행되는 명령을 시간 순으로 살펴보자.
- 137ms: 복제1 HGETALL 명령 실행
- 154ms: 복제1 EXISTS 명령 실행, 17ms 차이
- 167ms: 마스터3 HMSET 명령 실행, 13ms 차이
- 167ms: 복제1 HMSET 명령 실행, 0ms 차이
- 174ms: 마스터3 PEXPIREAT 명령 실행, 7ms 차이
- 174ms: 복제1 PEXPIREAT 명령 실행, 0ms 차이
- 188ms: 복제1 HGETALL 명령 실행, 14ms 차이
- 205ms: 복제1 EXISTS 명령 실행, 17ms 차이
명령 사이의 시간 간격은 약 10ms이고, 마스터에서 복제서버로 전파되는 시간은 1ms 이내이다.
그러므로 일련의 명령이 복제서버와 마스터에서 나누어서(분산) 실행되어도 순서가 바뀌어서 문제가 될 확률은 거의 없다.
(이 측정치는 부하(load) 정도, 서버나 네트워크 상황에 따라서 다를 수 있습니다)
http://localhost:8080/hello2
hello2() source
레디스 서버에서 실행되는 명령
Master1 (마스터1)
Master2 (마스터2)
Master3 (마스터3)
- 마스터에서는 2개 명령이 실행된다. 마스터 서버의 부담이 줄었다.
- setAttribute()로 인해서 HMSET 명령에 sessionAttr:hello2-2, sessionAttr:hello2-1 필드와 값이 추가되었다.
"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.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)
Replica3 (복제3)
http://localhost:8080/hello3
hello3() source
레디스 서버에서 실행되는 명령
Master1 (마스터1)
Master2 (마스터2)
Master3 (마스터3)
- 마스터에서는 2개 명령이 실행된다. 마스터 서버의 부담이 줄었다.
"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.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)
Replica3 (복제3)
http://localhost:8080/hello4
hello4() source
레디스 서버에서 실행되는 명령
Master1 (마스터1)
Master2 (마스터2)
Master3 (마스터3)
"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.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)
Replica3 (복제3)
<< Session Sentinel | Session Cluster | >> |
---|