일상

2021 NDC - 쿠키런 킹덤 서버 아키텍처 뜯어먹기!

poppy 2021. 6. 15. 18:07
반응형

https://ndc.nexon.com/session/sessionView?sessNo=100007286 

 

NDC|온라인세션|<쿠키런: 킹덤> 서버 아키텍처 뜯어먹기!-천만 왕국을 지탱하는 다섯가지 핵심 기

<쿠키런: 킹덤> 서버 아키텍처 뜯어먹기!-천만 왕국을 지탱하는 다섯가지 핵심 기술 이 세션에서는 올 초 출시된 <쿠키런: 킹덤> 게임 서버의 아키텍처에 대해 발표하려고 합니다. <쿠키런: 킹덤>

ndc.nexon.com

넥슨 개발자 컨퍼런스에 다양한 세션이 있었는데요! 저는 그 중에서 최근까지 재밌게 했던 게임인 쿠키런 킹덤 서버에 관한 세션을 보았습니다 실무에서 서버를 어떻게 구축하는지 알 수 있어서 정말 유용했던 세션이었습니다 세션을 보고 간단히 정리한 내용들을 적어보도록 할게요 :)

 

1. 액터 모델로 분산  Stateful 서버 만들기

Stateless 서버 - 서버가 유저의 상태를 저장하고 있지 않는다. 필요할 경우 매번 DB 혹은 캐시에서 불러와서 요청을 처리한다. 가장 중요한 특징은 게임 서버들이 서로 독립적으로 존재하고 임의의 게임 서버가 임의의 유저의 요청을 처리할 수 있다는 점. 

Stateful 서버 - 서버가 유저의 상태를 저장하고 있는다. 유저의 요청은 서버에 저장된 상태를 바탕으로 처리된다. Stateless 서버보다 구조가 복잡하다. 왜냐하면 특정 유저의 요청을 처리할 수 있는 게임 서버는 오직 한 개로 특정되기 때문에 로드밸런서가 유저와 게임 서버 간의 매핑을 관리해야 한다.

 

쿠키런 킹덤은 Stateful 서버를 택했는데 이 서버를 만들기 위해 Akka Clusters 라이브러리를 사용했다. Akka Clusters는 분산 어플리케이션 구축을 위한 기능들을 제공해주는 Akka의 모듈 중 하나이다. Akka는 주로 액터 모델을 구현하기 위해서 사용되는 라이브러리이다.

액터는 경량화된 쓰레드와 큐가 합쳐진 것이다. 액터에게 메시지를 보낸다는 것은 액터의 큐에 메시지를 넣는 것을 의미한다. 액터가 가지고 있는 경량화된 쓰레드는 자신의 큐에서 하나씩 메시지를 꺼내서 처리한다.

 

2. 데이터 저장 기법 - CRUD vs 이벤트소싱

 

전통적인 데이터 저장기법: CRUD

전통적인 방법에서는 DB 스키마를 설계하고 DB에 CRUD 성격의 쿼리들을 이용해 상태를 저장하게 된다. CRUD 방식을 사용하는 경우 데이터 베이스가 서버 개발의 중심이 되는 경우가 많다. 또, 서비스가 복잡해질수록 DB 테이블과 쿼리들이 많아지며 복잡한 쿼리를 사용할수록 성능에 악영향을 주게 된다.

 

상태 저장에 대한 새로운 접근: 이벤트 소싱

DB에는 최종 상태가 아닌 발생한 이벤트들을 저장한다. 최종 상태는 이벤들을 리플레이함으로써 얻을 수 있다. 이벤트가 많이 쌓이면 리플레이할 때 시간이 오래걸릴 수 있다. 따라서 이벤트 소싱을 사용할 때는 반드시 스냅샷이라는 방법을 같이 사용해야 한다. 이 방법은 특정 번호의 이벤트까지 리플레이된 상태를 미리 저장해둠으로써 다음부터는 처음부터 리플레이 하지 않도록 하는 최적화 방법이다. 즉, 리플레이를 할 때 스냅샷 테이블에서 가장 최신의 상태를 먼저 가져오고 그 이후의 이벤트들에 대해서만 리플레이를 수행한다.

 

쿠키런 킹덤에서는 이벤트 소싱을 사용하여 데이터를 저장한다.

 

3. 함수형 프로그래밍과 DSL로 콘텐츠 구현 박살내기!

Stateful 서버의 경우 상태를 어떻게 잘 관리하는냐가 중요하다. 킹덤 서버는 스칼라 언어를 사용해서 함수형 프로그래밍을 하고 있다. 자체적으로 사이드 이펙트 없는 콘텐츠 로직을 위한 DSL을 구현해서 사용한다.

 

프로그램의 특징은 오직 인풋만을 통해 로직을 수행하고 아웃풋을 통해서 결과를 반환한다는 점이 있다. 인풋으로는 이전 상태와 의존성을 받고 아웃풋으로는 이후 상태, 발생한 이벤트들, 발생한 예외들을 반환하게 된다. 프로그램이 사이드 이펙트가 없이 동작하기 위해서는 프로그램이 수행되는데 필요한 의존성들을 미리 준비해서 넣어줘야한다. 따라서 킹덤 서버는 의존성을 매번 외부에서 가져오는 것을 비효율적이라고 생각하여 다른 해결책을 모색했고 아래 사진이 해결책이다.

 

4. 유저간의 상호 작용 구현하기: 2PC vs SAGA

유저간의 상호작용은 하나의 요청이 여러 유저의 상태를 동시에 변켱시키는 것이다. 유저간의 상호작용에는 친구 요청 기능이 있다.

친구 요청 기능에서 하나의 정보가 2개의 액터에 나눠서 저장되므로 일관성 보장에 굉장히 주의해야한다. 그럼에도 불구하고 여러 가지 장점이 있었다. 장점에는 기존에 만들어 놓은 액터 모델, 이벤트 소싱, 프로그램 DSL 구조를 그대로 활용할 수 있고, 기존에 유저 액터 바탕으로 구현된 다양한 콘텐츠들과의 연계가 쉽다는 점이 있다. 

 

2PC는 이벤트들을 한번에 모아서 DB에 하나의 트랜잭션으로 저장하기 때문에 SAGA 패턴과 달리 일관성 보장이 가능해지게 된다. 친구 요청 기능에서 일관성 보장을 할 수 있게 된다.

 

5. 이벤트 기반 아키텍처를 바탕으로 확장하자

 

조회 API는 친구 정보 조회, PVP 상대방 정보 조회, 길드 정보 조회 등을 의미한다. 조회 API를 수행할 때 항상 액터에서 정보를 가져온다면, 서버에는 너무 많은 액터가 떠있어 메모리가 낭비된다. 또한 이벤트 리플레이도 너무 자주 일어나 DB와 서버에 많은 부하를 주게 된다. 따라서 킹덤 서버는 조회 API의 경우 액터를 사용하지 않고 별도의 “View DB”에서 정보를 가져온다. 이러한 기술을 CQRS(Command and Query Responsibility Segregation) 라고 한다.

 

쿠키런 킹덤은 이벤트 기반 아키텍처를 사용하고 아래가 이에 관한 설명이다.

반응형