TIL

24.08.21 TIL - session clustering

개발공명 2024. 8. 23. 11:35

오늘 배운 것

  • session clustering

 

문제 인식

세션

로그인을 구현할 때 세션 방식을 사용하는 방법도 있다. 

 

세션 방식 간단히 설명해보겠다. 

 

  1. 사용자가 로그인에 성공하면 서버 메모리에 세션 저장소가 있는데 이곳에 사용자 정보를 넣어 놓는 것이다. 
  2. 서버는 이 사용자에 대한 session id를 쿠키에 넣어 다시 브라우저 (사용자)에게 돌려준다. 
  3. 브라우저는 쿠키 저장소에 쿠키를 저장해 두었다가 서버에 요청을 보낼 때 session id를 같이 넣어서 보낸다. 
  4. 서버는 세션 저장소에서 session id와 연관된 사용자 정보로 요청 보낸 사용자가 누구인지 파악한다. 

 

어떻게 보면 세션은 로그인에서 중요한 어떤 사용자인지를 판단하는 일을 하는 것이다. 

 

HTTP의 특징인 stateless 때문에 어떤 사용자가 보낸 요청인지 알 수 없는데 세션이 이 일을 하는 것이다. 

 

로그인은 id, password DB에 저장된 것과 맞는지 확인하고 거기에 더해 세션으로 어떤 사용자인지 판단하는 것입니다.

 

문제 인식

세션의 단점이 있다. 

 

하나의 서버로는 사용자의 요청을 감당하기 어려워질 때가 있다. 

 

이럴 때 같은 기능을 하는 Spring Boot 서버를 여러 개 두는 Scale Out을 진행한다. 

 

이렇게 Scale Out을 하면 각 서버에 요청을 분산해서 부하를 줄일 수 있다

 

하지만 세션을 사용하면 Scale Out을 할 때 문제가 발생한다.

 

 

문제 상황은 다음과 같다. 

 

  1. 처음에는 A 서버로 요청을 보내서 session id를 받아온다. 
  2. 다시 요청을 보낼 때 session id를 넣어 보내는데 이번에는 B 서버로 요청이 간다. 
  3. B 서버는 session id를 보고 세션 저장소를 뒤져보지만 없는 session id이다. 
  4. 로그인한 사용자임에도 로그인하지 않은 사용자처럼 된다. 

 

위에서도 봤듯이 세션은 서버 내부에 세션 저장소를 만들고 사용자 정보를 저장한다. 

 

그렇기 때문에 여러 서버가 있다면 한 서버에서는 유효한 session id라고 해도 다른 서버에서는 없는 session id인 것이다. 

 

세션이 유지되지 않는다는 문제점이 생기는 것이다. 

 

그래서 여러 서버를 가동하게 되면 session id를 서버 내부에서 관리하기 어려워진다. 

 

이런 문제를 해결해야 하는데 방법이 2가지가 있다. 

 

각 방법을 살펴보자. 

 

해결방안 1 - sticky session

첫번째 해결 방법은 sticky session이다. 

 

이름에서 알 수 있듯이 특정 사람이 보낸 요청을 하나의 서버로 고정하는 방법이다. 

 

요청을 분산하는 로드밸런서에서 요청을 보낸 사용자를 기록한다. 

 

해당 사용자가 다시 요청할 경우 최초 요청이 전달된 서버로 요청을 전달한다.

 

이 방법은 로드밸런서의 기능만 설정하면 간단하게 구현할 수 있다. 

 

하지만 이 방법은 문제점이 있다. 

 

사용자가 급격히 늘어나 Scale Out을 진행했다. 

 

9명의 사용자가 있어서 각각 3명씩 3대의 A~C 서버로 요청을 분산했다.

 

그런데 A, C 서버로 요청을 고정한 사용자 6명이 서비스에 관심이 떨어져 서비스를 사용하지 않는 것이다. 

 

그러면 2개의 서버가 노는데 sticky session이기 때문에 3명의 사용자 모두 B 서버로 요청을 보내야 한다. 

 

서버는 놀고 요청 받는 서버만 바쁜 문제가 발생한다.

 

이런 문제점이 있어서 Sticky Session 방법은 좋은 해결 방법이 아니다. 

 

해결방안 2 - session clustering

다음 해결 방법은 session clustering이다. 

 

이것은 여러 서버들이 하나의 저장소를 공유하는 것이다. 

 

그리고 그 저장소에 세션에 대한 정보 저장해서 요청이 어느 서버로 전달이 되어도 세션 정보가 유지되게 하는 것이다. 

 

요청이 와서 세션을 생성하는데 사용자 정보를 서버 내부의 세션 저장소에 하는 것이 아니라 외부 저장소에 하는 것이다. 

 

그러면 해당 사용자가 다시 요청을 할 경우, 다른 서버로 요청이 보내져도, 세션 정보 자체는 외부 저장소에 저장되어 있기 때문에 확인이 가능해진다.

 

 

외부 저장소에 세션 정보를 저장해 놓으면 여러 서버가 데이터를 같이 공유할 수 있다. 

 

그래서 서버의 자유로운 추가, 제거도 가능해서 Scale Out에 굉장히 좋다. 

 

하지만 session clustering을 하면 서버 외부에 세션을 저장해서 관리해야 하는 곳이 하나 더 늘긴한다. 

 

그리고 통신 과정에서 어쩔 수 없는 지연이 발생하기도 한다. 

 

그래서 지연이 적은 Redis와 같은 인메모리 DB를 외부 저장소로 많이 사용한다.

 

session clustering 구현

그렇다면 이런 session clustering은 어떻게 구현해야 하는가?

 

내용만 보면 구현하기 굉장히 어려워 보인다. 

 

하지만 Spring Boot와 Redis를 함께 사용하고 있다면 매우 쉽게 구현할 수 있다. 

 

Redis와 Spring Session dependency를 추가해주기만 하면 된다. 

 

Redis를 사용하고 있다면 두번째 줄만 추가해주면 된다. 

dependencies {
	implementation 'org.springframework.boot:spring-boot-starter-data-redis'
	implementation 'org.springframework.session:spring-session-data-redis'
	...
}

 

아니면 spring initializer에서 아래와 같이 dependency 추가하면 된다. 

 

 

Spring Session은 Spring의 하위 프로젝트 중 하나로, 사용자의 세션 정보를 다루는데 유용한 API를 제공한다. 

 

이렇게 하면 Redis에 별도로 세션을 저장하게 된다. 

 

이렇게 하고 같은 서버 어플리케이션을 여러 개 띄우고 포트를 다르게 해서 보내도 세션이 유지가 된다. 

 

세션을 사용하는 것은 이전과 같이 스프링의 HttpSession을 사용하면 된다. 

 

 

세션 받기

 

Controller에서 진행해야 한다. 

 

2가지 방법 있는데 직접 주입 받거나 HttpServletRequest로부터 얻는 것이다. 

// 직접 주입 받기
@RequestMapping("/example")
public String example(HttpSession session) {
    String userName = (String) session.getAttribute("userName");
    return "exampleView";
}

or

// HttpServletRequest로부터 받기
@RequestMapping("/example")
public String example(HttpServletRequest request) {
    HttpSession session = request.getSession();
    String userName = (String) session.getAttribute("userName");
    return "exampleView";
}

 

세션 사용 메서드

// 세션에 데이터 저장
// key는 String, value는 Object 타입 (객체는 직렬화 필요)
session.setAttribute(key, value);

// 세션에서 데이터 얻기
// 아래는 value가 String 타입인 경우
String userName = (String) session.getAttribute(key);

// 세션에서 데이터 제거
session.removeAttribute("userName");

// 세션 무효화
session.invalidate();

 

이렇게 세션을 생성하고 안에 사용자 정보가 들어가 있으면 어느 서버에서 요청이 와도 session id로 사용자 정보를 얻을 수 있는 것이다. 

'TIL' 카테고리의 다른 글

24.08.23 TIL - 모니터링  (0) 2024.08.26
24.08.22 TIL - 캐싱  (0) 2024.08.23
24.08.20 TIL - Redis in Spring  (0) 2024.08.21
24.08.19 TIL - Redis  (0) 2024.08.20
24.08.14 TIL - GitHub Actions  (1) 2024.08.16