- 토이 프로젝트인 중고거래 서비스에서 로그인 상태 정보를 기억하기 위해 세션 정보를 이용해야 했습니다.
- 중고거래 서비스는 대용량의 트래픽을 처리하기 위해 스케일 아웃 환경이기에 여러 대의 서버가 각각 세션 저장소를 독립적으로 갖게 되므로 정합성 이슈가 발생하였습니다.
- 여러 대로 나뉜 서버가 하나의 서비스를 운영하기 위해서는 분리된 세션을 하나의 시스템으로 동작하게 하거나, 고정된 세션을 사용해야 합니다.
- 먼저 고정된 세션을 사용하는 Sticky Session 방식에 대해 알아보았습니다.
- 말 그대로 고정된 세션을 의미하며 로그인 이후의 요청은 각각 세션을 생성한 서버로 보내게 하는 방식입니다. 이를 위해 로드 밸런스는 요청을 받으면 가장 먼저 요청에 쿠키가 존재하는지 확인합니다.
- 쿠키가 있으면 해당 요청이 쿠키에 지정된 서버로 전송됩니다. 쿠키가 없으면 로드 벨런스는 기존 로드 밸런싱 알고리즘 기반으로 서버를 선정합니다.
- 동일한 사용자가 계속 해당 서버에 요청을 보낼 수 있도록 지속해서 서버 정보가 쿠키를 통해 응답에 삽입되어 보내집니다. Sticky Session 방식을 사용하면 세션이 유지되는 동안 동일한 서버만 사용하기 때문에 데이터 정합성은 일치시킬 수 있지만 특정 서버에 트래픽이 집중되는 위험이 있습니다.
- 이유는 자기가 접속해야 하는 서버가 정해져 있기 때문에 하나의 서버에 트래픽이 집중되어 있더라도 사용자는 자신의 세션이 없는 다른 서버는 사용할 수 없습니다. 또한, 서버에 장애가 발생 시 해당 서버를 사용하는 사용자들은 세션 정보를 잃어버리게 됩니다. 그러므로 가용성이 떨어지게 됩니다.
- 정합성 이슈를 해결하면서 가용성과 트래픽 분산까지 확보할 수 있는 세션 클러스터링 방식에 대해 알아보았습니다.
- Tomcat Document의 내용을 참고하였습니다. DeltaManger(all-to-all 방식)를 사용하여 세션 저장소에 변경되는 요소가 발생하면 변경 사항이 다른 세션에 복제가 되는 것이 있었습니다.
- all-to-all 방식은 유저가 이후에 어떤 서버에 접속하더라도 로그인 정보가 세션에 복제되어 있으므로 정합성 이슈가 해결 가능합니다. 또한, 서버 자애가 발생하더라도 중단되지 않고 운영이 가능합니다.
- 하지만 모든 서버가 동일한 세션 객체를 가져야 하기 때문에 많은 메모리가 필요합니다. 또한, 세션 저장소에 데이터가 저장될 때마다 모든 서버에 값을 입력해야 하므로 서버 수에 비례하여 네트워크 트래픽이 증가하는 등 성능 저하가 발생하게 됩니다. 대용량 트래픽을 처리하는 중고거래 시스템에는 맞지 않았습니다.
- Tomcat의 BackupManager를 활용한 primary-secondary 세션 복제 방식이 있었습니다.
- Primary 서버는 Secondary(Backup) 서버에 세션 객체의 key-value 전체를 복제하고 이외의 서버에는 key에 해당하는 JSession ID만 복제하기 때문에 메모리 사용이 all-to-all 방식보다 줄어들게 됩니다.
- 세션 객체 전체를 복제하는 것보단 key만 복제하기 때문에 상대적으로 처리 시간도 줄어듭니다.
- 즉 대규모 클러스터에 사용이 용이합니다. 하지만, primary-secondary 세션 복제 방식은 세션을 복제하는 데 걸리는 시간을 줄일 수 있으나, Primary 서버와 Secondary 서버를 제외한 Proxy 서버에 세션 정보를 요청할 경우 다시 Primary 서버에 요청하여 해당 key에 해당하는 객체를 받아야 하는 단점이 있었습니다.
- 세션 스토리지 분리를 통해 scale-out 시 발생하는 정합성 이슈와 Tomcat 세션 클러스터링 방식의 성능적 한계도 보완하는 방식이 있었습니다.
- 이는 기존 서버가 가진 로컬 세션 저장소가 아니라, 별도의 세션 저장소를 사용하여 WAS가 아무리 늘어난다고 할지라도 세션 스토리지에 대한 정보만 각각 서버에 입력해주면 세션을 공유할 수 있게 됩니다.
- 세션 스토리지 분리를 하면 로드 벨런싱 알고리즘이 잘 구현되어있다는 가정하에 트래픽이 비정상적으로 몰리는 현상이 일어나지 않고, 서버 장애가 발생하더라도 별도의 세션 저장소가 존재하기에 서비스를 계속 제공할 수 있는 가용성도 확보하였습니다.
- 그뿐만 아니라 가장 근본적으로 해결하려던 데이터 정합성 문제도 해결하였습니다. 여러 대의 서버가 하나의 세션을 사용하기에 기존 개별적으로 갖고 있던 로컬 세션 저장소의 데이터 불일치가 발생하지 않았기 때문입니다. 그리고 정합성 해결을 위한 별도의 세션 복제를 할 필요가 없어서 성능적 문제도 보완하였습니다.
- 추가로 세션 스토리지를 분리 후 해당 서버에 장애가 발생하면 세션 이용이 불가하기 때문에 가용성 확보를 위해 복제를 구성하는 방식을 고려하였습니다.
- 데이터베이스 transaction의 대부분은 read인데 slave에선 read를 하고 master에선 insert, delete 등 시간이 오래 걸리는 작업을 분산 처리하여 가용성을 높이고 조회 성능을 향상할 수 있습니다.
- 세션 객체는 key-value 방식으로 저장되기에 RDBMS보단 NoSQL 이 세션 처리 방식에 잘 어울린다 생각하였습니다. NoSQL 스토리지(Database) 중 가장 많이 사용되는 것이 Redis와 Memcached였습니다.
- Memached와 비교해서 Redis는 쓰기 연산은 상대적으로 안 좋지만 메모리 사용 효율, 읽기 연산 처리속도가 좋았습니다. 이는 세션 관련 작업의 경우 쓰기 연산보다는 읽기 연산이 압도적으로 많기에 채택하였습니다.
- 또한, Key-Value 저장소이면서 Value에 List, Set, Sorted Set 등 다양한 자료구조를 지원한다는 점입니다.
- 하지만, 이러한 점은 캐시 솔루션으로써 비교할 때 유효하였고 세션 솔루션으로는 큰 메리트로 느껴지진 않습니다. 하지만 중고거래 비즈니스 로직 중 대용량의 중고 게시글을 조회할 때도 사용할 수 있을 것 같았고 관련 공식 Document가 많아 최종적으로 Redis를 사용하였습니다.