Spring Security와 Redis Session을 적절히 조합하여 Session Clustering(세션 클러스터링)을 구현하고자 했는데 직렬화 오류가 계속 발생하여 어떻게 해결할지 고민했다.
세션 클러스터링이란 여러 WAS의 Session이 모두 공유되도록 하는 방법을 말한다.
예를 들어, 서버 A, B가 존재한다고 가정하자. 사용자가 로그인 요청을 서버로 보내면 세션이 서버 A에 저장된다. 이후에 사용자가 저장된 JSessionID로 요청을 보낼 때, 서버 A에 트래픽이 과중되어 서버 B로 요청을 하게 될 경우 서버 B에는 해당 사용자의 JSessionID가 저장되지 않았기 때문에 인가 요청이 정상적으로 처리되지 않을 것이다.
이 문제를 해결하기 위해서 구글링을 하던 도중 인메모리 데이터베이스인 레디스를 사용한 세션 클러스터링을 발견했고 레디스와 함께 집중적으로 공부했다.
그림과 같이 여러 서버가 같은 Redis 세션 스토리지를 가리키도록 설계하는 것이 세션 클러스터링이다.
이렇게 되면 하나의 서버에서 세션이 생성될 때, 한 번만 저장소에 접근하면서 저장이 되기 때문에 부하를 줄일 수 있다.
implementation "org.springframework.session:spring-session-data-redis"
implementation 'org.springframework.boot:spring-boot-starter-data-redis'
@Configuration
public class RedisConfig {
@Value("${spring.data.redis.host}")
private String host;
@Value("${spring.data.redis.port}")
private int port;
@Bean
public LettuceConnectionFactory lettuceConnectionFactory() {
return new LettuceConnectionFactory(new RedisStandaloneConfiguration(host, port));
}
@Bean
public RedisTemplate<?, ?> redisTemplate() {
RedisTemplate<?, ?> redisTemplate = new RedisTemplate<>();
redisTemplate.setConnectionFactory(lettuceConnectionFactory());
redisTemplate.setKeySerializer(new StringRedisSerializer());
redisTemplate.setValueSerializer(new StringRedisSerializer());
return redisTemplate;
}
}
Jedis보다 Lettuce를 써야 하는 이유 - 향로 개발자님 블로그
spring:
session:
store-type: redis
redis:
flush-mode: on_save # Sessions flush mode.
namespace: spring:session # Namespace for keys used to store sessions.
server:
port: 8080
servlet:
session:
cookie:
name: JSESSIONID
@EnableRedisHttpSession
@SpringBootApplication
public class BoardApplication {
public static void main(String[] args) {
SpringApplication.run(BoardApplication.class, args);
}
}
세션 클러스터링 테스트를 위해선 최소 2개의 서버가 필요했다. 인텔리제이에서 port 번호만 변경하면 여러 서버를 만들 수 있다.
서로 다른 서버에서 세션이 유지되면서 로그인이 정상적으로 처리되는 것을 확인할 수 있었다.