#9. 분산 서버 구조

게임 서버 프로그래밍/게임 서버 프로그래밍 교과서

2020. 2. 17. 16:07

9.분산 서버 구조

분산 서버 구조

1. 수직 확장과 수평 확장
  • 확장성

    사용자 수가 늘어나더라도 쉽게 대응할 수 있어야 한다는 의미

    • 확장성의 목표

      최대로 처리할 수 있는 사용자 수가 무제한이어도 가능해야 한다

    • 스케일 업 = 수직 확장

      • 서버의 하드웨어를 더 좋은 것으로 교체하여 처리량을 늘린다
    • 스케일 아웃 = 수평 확장

      • 서버 대수를 늘려서 더 많은 처리를 하는 것
      • 더 현실적인 방법
      • 소프트웨어 설계가 복잡하다
      • 수직 확장으로 향상되는 서버보다 단위 처리 속도가 더 느리다
      • 수평 확장 하기 전 보다 더 느려질 수 있다
2. 서버 분산이 없는 경우
  • 모든 게임 서버 로직은 서버 프로세스 하나에서 수행한다

  • 모든 플레이어 정보는 데이터베이스 하나에 저장한다

  • 동시 접속사 수가 무제한으로 증가한 경우

    • 서버로 보낸 메시지에 대한 처리 응답이 늦게 도착

    • 서버 접속 과정이 매우 오래 걸린다

    • 서버와 연결이 돌발 해제 된다

    • 서버 접속 자체가 실패하여 타임아웃 현상이 발생

    • 서버에서 발생하는 현상

      • CPU 사용량이 증가
      • 클라이언트에서 메시지를 받는 속도보다 메시지를 처리하는 속도가 느릴 때 메모리 사용량 증가
      • 클라이언트에 보낼 메시지의 발생 속도보다 실제로 메시지를 보내는 속도가 느릴때 메모리 사용량 증가
      • CPU 과부하는 램 사용량 증가로 이어진다
  • 데이터베이스에서는 디스크의 최대 처리 속도를 웃도는 I/O를 요구하는 명령이 쌓임

  • 서버에 물려 있는 네트워크 기기에 과부하가 걸림

    • 라우터 과부하로 패킷 유실 발생
    • TCP 재전송 타임아웃으로 TCP 연결 해제 발생
    • TCP 소켓에서 ECONNABORTED 오류 발생
3. 고전적인 서버 분산 방법
  • 서버 클러스터

    • 인증 서버

      • 사용자가 ID/PassWord 입력했을 때 그것을 인증 처리하는 역할을 함
      • 하는 일이 적다 보니 굳이 수평 확장을 하지 않아도 됨
    • 채널 서버

    • 스위치/라우터

    • 방화벽

      • 악성 해커에게서 서버 클러스터를 보호
  • 장점/단점

    • 간단한 분산 서버 구성은 즉시 할 수 있다

    • 게임 기획과 관련된 문제가 있다

      • 같은 계정이라도 플레이어 정보가 서로 다른 채널 서버에 있으므로 플레이어가 열심히 키워놓은 캐릭터를 다른 채널 서버에서 쉽게 사용할 수 없다
      • 플레이어는 자기가 플레이했던 채널 서버에서만 계속 게임을 해야 한다
      • 모바일 게임과 글로벌 서비스 게임에서는 채널 서버를 선택하는 과정이 없을 때가 많다
  • 논리적으로 단일서버이지만, 실제로 서버 클러스터 형태인 서버를 개발할 필요가 있다

4. 논리적 단일 서버 분산
  • 게임 서버 분산 절차

    • 단일 서버 기준에서 과부하가 걸리는 지점을 분석해 파악

      • 라우터,스위치 / 방화벽 / CPU / 스토리지중 하나가 한계에 부딪히면 분산처리
      • 엄선한 지점에서만 분산 처리를 하도록 추가 개발하는 것은 경제적이다
      • 성능 분석 도구를 사용해서 어떤 함수가 가장 많은 처리량을 차지하는지 확인후 최적화
    • 과부하가 걸리는 지점을 여러가지 분산 처리 방식으로 분산

      • 데이터 단위 분산

      • 기능 단위 분산

      • 게임 로직의 분산 처리 방식

        • 동기 분산 처리
        • 비동기 분산 처리
        • 데이터 복제 및 로컬 처리
5. 데이터 분산 vs 기능적 분산
  • 데이터 분산

    한 머신이 처리해야 하는 데이터를 같은 역할을 하는 여러 머신이 나누어서 처리하는 것

  • 기능적 분산

    한 머신이 처리해야 하는 데이터의 처리 단계를 세분화해서 여러 머신이 나누어 처리하는 것

  • 데이터 단위 분산 (데이터베이스 개념)

    테이블 1개를 테이블 안의 키 필드 단위로 분배하는 것

  • 기능 단위 분산 (데이터베이스 개념)

    서로 다른 테이블을 서로 다른 서버에 배치하는 것

6. 로직 처리의 분산 방식들
  • 상호작용 의사코드
// 분산 처리가 안된 코드
Player_Att(player,Monster)
{
    player.bullet--;
    monster.hitpoint -=10;
    if(monster.hitpoint <0)
    {
        player.item.add(gold,30);
        DeleteEntity(monster, 10sec);   
    }
}
  • 동기 분산 처리

    • 어떤 연산을 다른 서버에 던져 놓고 그 결과가 올 때까지 대기
    • 그 연산과 관계된 데이터가 도중에 변경되지 않게 잠금(lock)을 해야 한다.
  • 동기식 명령 처리법

Player_Att(player, monster)
{
    lock(player)
    {
        player.bullet--;
        e = otherServer.DamagerCharacter(player.id,monster.id,10);
        waitForResult(e);
        if(e.hitPoint < 0)
        {
            player.item.Add(gold,30);
        }
    }
    
    DamageCharacter(attacker, character, damage)
    {
        character.hitPoint -= damage;
        result.hitPoint = character.hitPoint;
        Reply(result);
    }
}
  • 분산 락 기법
Player_Attack(player, monster)
{
    lock(player)
    {
        player.bullet--;
        //몬스터 정보를 엑세스하기 위해 분산 락을 요청
        otherServer.RemoteLock(monster);
        //서버2에서 몬스터 정보를 얻어옴
        m = otherServer.RemoteGet(monster.id);
        m.hitPoint -= damage;
        if(m.hitPoint <0)
        {
            player.item.Add(gold,30);
            //서버2의 몬스터 정보를 업데이트 함
            otherServer.RemoteDelete(monster.id);
        }else
        {
            //서버2의 몬스터 정보를 업데이트 함
            otherServer.RemoteSet(m);
        }
        //서버 2에 분산 락 해제를 요청하고 응답을 받음
        otherServer.RemoteUnlock(m);
    }
}
  • 동기식 데이터 변경법 특징

    • 서버 1에서는 서버 2에 명령을 보낸 후 이에 대한 응답이 올 때까지 기다려야 한다

    • 동기식 데이터 변경법에서는 메시지 왕복이 총 세 번 오간다. 따라서 최소 120 마이크로초가 걸린다

    • 서버 1에서 플레이어 정보를 보호하는 임계 영역이나 뮤텍스가 지나치게 광범위하게 보호하는 경우

      40~120마이크로초만큼 시간이 지연될 가능성이 높아진다

    • 멀티스레드 혹은 멀티 프로세스로 작동하거나 뮤텍스 잠금 범위를 좁혀야한다

  • 비동기 분산 처리

Player_Att(player, monster)
{
    player.bullet--;
    otherServer.DamageCharacter(player.id, monster.id,10);
}

DamageCharacter(callerServer, attacker, character, damage)
{
    character.hitPoint -= damage;
    if(character.hitPoint < 0)
    {
        callerServer.GiveItem(attacker.id, gold, 30);
        DeleteEntity(character, 10sec);
    }
}

GiveItem(character, item, amount)
{
    character.item.Add(item, amount);
}
  • 비동기 분산 처리의 특징

    • 동기 분산 처리와 달리 잠금으로 인한 병목이 없음
    • 모든 로직을 이 방식으로 구현하기 어렵거나 불가능함
    • 요청에 대한 응답을 기다리는 과정이 없기 때문에 반환 값을 주고받을 수 없음
    • 서로 일방적인 통보를 주고받아야 함
  • 동기식, 비동기식,데이터 복제 로컬 분산 처리의 단점

    • 중요한 핵심 처리는 기계어 명령어 수십~수백 개인데, 분산된 서버 간 대화에 기계어 명령어가 수천 개 사용되므로 과도하게 분산 처리를 하면 비효율적인 상황이 발생
  • 데이터 복제에 기반을 둔 로컬 처리

Player_Att(player, monster)
{
    player_bullet;
    monster.hitPoint -=10;
    if(monster.hitPoint < 0)
    {
        player.item.Add(gold,30);
        DeleteEntity(monster, 10sec);
    }
}
  • 데이터 복제의 특징

    • 다른 서버 데이터를 들고 있으며 로직에 따른 데이터 변경 후 그것을 다른 서버에 알림
    • 한쪽 서버에서 데이터 변화가 발생하면 나머지 서버에도 전파된다. 즉 복제가 일어난다.
    • 분산 처리에서 발생하는 병목 현상이 없을 뿐만 아니라 여러 머신에 걸쳐 연산하지도 않으므로 응답 속도도 분산하기 전과 같이 빠르다
    • 사본 데이터는 원본 데이터와 간발의 차이로 생기는 스테일 데이터 문제가 있을 수 있다.
    • 하이젠버그 (찾아 보려고 하면 바로 사라지는 버그)가 생김
7. 데이터 응집도
  • 응집도

    • 특정 영역 안에 얼마나 많은 데이터가 관련되고 뭉쳐 있는지에 대한 의미

    • 어떤 데이터가 있을 때 그 데이터와 자주 상관되는 다른 데이터가 얼마나 많은지에 대한 의미

    • 응집도가 높은 데이터끼리는 가급적 분산 처리를 하지 말고, 응집도가 낮은 데이터에 대해 분산 처리를 하라

    • 좁은 지역에 많은 캐릭터가 있을 경우 서버 한 대가 이것을 모두 처리하는게 낫다. 캐릭터의 지리적 위치를 응집도를 기준으로 하여 가까운 거리에 있는 캐릭터끼리는 같은 서버에 두는 것

    • 매치메이킹

      • 가급적 실력이 비슷한 플레이어끼리 매칭
      • 데이터 응집도의 기준이 '플레이어 실력'으로 잡을 수 있다
8. 기능적 분산 처리
  • 수평 분산 처리를 못하는 경우

    • 암달의 법칙이 심하게 작용 (동기식)
    • 요청과 응답을 받아야하는 경우(비동기식)
    • 데이터 일관성이 깨지는 경우(데이터 복제)
  • 기능적 분산 처리 / 수직 분산 처리

    • 수평 분할을 할 수 없을 때의 선택될 수 있는 대안

    • 경매장

      • 플레이어 간 입찰 경쟁과 낙찰 과정이 원자성을 가지고 작동
      • 게임 서버에서 경매장을 담당하는 처리를 분리, 경매장 처리만 담당하는 서버를 개발
    • 분산 처리를 할 수 있는 범위가 제한적

      • 분산의 양은 기능을 쪼갠 만큼만 할 수 있음
      • 수평 분산 처리보다 효율이 떨어짐
    • 최후의 수단. 수평 분산 처리 할 수 있는 방법을 최대한 찾아본 후 적용

9. 분산 처리를 엄선해야 하는 이유
  • 디버깅이 까다롭다
  • 운영체제가 해야 하는 일을 불필요하게 증가시킨다
  • 클라우드 서버 환경에서는 클라우드 서버 인스턴스 간에 통신 회선의 신뢰성도 문제가 된다
  • 분산 처리는 꼭 해야 하는 이유를 설명할 수 없다면 피하는 것이 좋다
10.분산 처리 전략
  • 성능 분석을 하여 분산 처리가 필요한 지점을 엄선
  • 데이터 응집력을 확인. 상호 작용이 높은 것은 분산하지 말고, 상호 작용이 적은 것들만 분산
  • 지리적 구분, 플레이어 레벨이나 등급에 따라 분산
  • 분산 처리 방식은 동기 분산 처리, 비동기 분산 처리, 데이터 동기화에 기반을 둔 로컬 처리
  • 불필요한 분산은 하지 마라
11. 분산 서버의 또 다른 장점
  • 확장성뿐만 아니라 안정성에도 효과를 줌
12. 고가용성
  • 사용자가 항상 서비스를 이용할 수 있게 하는 것

    • 사용자 입장에서는 논리적으로 서버 한 대처럼 보이지만, 이 서버의 정체는 컴퓨터 여러 대로 구성된 서버 클러스터
    • 이 서버 중 몇몇 서버가 과부하가 걸리거나 오작동으로 정지하더라도, 나머지 서버가 계속해서 사용자 요청을 처리할 수 있음
  • 장애 극복

    • 서버 클러스터에 있는 서버 중 하나가 죽었을 때, 다른 서버가 죽은 서버를 대신해서 일을 하고 그동안 죽은 서버가 다시 살아나는 것
  • 다중화 / 이중화

    • 장애 극복을 위해서 필요 이상의 서버를 둔다

    • 액티브-패시브 패턴

      • 액티브 서버만 클라이언트 요청을 전담
      • 패시브 서버는 필요할 때 액티브 서버의 데이터를 지속적으로 복제
      • 전담하던 액티브 서버가 죽으면 패시브 서버는 액티브 서버로 승격. 클라이언트 요청을 처리
      • 죽었던 액티브 서버가 다시 살아나면 이 서버는 패시브 서버가 된다
      • 패시브 서버는 그저 백업 역할만 할 뿐 다른 하는 일이 없다. 자원의 낭비로 이어짐
    • 액티브-액티브 패턴

      • 서버 두 대가 클라이언트 측 요청을 분담하여 처리하고, 필요할 때면 두 서버는 각자 가진 데이터를 상대방에게 전송한다
      • 두 서버의 상태가 서로 동기화 된다
      • 경우에 따라 데이터 스테일 문제를 해결하려는 노력이 필요하다
      • 두 액티브 서버 사이에 메모리 저장소 역할을 담당하는 서버를 두면 해결
      • 데이터에 접근할 때마다 기기 간 통신이 발생한다는 단점이 있다
      • 메모리 저장소 서버가 죽는 경우를 해결하지 못한다
      • 메모리 저장소 서버도 이중화를 해야한다
13. 데이터베이스의 분산
  • 게임 서버가 분산 처리되어 있다고 해도 데이터베이스를 분산 처리 하지 않으면 서비스 가용성은 떨어진다

  • 파티셔닝

    • 더 많은 사용자를 처리하고자 데이터베이스가 수평 확장을 할 때는 갖고 있는 레코드를 서로 다른 데이터베이스에 나누는 것
  • 수직 파티셔닝

    • 레코드 일부 필드를 다른 테이블로 나누어 놓고 그것을 다른 데이터베이스 서버에 두는 것
  • 데이터베이스도 고가용성을 위해 이중화를 한다. 같은 내용의 레코드를 서버 두 대 이상에 저장하는 것

'게임 서버 프로그래밍 > 게임 서버 프로그래밍 교과서' 카테고리의 다른 글

#10. 분산 서버 구조 사례  (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