기본적으로 많은 서버시스템의 구성이 Scale-out 방식으로 이루어 집니다.
이런 웹서버를 구축할 때 서버 분산 시스템으로 Scale-out
방식을 선택하는데 서버를 분산•확장할 시에는 몆 가지 문제점이 생길 수 있습니다.
그 중의 하나가 네트워크로부터 온 다수 유저들의 request들이 동시에 올 수 있어 가져온 데이터의 정합성이 불일치
되는 문제입니다.
예를 들어, 사용자가 Session
로그인할 시 로그인했음에도
세션이 저장되지 않는 서버에 요청이 온다면
사용자는 Session 정보를 받지 못하겠지요?
이를 해결하기 위한 redis
를 사용하였습니다.
별도의 cache 서버는 다음 블로그를 참조 해주세요
https://velog.io/@mooh2jj/Redis-캐싱적용해서-read-작업-성능-개선하기
세션을 관리하는 별도의 session storage
로 만들고자 했습니다. 세션 조회와 삭제등과 같은 관리는 모두 이 서버에 요청이 오도록 하는 것
입니다.
그리고 사용자가 증가함에 따라 캐시 저장소에 등록되는 데이터도 함께 늘어나게 될 것이며, 마찬가지로 Redis에 서버에 저장되는 로그인 정보를 담은 세션과 인증번호의 건수 또한 늘어나게 될 것입니다.
Redis는 In-memory Data Stroe
이기 때문에 Physical Memory
이상을 사용하게 되면 swap이 발생
하게 됩니다. swap이 한번 발생하게 되면 Redis가 디스크를 계속 읽게 되어 따라서 성능이 크게 저하
될 수 있습니다. 즉, 성능 향상을 위해 Redis를 사용하고 있는데 그 이점이 사리지지 않기 위해
세션을 저장하는 서버와 캐시 데이터를 저장하는 서버를 분리
해 조금 더 효율적인 메모리 관리가 가능할 것이라 생각했습니다.
ward-study-reservation Project에서는 세션서버에서의 세션저장
은 기본적으로 Httpsession
객체를 통한 로그인과 로그아웃 기능
에 구현하였습니다.
아래는 구현 코드입니다.
Redis에 세션을 관리하기 위해선 아래의 의존성을 추가하고, Redis config 클래스를 작성합니다.
// redis-session
implementation 'org.springframework.session:spring-session-data-redis'
@Configuration
public class RedisConfig {
@Value("${spring.redis.host}")
private String redisHost;
// Redis Session, Cache 서버(Port)의 분리
@Value("${spring.redis.session.port}")
private int redisSessionPort;
@Value("${spring.redis.cache.port}")
private int redisCachePort;
@Bean
public LettuceConnectionFactory connectionFactory() {
return new LettuceConnectionFactory(redisHost, redisPort);
}
@Bean
public RedisTemplate<String, Object> redisTemplate() {
RedisTemplate<String, Object> redisTemplate = new RedisTemplate<>();
redisTemplate.setDefaultSerializer(new GenericJackson2JsonRedisSerializer());
redisTemplate.setConnectionFactory(connectionFactory());
return redisTemplate;
}
}
@Service
@RequiredArgsConstructor
public class LoginServiceImpl implements LoginService {
private final HttpSession httpSession; // HttpSession 처리로 로그인, 로그아웃 구현
private final String USER_ID = "USER_ID";
@Override
public void login(User user) {
httpSession.setAttribute(USER_ID, user.getId());
}
@Override
public void logout() {
httpSession.removeAttribute(USER_ID);
}
}
HttpSession으로 Redis 세션저장소에 관리하기
HttpSession
- 세션을 손쉽게 생성하고 관리하게 할 수 있는 인터페이스- UUID로 세션 ID를 생성해주며,
JSESSIONID
라는 이름의 cookie를 설정해준다.- 로그인시, redis 세션 저장소에
key-value
형태로 로그인한 userId를 httpSession 객체를 통해 set해준다.- 반대로 로그아웃시, remove를 해준다.
- Redis에 저장시
JSESSIONID
->SESSION
으로 이름이 바뀐다.
spring:
session:
store-type: redis # 세션저장소 redis로 지정
redis:
cache:
host: localhost
port: 6379
session:
host: localhost
port: 6380
redis 설치는 이 블로그를 참조하시길 바랍니다.
$ docker run --name redis-session -d -p 6380:6379 redis
$ docker exec -it redis-session redis-cli
localhost:8081/users/login
로그인 후 Redis에 session 데이터들이 저장됩니다.
아래는 redis-cli 콘솔창의 session 데이터들이 저장된 내용들입니다.
여기서 session에 해당되는 값들은 3개로 나옵니다.
3개!
"spring:session:sessions:expires:b15c2c33-9a99-4933-bd4a-fb3e870f1914"
"spring:session:expirations:1666114800000"
"spring:session:sessions:b15c2c33-9a99-4933-bd4a-fb3e870f1914"
여기서 key값으로 설정한 USER_ID
값은 어떻게 확인할까요?
그전에 알아야 할 것이 각각,
Spring redis session에 세션을 저장하면 조회할 수 있는 데이터들인데 타입이 다 다르다는 것입니다.
1. spring:session:sessions:expires (string)
2. spring:session:expirations (set)
3. spring:session:sessions (hash)
여기서 sessiondp 저장한 USER_ID
를 확인할려면 hash 타입에서 값을 가져와서 확인할 수 있었습니다. 명령어로,
hkeys "spring:session:sessions:b15c2c33-9a99-4933-bd4a-fb3e870f1914"
// 해당 "sessionAttr:USER_ID" 있음을 확인!
hget "spring:session:sessions:b15c2c33-9a99-4933-bd4a-fb3e870f1914" "sessionAttr:USER_ID"
USER_ID가 넘버값으로 나옵니다!
참고로 값이 복잡하게 되어있는데 이 값이 해쉬값이기 때문입니다.
redis config 클래스에서 직렬화 설정에 따라 저장하는 방식을 다르게 할 수 있습니다.
로그인시에 USER_ID
가 session에 저장되는 것을 확인할 수 있었고
로그아웃은 session에서 USER_ID
값이 사라지게 할 수 있을까요?
확인해보았습니다.
post localhost:8081/users/logout
post방식의 로그아웃 요청을 보내고
다시 redis에서 해시값을 확인하였습니다.
hget "spring:session:sessions:b15c2c33-9a99-4933-bd4a-fb3e870f1914" "sessionAttr:USER_ID"
확인해보니, session에 저장된 USER_ID
값이 사라졌음을 확인할 수 있었습니다.