34. 서버 관리 Part 2: 서버 모니터링

34.1. Counter

아이펀 엔진은 게임 서버 내 Counter 를 통해서 노출합니다. 이를 이용하면 직접 외부 모니터링 툴을 만들 수 있습니다. 또한 아이펀 엔진은 counter 를 이용해 다양한 정보를 모니터링할 수 있는 아이펀 엔진 대시보드 를 제공하고 있습니다. (자세한 내용은 아이펀 엔진 대시보드 을 참고하세요.)

34.1.1. 카운터 읽기 & 쓰기

34.1.1.1. 카운터 쓰기

다음의 3 가지 함수를 이용해 카운터 값을 씁니다.

Note

별도의 등록 함수는 존재하지 않고, 위의 쓰기 함수를 호출하는 경우 카운터가 등록됩니다.

34.1.1.2. 카운터 읽기

만들어진 카운터는 서버 안에서 Integer, String, Double 형태로 읽을 수 있습니다. 외부에서 카운터를 읽는 것은 외부에서 REST 형태로 카운터 읽기 를 참고하세요.

34.1.1.3. 카운터 사용 예제

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
#include <funapi.h>

void example(){

  UpdateCounter("server", "item_count", 150);

  UpdateCounter("server", "monster_count", "The number of monsters", 150);

  IncreaseCounterBy("server", "item_count", 1);

  DecreaseCounterBy("server", "item_count", 1);

  int64_t item_count = ReadCounterAsInteger("server", "item_count");
  BOOST_ASSERT(item_count == 150);

  UpdateCounter("server", "connection_per_second", 77.7);

  UpdateCounter("billing", "purchase_per_second", 7.1);
}
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
using funapi;

public void Example()
{
  Counter.Update ("server", "item_count", 150);

  UpdateCounter("server", "monster_count", "The number of monsters", 150);

  Counter.Increase ("server", "item_count", 1);

  Counter.Decrease ("server", "item_count", 1);

  Int64 item_count = Counter.ReadInteger ("server", "item_count");

  Counter.Update ("server", "connection_per_second", 77.7);

  Counter.Update ("billing", "purchase_per_second", 7.1);
}

34.1.1.4. 외부에서 REST 형태로 카운터 읽기

위에서 정의된 counter 는 다음과 같은 방식으로 접근 가능합니다.

GET http://localhost:8014/v1/counters/

전체 카운터 카테고리 목록을 불러옵니다.

GET http://localhost:8014/v1/counters/funapi/

예약되어 있는 카운터 카테고리인 아이펀 엔진 카테고리 안의 카운터 목록을 보여줍니다.

GET http://localhost:8014/v1/counters/server/item_count/

프로그래머가 만든 server 카테고리 안의 item_count 라는 카운터의 값을 읽어옵니다. 위의 예제에서는 150 가 됩니다.

GET http://localhost:8014/v1/counters/billing/purchase_per_second/

프로그래머가 만든 billing 카테고리 안의 perchase_per_second 라는 카운터 값을 읽어옵니다. 위의 예제에서는 7.1 이 됩니다.

GET http://localhost:8014/v1/counters/server/monster_count/description/

프로그래머가 만든 server 카테고리 안의 monster_count 라는 카운터의 설명을 읽어옵니다. 위의 예제에서는 “The number of monsters” 가 됩니다.

Tip

Counter 에 설명을 붙이면, 외부 시스템과 연동할 때 외부 작업자에게 보다 명확한 의미를 전달할 수 있습니다.

34.1.2. 카운터 변화 감지하기

IntegerDouble 타입의 카운터의 경우 카운터의 값이 특정값 이상일 때 로그를 출력할 수 있습니다. 이를 이용하면 게임 내 재화의 급격한 변화 등을 감지하는데 도움이 됩니다.

아래는 매 시간 동안 유저의 골드 수집량을 Counter 로 계산하고, 10만이 넘을 경우 경고 메시지를 내는 예제입니다.

34.1.2.1. 카운터 변화 감지 예제

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
void OnResetGoldCounterTimerExpired(const Timer::Id &, const WallClock::Value &) {
  UpdateCounter("game", "gold_per_hour", 0);
}

void Install() {
  UpdateCounter("game", "gold_per_hour", 0);

  MonitorCounter("game", "gold_per_hour", 100000);

  Timer::ExpireRepeatedly(WallClock::FromSec(3600), OnResetGoldCounterTimerExpired);
}


// Assume this function is called when user pick gold.
void PickGold(int64_t gold) {
  IncreaseCounterBy("game", "gold_per_hour", gold);
}
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
void OnResetGoldCounterTimerExpired(UInt64 timer_id, DateTime clock)
{
  Counter.Update ("game", "gold_per_hour", 0);
}

void Install()
{
  Counter.Update ("game", "gold_per_hour", 0);

  Counter.Monitor ("game", "gold_per_hour", 100000);

  Timer.ExpireRepeatedly (WallClock.FromSec (3600), OnResetGoldCounterTimerExpired);
}

// Assume this function is called when user pick gold.
void PickGold(UInt64 gold)
{
  Counter.Increase ("game", "gold_per_hour", gold);
}

Note

모니터를 등록하기 위해서는 예제에서와 같이 UpdateCounter(), IncreaseCounterBy(), DecreaseCounterBy() 함수 등을 호출하여 카운터를 미리 등록해두어야 합니다.

이제 시간당 골드량이 10만 골드를 넘어서면 다음과 같은 로그가 출력됩니다.

W0818 11:03:06.520730 18324 counter.cc:160] The 'gold_per_hour of game' counter exceeded threshold: value=123456, threshold=100000

34.1.2.2. 변화 감지 로그 출력 주기

지나치게 많은 로그를 생성하는 것을 방지 하기 위해 MonitorCounter() 함수에 입력한 threshold 값을 넘는다고 매번 로그를 출력하지는 않습니다. 그 대신 카운터의 설정 파라미터counter_monitoring_interval_in_sec 에 입력된 초마다 카운터값을 확인하고 로그를 출력합니다.

34.1.2.3. 엔진에서 기본적으로 변화를 감지하는 카운터

엔진에서 기본적으로 모니터링하는 카운터들은 다음과 같습니다.

34.1.2.3.1. Event
  • event_queue_length: 처리 대기중인 이벤트가 쌓여 있는 이벤트 큐의 길이가 지정된 threshold 값을 넘었는지 모니터링합니다.
34.1.2.3.2. ORM
  • outstanding_fetch_query: 처리 대기중인 오브젝트 가져오기 관련 쿼리 개수가 지정된 threshold 값을 넘었는지 모니터링합니다.
  • outstanding_update_query: 처리 대기중인 오브젝트 변경하기 관련 쿼리 개수가 지정된 threshold 값을 넘었는지 모니터링합니다.

Tip

이 카운터들의 경고 threshold 는 카운터의 설정 파라미터 에서 변경할 수 있습니다.

34.1.3. 기본 제공되는 카운터 목록

다음은 기본적으로 엔진에서 제공하는 카운터 목록입니다.

34.1.3.1. process

실행 중인 아이펀 엔진의 프로세스 정보를 조회하는 카운터입니다.

Counter 이름 설명
vsz 프로세스가 사용하는 가상 메모리 크기입니다.
cpu 프로세스가 사용하는 cpu 사용량입니다.
nivcsw 컨텍스트 스위칭 이전에 작업이 끝나지 않은 횟수입니다.
nswap 현재는 사용되지 않는 필드입니다.
oublock 파일 시스템을 출력한 수입니다.
minflt I/O 없이 일어난 페이지 폴트 수입니다.
idrss 공유되지 않은 rss 사이즈입니다.
isrss 공유되지 않은 스택 사이즈입니다.
ixrss 공유되는 rss 메모리 사이즈입니다.
nsignals 응답받은 시그널 수입니다.
majflt I/O 에 의해 일어난 페이지 폴트 수입니다.
maxrss 최대로 사용했던 rss 값입니다.
msgsnd IPC 메시지 전송 수입니다.
msgrcv IPC 메시지 응답 수입니다.
nvcsw 컨텍스트 스위칭 이전에 작업이 끝난 횟수입니다.
stime 프로세스가 커널모드로 실행된 총 시간입니다.
updated 마지막으로 업데이트 된 UTC 시간입니다.
utime 프로세스가 유저 모드로 실행된 총 시간입니다.
inblock 파일 시스템에 입력한 수입니다.
refresh_interval 카운터가 갱신되는 주기(초)입니다.

34.1.3.2. os

서버 os의 정보를 조회하는 카운터입니다.

Counter 이름 설명
procs 실행중인 전체 프로세스 수입니다.
freeswap 사용 가능 한 메모리 크기(byte)입니다.
bufferram 버퍼로 쓰이는 메모리 크기(byte)입니다.
load15 15분 동안의 load average 입니다.
totalswap 전체 메모리 크기(byte) 입니다.
load5 5분 동안의 load average 입니다.
load1 1분 동안의 load average 입니다.
updated 마지막으로 업데이트 된 UTC 시간입니다.
uptime 서버의 부팅시간부터 현재까지의 시간(초)입니다.
freeram 여유 램 크기(byte)입니다.
cpus cpu 코어 수 입니다.
totalram 전체 램 크기(byte)입니다.
refresh_interval 카운터가 갱신되는 주기(초)입니다.
sharedram 전체 공유 메모리 크기(byte)입니다.
type os 타입 종류입니다.

34.1.3.3. funapi

서버의 엔진 정보를 조회합니다.

Counter 이름 설명
concurrent_user 서버에 접속 중인 유저 수입니다. AccountManager 를 이용해 로그인 처리를 해야합니다.자세한 내용은 클라이언트가 접속한 서버 찾기 를 참고해주세요.
sessions 서버에 접속 중인 세션 수입니다.
event 서버의 이벤트 유입량, 처리량, 대기 중인 이벤트 수 등을 조회합니다.
outstanding_event_profiling 처리 중인 이벤트 통계입니다.
object_database_stat 각 데이터베이스 별 오브젝트 관련 쿼리 처리 시간 통계 입니다. 자세한 내용은 (고급) ORM 프로파일링 을 참고해주세요.
reset_event_profiling 이벤트 프로파일링을 리셋합니다.
object 서버에 캐싱되어있는 오브젝트 수, 처리 대기 중인 읽기 / 쓰기 쿼리 수를 조회합니다.
event_profiling_summary 이벤트 전체 처리 시간 통계입니다. 자세한 내용은 이벤트 프로파일링: 요약 정보 을 통해 참고해주세요.
event_profiling 이벤트 별 처리 시간 통계입니다. 자세한 내용은 이벤트 프로파일링: 상세 정보 을 통해 참고해주세요.
rpc_stat RPC 성능 통계입니다.
zookeeper_stat 오브젝트 공유를 위해 사용되는 Zookeeper 처리 시간 통계입니다. 자세한 내용은 Zookeeper 처리 프로파일링 을 통해 참고해주세요.

34.1.3.4. funapi_object_model

서버에 캐싱 되어있는 오브젝트 모델별 수를 조회합니다.

34.1.4. (고급) Callback 형 카운터

숫자 값을 단순히 등록하고 반환하는 것 외에, 카운터 값을 계산하기 위한 추가적인 연산을 수행하거나 복수개의 값을 반환해야되는 경우가 있을 수 있습니다.

가령 평균값을 반환해야되는 경우, 보통 내부적으로 합과 갯수를 계산하다가 평균이 필요해지는 경우 합을 갯수로 나눈 값을 반환하는게 일반적입니다.

이런 연산을 위해서 counter 에 callback 을 등록할 수 있습니다.

34.1.4.1. Callback 형 카운터 사용 예제

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
#include <funapi.h>

http::StatusCode OnAverageQueried(
    const string &counter_group,  // "server" when called
    const string &counter_name,   // "average_users_per_room" when called.
    Json *ret) {
   if (total_rooms == 0) {
     return http::kNoContent;
   }

   double average = total_users / total_rooms;
   ret->SetDouble(average);

   return http::kOk;
}


void example() {
  RegisterCallableCounter(
      "server",
      "average_users_per_room",
      "Returns the average number of users per game room",
      OnAverageQueried);
}
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
using funapi;

funapi.http.StatusCode OnAverageQueried(
    string counter_group,  // "server" when called
    string counter_name,   // "average_users_per_room" when called
    JObject ret)
{
  if (total_rooms == 0) {
    return funapi.http.StatusCode.kNoContent;
  }

  double average = total_users / total_rooms;
  ret->SetDouble(average);

  return funapi.http.StatusCode.kOk;
}

void Example()
{
  Counter.RegisterCallableCounter (
      "server",
      "average_users_per_room",
      "Returns the average number of users per game room",
      OnAverageQueried);
}
GET http://localhost:8014/v1/counters/server/average_users_per_room/

이 API 가 호출되면 OnAverageQueried() 가 호출되어 평균값을 계산해서 반환하게 됩니다.

34.1.5. 카운터의 설정 파라미터

  • counter_flush_interval_in_sec: 외부에 노출되는 counter 값을 주기적으로 갱신하는데, 이 시간 간격을 초단위로 지정 (type=uint64, default=0)

직접 설정을 바꿀 일이 거의 없는 설정들

  • counter_monitoring_interval_in_sec: 단순히 외부로 내보내는 것 외에 값들을 모니터링하는 초단위 시간 간격 (type=uint64, default=30)
  • warning_threshold_event_queue_length: 카운터 모니터링 시 이 값 이상으로 이벤트 큐가 길어지면 경고 메시지를 출력함 (type=uint64, default=3000)
  • warning_threshold_outstanding_fetch_query: 카운터 모니터링 시 이 값 이상으로 DB 읽기 큐가 길어지면 경고 메시지를 출력함 (type=uint64, default=5000)
  • warning_threshold_outstanding_update_query: 카운터 모니터링 시 이 값 이상으로 DB 쓰기 큐가 길어지면 경고 메시지를 출력함 (type=uint64, default=5000)
  • warning_threshold_slow_query_in_sec: 카운터 모니터링 시 DB 읽기 또는 쓰기에 소요되는 시간(단위: 초)이 기준 값보다 길어질 때 경고 메세지를 출력함(type=uint64, default=1)
  • warning_threshold_slow_distribution_in_sec: 카운터 모니터링 시 분산 처리에 사용되는 Zookeeper/Redis 처리 시간(단위: 초)이 기준 값보다 길어질 때 경고 메세지를 출력함(type=uint64, default=3)

34.2. 아이펀 엔진 대시보드

아이펀 엔진은 편리하게 게임 서버를 모니터링할 수 있는 별도의 대시보드 프로그램을 제공합니다. 이 대시 보드 프로그램은 Counter 를 활용하여 구현되어있습니다. 자세한 설명은 아이펀 엔진 대시보드 매뉴얼 를 참고해주세요.

아이펀 엔진 대시보드는 대표적으로 다음의 기능들을 제공합니다.

34.2.1. OS 수준의 리소스 사용량 모니터링

CPU, RAM, 접속 세션 수, 로그인 처리된 사용자 수들을 그래프로 표시합니다.

OS 수준의 리소스 사용량 모니터링

34.2.2. 아이펀 엔진 이벤트 처리 성능 모니터링

아이펀 엔진 내의 초당 이벤트 발생 건수, 이벤트 처리 속도, 이벤트 큐의 길이 변화를 그래프로 표시합니다.

아이펀 엔진 이벤트 처리 성능 모니터링

34.2.3. 아이펀 엔진 ORM 처리 성능 모니터링

각 DB (shard 가 되어있는 경우 shard DB) 의 읽기/쓰기 성능, 대기 중인 읽기/쓰기 요청 수, 메모리에 캐싱된 ORM 오브젝트 수를 그래프로 표시합니다.

아이펀 엔진 ORM 처리 성능 모니터링

34.2.4. 아이펀 엔진 분산처리 성능 모니터링

서버간 통신을 위해 사용한 RPC 트래픽에 대해서 그래프로 표시합니다.

아이펀 엔진 분산처리 성능 모니터링

34.2.5. Stage 별 처리 시간 및 Queue time 프로파일링

34.2.5.1. Overview

_images/dash-summary-1.png
_images/dash-summary-2.png

34.2.5.2. 테이블 형태

_images/dash-tabular.png

34.2.5.3. 실행 시간 분석

_images/dash-exec-time.png

34.2.5.4. 대기 시간 분석

_images/dash-wait-time.png