본문 바로가기
개발 지식/Spring Framework

[Redis Session] Spring Boot에서 Redis Session 활용하기 - 04

by 에르주 2021. 9. 26.
반응형

2021.09.24 - [개발 지식/Spring Framework] - [Redis Session] Spring Boot에서 Redis Session 활용하기 - 03

 

[Redis Session] Spring Boot에서 Redis Session 활용하기 - 03

2021.09.23 - [개발 지식/Spring Framework] - [Redis Session] Spring Boot에서 Redis Session 활용하기 - 02 [Redis Session] Spring Boot에서 Redis Session 활용하기 - 02 2021.09.22 - [개발 지식/Spring Fra..

erjuer.tistory.com


이어서 이번 포스팅은 Session을 set하고 get하는 API를 하나 뚫어놓고 redis-cli.exe로 확인해보고자 한다.

@RestController
public class JustController {
    @Autowired
    HttpSession httpSession;

    @GetMapping(path = "/setsession")
    public String setsessionAPI() {
        String value = "Erjuer";
        httpSession.setAttribute("KEY", value);
        String returnValue = LocalDateTime.now().toString() + " \nsession set id : " + httpSession.getId() + " \nsession set Value : " + value;
        return returnValue;
    }

    @GetMapping(path = "/getsession")
    public String getsessionAPI() {
        Random random = new Random();
        String value = (String) httpSession.getAttribute("KEY");
        String returnValue = LocalDateTime.now().toString() + " \nsession get id : " + httpSession.getId() + " \nsession get value " + value;
        return returnValue;
    }
}

redis 및 session timeout 설정

코드를 보면 "KEY" : "Erjuer" 이라는 Key-Value값으로 httpSession에 set하고 get하는 간단한 API이다.

HttpSession에 set을 요청해보고 redis에 저장되어 있는 keys 값들을 조회해보면

command : keys *

application.yml에 설정되어 있는 session.redis.namespace가 prefix로 붙고 session Id값이 저장되어 있는 것을 확인할 수 있다.

그렇다면

  1. spring:session:Erjuer:sessions:09897d67-7b4c-4570-94e9-d9ffad974b16
  2. spring:session:Erjuer:expirations:1632582240000
  3. spring:session:Erjuer:sessions:expires:09897d67-7b4c-4570-94e9-d9ffad974b16

은 각각 무슨 의미일까?
application.yml에 보면 server.servlet.session.timeout: 8m 즉 session timeout이 8분으로 설정되어 있다.

여기서 각각의 TTL(Time to live)를 체크해보면

음?
ttl 조회 시간 차이 (명령어 타이핑 시간) 로 정확히 맞지는 맞지는 않지만
spring:session:Erjuer:sessions:09897d67-7b4c-4570-94e9-d9ffad974b16, spring:session:Erjuer:sessions:expires:09897d67-7b4c-4570-94e9-d9ffad974b16
두개의 시간차이가 5분이 난다.

이 5분은 어디서 생겨난 것일까?
세션이 만료 될 때는 redis에서 해당 세션 ID값에 액세스하여 해당 세션값을 Expire(SessionDestroyedEvent를 실행) 해야한다. 사용자가 설정한 timeout(여기서는 8분)에 바로 만료가 되어버리면 redis가 해당 세션에 액세스 할 수가 없다. 그렇기 때문에 redis 자체에서 실제 만료 시간을 임의로 5분 추가하여 설정한다.

Note: findById 메서드를 사용하면 만료된 세션이 반환되어서는 안된다. 이는 세션을 사용하기 전에 만료 여부를 확인할 필요가 없음을 의미한다. (그래서 5분을 늘렸다는 소리..)

즉 spring:session:Erjuer:sessions:expires은 우리가 설정한 timeout에 해당하는 8분이 설정되어 있고spring:session:Erjuer:sessions은 그 시간보다 5분이 추가된 13분으로 설정이 된 것이다.

더보기

우리가 session값에 설정한 것들은

spring:session:Erjuer:sessions:09897d67-7b4c-4570-94e9-d9ffad974b16에 Hash 형태로 저장되어 있지만 redis가 접근하는 값은 spring:session:Erjuer:sessions:expires이다.


그렇다면 httpsession.set(#{Key}, #{Value}); 만 했을 뿐인데 어떻게 redis에 저장이 되는 것일까?
Spring 공식문서에 있는 RedisIndexedSessionRepository 를 살펴보면

The sections below outline how Redis is updated for each operation. An example of creating a new session can be found below. The subsequent sections describe the details.
HMSET spring:session:sessions:33fdd1b6-b496-4b33-9f7d-df96679d32fe creationTime 1404360000000 maxInactiveInterval 1800 lastAccessedTime 1404360000000 sessionAttr:attrName someAttrValue sessionAttr2:attrName someAttrValue2
=========
EXPIRE spring:session:sessions:33fdd1b6-b496-4b33-9f7d-df96679d32fe 2100
==========
APPEND spring:session:sessions:expires:33fdd1b6-b496-4b33-9f7d-df96679d32fe ""
EXPIRE spring:session:sessions:expires:33fdd1b6-b496-4b33-9f7d-df96679d32fe 1800
==========
SADD spring:session:expirations:1439245080000 expires:33fdd1b6-b496-4b33-9f7d-df96679d32fe
EXPIRE spring:session:expirations1439245080000 2100

HMSET, EXPIRE, APPEND, SADD 명령어를 통해 saving Session을 수행하며 각각의 의미는 다음과 같다.

  • HSET: 입력한 해시 키 밑에 지정한 필드에 값을 저장하고 필드 값이 존재하면 덮어쓴다.
  • EXPIRE: 키값에 대한 Timeout을 명시한다.
  • APPEND: 해시값이 존재하면 그 Value값 끝에 추가한다.
  • SADD: 키값에 리스트 형식으로 더한다. 단, 값이 존재하는 경우 무시한다.


redis-cli.exe에 monitor 명령어를 입력 후 session을 get했을 때

1632585039.476935 [0 127.0.0.1:57771]
"HGETALL" "spring:session:Erjuer:sessions:09897d67-7b4c-4570-94e9-d9ffad974b16"
1632585039.478381 [0 127.0.0.1:57771]
"HMSET" "spring:session:Erjuer:sessions:1a61bf7b-5978-438c-a57f-82f9fd9df76f"
"lastAccessedTime" "\xac\xed\x00\x05sr\x00\x0ejava.lang.Long;\x8b\xe4\x90\xcc\x8f#\xdf\x02\x00\x01J\x00\x05valuexr\x00\x10java.lang.Number\x86\xac\x95\x1d\x0b\x94\xe0\x8b\x02\x00\x00xp\x00\x00\x01|\x1d\xa6\xbet"
"maxInactiveInterval" "\xac\xed\x00\x05sr\x00\x11java.lang.Integer\x12\xe2\xa0\xa4\xf7\x81\x878\x02\x00\x01I\x00\x05valuexr\x00\x10java.lang.Number\x86\xac\x95\x1d\x0b\x94\xe0\x8b\x02\x00\x00xp\x00\x00\x01\xe0"
"creationTime" "\xac\xed\x00\x05sr\x00\x0ejava.lang.Long;\x8b\xe4\x90\xcc\x8f#\xdf\x02\x00\x01J\x00\x05valuexr\x00\x10java.lang.Number\x86\xac\x95\x1d\x0b\x94\xe0\x8b\x02\x00\x00xp\x00\x00\x01|\x1d\xa6\xbet"
"sessionAttr:KEY" "\xac\xed\x00\x05t\x00\x06Erjuer"

========================
1632585039.478973 [0 127.0.0.1:57771]
"SADD" "spring:session:Erjuer:expirations:1632585540000" "\xac\xed\x00\x05t\x00,expires:1a61bf7b-5978-438c-a57f-82f9fd9df76f"
1632585039.479212 [0 127.0.0.1:57771]
"PEXPIRE" "spring:session:Erjuer:expirations:1632585540000" "780000"

========================
1632585039.479465 [0 127.0.0.1:57771]
"APPEND" "spring:session:Erjuer:sessions:expires:1a61bf7b-5978-438c-a57f-82f9fd9df76f" ""
1632585039.479730 [0 127.0.0.1:57771]
"PEXPIRE" "spring:session:Erjuer:sessions:expires:1a61bf7b-5978-438c-a57f-82f9fd9df76f" "480000"
========================

1632585039.479965 [0 127.0.0.1:57771]
"PEXPIRE" "spring:session:Erjuer:sessions:1a61bf7b-5978-438c-a57f-82f9fd9df76f" "780000"
========================

1632585039.480334 [0 127.0.0.1:57771]
"PUBLISH" "spring:session:Erjuer:event:0:created:1a61bf7b-5978-438c-a57f-82f9fd9df76f" "\xac\xed\x00\x05sr\x00\x11java.util.HashMap\x05\a\xda\xc1\xc3\x16`\xd1\x03\x00\x02F\x00\nloadFactorI\x00\tthresholdxp?@\x00\x00\x00\x00\x00\x04w\b\x00\x00\x00\x04\x00\x00\x00\x00x" 1632585039.482415 [0 127.0.0.1:57771]
"HGETALL" "spring:session:Erjuer:sessions:09897d67-7b4c-4570-94e9-d9ffad974b16"

이런식의 명령어를 통해 redis에 save되었을 것이다.


각각의 data를 조회해보면
spring:session:Erjuer:sessions:1a61bf7b-5978-438c-a57f-82f9fd9df76f 의 Type과 Value는 다음과 같다.

spring:session:Erjuer:sessions:1a61bf7b-5978-438c-a57f-82f9fd9df76f

특히 httpsession에 get한 부분은

sessionAttr

이다.

spring:session:Erjuer:sessions:expires:1a61bf7b-5978-438c-a57f-82f9fd9df76f의 Type과 value는 다음과 같다.

spring:session:Erjuer:sessions:expires:1a61bf7b-5978-438c-a57f-82f9fd9df76f



특히 모든 데이터값 앞에 \xac\xed\x00\x05sr\x00\x0와 같이 인코딩 된 것과 같은 형태로 붙여져 있는데 이것은 Redis template에서 데이터의 직렬화를 통해 데이터를 저장하기 때문이다.
직렬화를 통해 데이터를 저장하므로 session 클러스터링을 진행 했을 때 각각의 session에서 해당 데이터들을 조회할 수 있다.

[참조] The Redis template uses serializers for keys, values and hash keys/values. Serializers are used to convert the Java input into the representation that is stored within Redis


spring:session:Erjuer:expirations:1632585540000의 값은
1632585540000 millis-> Sun Sep 26 2021 00:59:00 Date에 만료되는 것을 의미한다.



추가로 8분이 지나고 session ID값이 1a61bf7b-5978-438c-a57f-82f9fd9df76f인 값은 expire되었는데

(spring:session:Erjuer:sessions:expires:1a61bf7b-5978-438c-a57f-82f9fd9df76f 없음)

조회시 아직 spring:session:Erjuer:sessions:1a61bf7b-5978-438c-a57f-82f9fd9df76f 값이 존재하는 것을 확인할 수 있다.


다음 포스팅은 Redis 명령어를 정리해보려고 한다.
Session을 set하고 get하는 API를 하나 뚫어놓고 redis-cli.exe로 확인하기 위해서는 명령어를 알아야 했고 매번 구글링하고 찾아보는 것이 아닌 자주 사용하는 명령어를 블로그에 정리해보고자 한다.




출처:
https://docs.spring.io/spring-session/docs/current/api/org/springframework/session/data/redis/RedisIndexedSessionRepository.html

 

RedisIndexedSessionRepository (spring-session-docs 2.5.2 API)

A SessionRepository that is implemented using Spring Data's RedisOperations. In a web environment, this is typically used in combination with SessionRepositoryFilter . This implementation supports SessionDeletedEvent and SessionExpiredEvent by implementing

docs.spring.io

반응형

댓글