Search

분산 서버에서 효율적으로 세션을 관리하는 방법

Created
2023/09/09 16:32
Tags
Backend
Redis
Session Management
Date
2023/09/10
설명
카테고리
컴퓨터 과학 ⚙️
이 글은 스케일아웃 방식으로 서버를 확장할 경우 발생할 수 있는 세션 문제에 대해 공부한 글을 정리했습니다.
모든 소스코드와 가끔 나오는 사진 모두 직접 만들어 틀린 내용이 있을 수 있습니다.
틀린 내용이 있거나 피드백이 있다면  에 있는 채널톡으로 알려주시면
참고하면 좋을 내용들
session-cluster-test
ing9990

세션 불일치 문제

먼저 분산환경에서 세션 불일치 문제가 왜 일어나는지 알아보자.
사진처럼 단일 서버로 운영되고 있는 서비스의 경우에는 세션 불일치 문제가 발생하지 않는다.
하지만 이 사진처럼 여러 대의 서버로 라우팅되도록 구성되어 있다면 서버가 세션을 잃어버리는 문제가 생길 수 있다.
첫 번째 요청
첫 요청에서는 x.20번 서버로 라우팅이 되어 세션 ID를 발급 받았다.
두 번째 요청
그러나 두번 째 요청에서는 x.22번 서버로 라우팅되어 .22번 서버가 들어온 세션을 모르는 상태가 발생할 수 있다.
엇!!? 그러면 사용자의 요청을 해당 세션을 가지고 있는 서버로 라우팅하면 문제 해결이네 !!!

Sticky Session

Sticky Session 방식은 요청을 최초로 처리한 서버로만 라우팅 해서 트래픽을 분산시키는 방법을 말한다.
그림처럼 세션을 발급한 서버로만 라우팅해서 애초에 다른 서버로 가지 않도록 하는 방법이다.
하지만 예상대로 문제가 또 발생한다.
클라이언트는 세션을 발급한 서버로만 요청하기 때문에 시간이 지나 한 서버로 요청이 몰리는 문제가 생길 수 있다.
또한 세션은 기본적으로 서버 메모리에 저장된다. 이 말은 즉 서버가 죽거나 재실행되는 경우 세션 데이터가 소멸된다.
엇? 그러면 세션을 잃어버리지 않도록 모든 서버에 세션을 공유하면 문제 해결이네!!

Session Clustering

세션 클러스터링은 세션을 가용중인 모든 서버에 공유해서 세션을 잃어버리지 않게 하는 방법이다.

장점

세션이 모든 서버에 복제되기 때문에 한 서버에 고장으로 세션이 유실되지 않는다.
또한 Sticky Session처럼 한 서버로 부하가 몰리지 않는다.
사진처럼 xx.01 서버에 저장된 세션이 xx.02, xx.03 서버까지 모두 동기화 한다.

단점

모든 서버로 세션을 복제하기 때문에 메모리 관리가 비효율적이다.
사진처럼 x.22 서버에서 x.21 서버로 세션이 복제되는 찰나에 x.20으로 요청되어 세션이 불일치하는 문제가 생길 수 있다.
세션을 데이터베이스에 저장하면 데이터를 영속할 수 있다는 장점이 생기고 다중 서버 환경에서 세션 데이터를 공유할 수 있다는 장점이 있다.
하지만 역시나 데이터베이스에 세션을 저장하는 방법도 문제가 된다.
1.
성능 이슈
데이터베이스에 세션을 저장하면 메모리 기반 저장소보다 성능이 떨어진다.
네트워크 지연 문제
데이터베이스에 접속하기 때문에 지연 문제가 생길 수 있다. (Connection이 부족할 경우)
2.
데이터베이스 부하
이 문제는 해당 데이터베이스를 사용하는 다른 서버에서도 문제를 일으킬 수 있기 떄문에 지양해야 하는 방법이다.
3.
디스크이다 보니 I/O가 메모리에 비해 느리다.
엇!! 그럼 외부 세션 클러스터로 데이터베이스가 아닌 캐시를 사용하면 문제 해결이네 !!!

Distributed Caching

세션 데이터를 분산 캐시에 저장하여 분산 환경에서 세션을 공유할 수 있고 디스크에 비해 성능적으로 좋다.
분산 캐시에 세션 데이터를 저장하면 I/O가 많은 세션 데이터를 디스크보다 효율적으로 조회, 저장할 수 있음.
분산 환경에서 세 간 데이터 공유가 가능해짐.

스프링 부트에서 적용하는 방법

1.
Redis 관련 의존성 추가
implementation 'org.springframework.boot:spring-boot-starter-data-redis' implementation 'org.springframework.session:spring-session-data-redis:3.1.1' implementation 'org.springframework.boot:spring-boot-starter-web'
Java
복사
2.
세션 저장
@RestController public class GreetingApi { @GetMapping void greeting(HttpServletRequest request) { request.getSession(true).setAttribute("Hello", "world"); } }
Java
복사
3.
설정파일 수정
server: port: 8091 servlet: session: cookie: path: / # 쿠키가 유효한 경로 지정. 루트로 지정하면 모든 하위 도메인에서 사용가능 name: JSESSIONID # 쿠키 이름 설정 http-only: true # JS에서 수정하는 것을 방지한다. (XSS로 세션 하이재킹 하는거 대비) secure: true # 쿠키가 HTTPS 경로만 전송되도록 설정한다. spring: data: redis: host: localhost port: 6379 password: session: redis: repository-type: indexed
Java
복사
4.
결과 확인
~ redis-cli == Redis 접속 == # 설정한 AttributesRedisHashes에 저장된다. # Key: spring:session:sessions:1eaa004a-7ae1-45ad-aac4-41d324407ff6 # Field: sessionAttr:{세션에 설정한 Arrt이름} 127.0.0.1:6379> hget spring:session:sessions:1eaa004a-7ae1-45ad-aac4-41d324407ff6 sessionAttr:Hello "\xac\xed\x00\x05t\x00\x05world"
Java
복사

분산 캐시의 예시

Redis
Memcached