분산 서버 구조 사례
1. 로그온 처리의 분산
로그온 분산 처리 과정
- 클라이언트->서버 ID,Password를 암호화 해서 보낸다
- 로그온 담당 서버(인증서버)에서 이 메시지를 받고 복호화한다
- 서버는 복호화된 메시지에서 ID,Password를 구한다. 그리고 DB에 질의를 던지고 결과를 기다린다
- DB에서 질의를 수행한 후 응답을 서버에 회신한다
- 서버는 이를 받아 로그인 성공 혹은 실패를 판단. 그리고 로그온 결과를 클라이언트에 보낸다
과부하 지점
동시 접속자가 많아지면 인증 서버에서 CPU 사용량이 점점 과부하에 가까워진다
- 로그온 메시지를 복호화하는 연산 과정 때문
데이터베이스 서버도 과부하에 점점 가까워진다
- 질의 분석이나 디스크 스토리지에서 읽기 엑세스를 하는 데 걸리는 시간 때문
과부하 지점 수평 확장
인증 서버 수평 확장
클라이언트가 서버 주소 목록을 가지는 경우
- 수평 확장된 인증 서버 중 하나에 접속
- ID/Password를 보내고 질의 과정을 통해 서버는 클라이언트에 로그온 결과를 보낸다
클라이언트가 서버 주소 목록을 가지고 있지 않는 경우
2. 데이터베이스의 수평 확장
해쉬 함수를 이용한 수평 확장
- 클라이언트는 여러 서버 중 하나를 무작위로 골라서 로그온 요청을 한다
- 서버는 로그온 요청을 받으면 입력받은 ID를 해시 함수에 넣어서 샤드 인덱스를 구한다
- 서버는 해당하는 데이터베이스 샤드에 질의를 던져서 나머지를 실행한다
샤드
- 데이터베이스의 테이블을 레코드 묶음으로 나눈 집합
int Hash(string userID, int shardCount)
{
int x = 0;
foreach(ch in userID)
{
//0에 xor 연산하면 ? ch 그대로가 x에 담긴다
//그 다음 부턴 절대 전의 ch와 같은 값을 가질 수 없는 조합이 된다
x ^= ch;
}
//미리 정해둔 샤드 개수 만큼 리턴 (이 값이 샤드 인덱스가 된다)
return x % shardCount;
}
샤드키
- 어떤 키로 레코드를 찾는데, 그 레코드를 찾기 전에 그것이 어느 데이터베이스 샤드에 있는지 먼저 알아내려는 목적으로 사용되는 키. 파티션 키라고도 불린다
데이터베이스의 업데이트
샤드 개수가 추가되는 경우
위의 MOD 방식의 샤드 인덱스는 샤드가 추가 됨으로써 값이 달라져버린다. 리해시를 하여 해결한다.
리해시
- 해시 테이블 알고리즘에서 항목 개수가 달라지면 기존에 있던 모든 데이터에서 항목 재배치를 하는 것
- 총 레코드 수가 굉장히 많을 때 서버는 점검 상태가 되어야한다
- 문제를 개선하려면 샤드가 추가되거나 제거될 때 레코드 이동으로 발생한 정지 순간이 최소한이어야 한다
리해시 문제의 해결 방법
일관된 해시 알고리즘
- 항목 개수를 고정시키고 한 항목을 해시 함수의 여러 반환 값에 대응 하는 것
- 샤드가 추가될 때마다 기존 샤드 중 하나가 담당하던 해시 값의 범위를 나누어 준다
- 샤드가 제거될 때는 제거 되는 샤드가 인접한 샤드에 해시 값의 범위를 나누어 준다
- 샤드가 1개 늘어나더라도 다른 샤드 중 하나만 재배치 연산을 하면 된다.
매핑 DB
데이터 자체보다 데이터를 가리키는 또 다른 데이터를 다루는 방법
"어떤 키는 어떤 샤드에 있다." 라는 정보만을 가진다
데이터베이스 샤드가 증설되더라도 인접한 샤드에서 레코드를 옮겨 올 필요가 없다.
해당 키를 관리하는 DB가 따로 있기 때문
샤드 삭제시 레코드들을 다른 샤드로 옮겨야 한다. 이때 블로킹은 어쩔 수 없이 일어난다.
다만, 사용자수 급감으로 샤드 개수를 줄일 때는 신속하지 않아도 되기에 블로킹에 의한 지연은 큰 의미가 없다
매핑 DB가 과부하에 걸린다면 ?
- 다른 모든 것이 확장성을 대비 했어도 아무 소용이 없어진다
- 매핑 DB도 확장성을 가져야 한다.
최종적으로 로그인을 담당하는 분산 서버
- 로그인 서버
- 매핑 DB 서버 샤드
- 플레이어 정보를 담은 DB 서버 샤드
이에 해당하는 프로세스 시퀀스
- 클라이언트는 인증 서버 중 하나에 접속
- 매핑 데이터베이스 중 하나를 골라 유저 ID에 대응하는 데이터베이스 #ID를 얻는 질의를 던진다
- 인증 서버는 해당 DB에 CURD 질의를 던진다
서버를 접속할 때마다 로그인을 해야하는 문제
로그인 서버와 용무를 끝낸 후 다른 서버에 접속할 때도 로그인을 해야하는 문제
모든 서버가 로그인 로직을 가지고 있어햐 하므로, 데이터베이스에 부하가 걸린다
해결 방법
- 클라이언트는 로그인 서버에 ID/PassWord를 보낸다
- 로그인 서버는 클라이언트에 로그인 성공여부를 보낸다
- 로그인 서버는 서버B에 크리덴셜을 보낸다
- 서버 B는 ACK를 인증 서버에 보낸다
- 로그인 서버는 클라이언트에 크리덴셜을 보낸다
- 클라이언트는 서버 B에 연결한다
- 크리덴셜도 보낸다
- 서버는 크리덴셜을 보고 유저가 누군지 알아채고 로그인 성공을 클라이언트에 회신한다
크리덴셜
- 비밀 징표란 뜻(대략 ID+Password 조합)
3. 매치메이킹 분산 처리
매치 메이킹
- 서로 다른 플레이어 간 대전이나 협력 플레이를 위해, 다른 플레이어들을 찾아 모으는 것
로비 서버
- 매치메이킹을 담당 하는 서버
서버 하나가 매치메이킹 + 게임방 처리를 한다면?
게임방 안의 월드 시뮬레이션을 위한 서버 CPU 과부하
매치메이킹에 동시접속자가 몰릴 경우 서버 CPU, 램 사용량 과부하
과부하 지점
- 게임방 안 전투 플레이를 처리하는 곳
과부하 해결 방법
응집력에 따른 분산
- 방 안에 있는 플레이어끼리는 데이터 응집력을 높임
- 서로 다른 방에 있는 플레이어끼리는 응집력이 없음
매치 메이킹은 가까운 리전에 있는 플레이어 끼리 매칭 시킴
네트워크 레이전시가 줄어든다
- 로비 서버는 레이턴시에 민감하지 않다
매치 메이킹의 최소 조건
- 나는 지금 매칭을 기다리는 중
- 너도 지금 매칭을 기다리는 중
- 너와 내 실력이 비슷(매칭 기준)
로비 서버의 과부하
- 매치메이킹만 담당하는 로비 서버는 한 대뿐이므로 동시접속자가 계속 증가하면 로비 서버도 결국 과부하에 걸린다
로비가 분산되었을 때
동기 분산 처리
- 다른 로비 서버의 플레이어 목록을 뒤지는 데 서버 간 통신 횟수가 너무 많다
비동기 분산 처리
- 다른 서버에 명령은 보낼 수 있으나 그 명령에 대한 응답을 받을 방법이 없다
매치 메이킹을 위한 의사코드( 비동기 분산 처리)
Match(player)
{
var list;
for each(p in players)
{
//이 코드 구현 자체가 불가능하다 (비동기에서)
if(p != player
&& p.waitingForGame
&& SmilarTo(player.elo , p.elo))
{
list.add(p);
}
}
if(list.count == 9)
{
StartGameRoom(list);
}
}
데이터 복제에 기반을 둔 로컬 처리
작동 방식
로비 서버를 여러 개 둔다
- 클라이언트는 로비 서버 중 아무것이나 하나 골라 접속한다
- 모든 로비 서버 간 통신을 통해 플레이어 목록을 동기화 한다 (매치 메이킹을 기다리는 것들만 동기화)
매칭 대기 취소 버튼을 누르면 그 정보도 다른 서버에 알린다
- 플레이어 정보에는 플레이어가 어떤 로비 서버에 있는지를 담고 있어야 한다
잠재적인 문제점1
- 로비 서버 개수가 많은 경우 초당 보내야하는 메시지가 너무 많아 과부하가 걸린다
해결 방법
서로 같은 범위의 실력을 가진 플레이어들을 같은 로비 서버에 넣는다
- 비슷한 실력의 플레이어끼리만 동기화 하므로 결과적으로 각 로비 서버가 수신하거나 송신해야 할 데이터의 양이 적어진다
잠재적인 문제점2
- 로비 서버 간 플레이어 데이터를 공유할 경우 간발의 차이로 옛날 데이터를 엑세스하는 불상사가 생긴다 = 데이터 스테일 문제
해결 방법
근본적인 해결 방법은 없다. 매치메이킹 재시작을 자동으로 하는 등 수습 처리를 해야 함
메모리저장소에 플레이어 목록을 보관한다
- 로비 서버 안에 연산량이 많고, 메모리 저장소 서버 내부 연산량이 적을 때 효과적
- 메시지 통신량이 많은 경우 메모리 저장소 서버를 수평 확장(파티셔닝)하는 방법으로 해결 가능
4. 몬스터 NPC 처리의 분산 처리
몬스터 처리에서 성능 문제
- 몬스터 수는 플레이어 수보다 훨씬 많다.
- 몬스터의 길찾기 알고리즘은 A* 알고리즘이기 때문에 연산량이 많이 필요하다
- 연산 로직에 기획자의 아이디어가 많이 들어가기에 구조가 복잡해 진다
해야 하는 연산량보다 메시지 송수신에 더 많은 처리량이 낭비됨
가장 많은 연산을 차지하는 길찾기 연산 부분만 기능적으로 분산하는 방법이 좋다
- 게임 서버와 길찾기 연산 전담 서버를 분리
- 게임 서버가 길찾기 서버에 시작점과 끝점 정보를 보낸다
- 길찾기 서버는 두 점을 잇는 경로 정보를 응답한다
5. 플레이어 간 상호 작용 분산 처리
특징
- 매우 정확해야 한다
- 높은 성능 수준이 요구된다
- 원자성이 있어야 한다
- 간발의 지연 시간도 생각해야 한다
플레이어 간 상호 작용 처리는 분산하지 않는 것이 일반적이다. 굳이 분산 한다면 지역적 분산 처리를 한다
6. 로그 및 통계 분석의 분산 처리
게임 서버에서는 플레이어들의 행적, 즉 로그를 모집하고 통계 분석을 한다
- 이러한 처리는 순간적으로 많은 부하를 일으킨다
- DB에 기록하는 시간은 파일에 직접 기록하는 시간보다 훨씬 많이 걸린다
- DB에 쌓인 데이터를 분석하는 경우는 굉장히 오랜 시간이 소요된다
- 로그를 담는 DB에 순간적으로 디스크 I/O가 많이 발생한다
게임 서버와 로그 서버는 분리 해야한다
로그 DB는 신뢰성이 보장되어야 한다
로그 DB가 죽더라도 신뢰성을 가지는 방법
- 게임 서버에서 로그를 남기라고 지시한다
- 그 지시를 로컬 파일에 일단 저장
- 로그 DB에 질의를 던진다
- 질의 실행 후 로컬 파일에 있는 해당 내용을 삭제
- 로그 서버가 죽었다 켜지면 로컬 파일에서 아직 기록하지 않은 로그를 로그 DB에 채워 넣음
역할에 따른 다양한 서버와 종류
순위 서버
- 순위 연산은 많은 연산량을 차지하므로 순위만 담당하는 서버를 분리하는 것이 일반적
메신저 채팅 서버
- 게임 서비스가 불안정한 상황에서도 채팅이 잘 되게 만들고 싶을 때는 메신저와 채팅 서버를 별도로 분리 함
7. 게임 장르별 분산 서버 형태
게임 장르별로 어느 정도는 유사한 패턴이 있으므로 게임 서버 분산 모델을 설정할 때 도움이 된다
MMORPG
게임 월드를 구성하는 소규모 지역별로 담당 서버를 둔다
경매장 같은 특수한 목적의 서버를 별도로 분리하기도 한다
NPC 관련 로직에 과부하가 걸리면 분산. 플레이어 이동도 마찬가지
소켓 I/O에서 과부하가 걸리면 게임서버와 클라이언트 사이에 패킷 릴레이 서버를 두어 릴레이 서버가 멀티캐스트를 분담
무제한의 동시접속자 처리 방법
고전적 분산 처리 방식
- 게임 월드 하나를 담당하는 서버 세트와 똑같은 것들을 계속해서 증설하는 방식
클라이언트 메시지를 적절한 서버로 전달해 주는 릴레이 서버와 넓은 지역을 작은 단위로 나누어 처리하는 로직 서버로 구별
- 플레이어 수만 명에서 수백만 명이 매우 넓은 하나의 지역에서 플레이할 때 이용
심리스 MMO 서버 구조
- 로직 서버가 담당하는 캐릭터 수가 지나치게 많아지면, 로직 서버가 담당하는 캐릭터의 소유권을 인접한 다른 로직 서버에 넘겨준다. 그리고 로직 서버가 담당하는 지역의 넓이를 줄이는 방식.
- 서버당 통신량과 OS 레벨 처리량이 많이 증가한다. 서버 운영 효율성이 떨어진다
- 스테일 , 블로킹 문제가 발생하기도 한다
- 이음새 없는 거대한 월드 안에 동시접속자가 수만 명 이상 있어야 하는 온라인 게임이라면 심리스 MMO 서버 구조가 유일한 해결책
방을 만드는 게임 (MOBA, FPS, MMORPG)
- 수평 확장된 로비 서버가 있고, 여기서 매치메이킹을 담당한다
- 다수의 게임 플레이방을 담당하는 배틀 서버를 구성한다
SNG(소셜 게임)
- 게임 플레이 로직을 담당하는 서버와 게임 플레이어의 메모리 내부 정보를 가진 서버가 분리된다
- 플레이어 간 상호 작용 횟수가 MMORPG,FPS 게임에 비해서 훨씬 적다
- 플레이어 간 상호 작용에서 발생하는 지연 시간이 어느정도 길어도 된다
- 상호작용 해야 되는 플레이어 범위가 훨씬 넓다
- 비동기 멀티플레이를 하는 모바일 RPG 게임에서도 종종 이렇게 구성한다
'게임 서버 프로그래밍 > 게임 서버 프로그래밍 교과서' 카테고리의 다른 글
#9. 분산 서버 구조 (0) | 2020.02.17 |
---|---|
#8. NOSQL (1) | 2020.02.17 |
#7. 데이터베이스의 기초 (0) | 2020.02.17 |
#6. 프라우드넷 (0) | 2020.02.17 |
#5. 게임 네트워크 (0) | 2020.02.17 |