카운터 기능 사용하기

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

카운터 읽기 & 쓰기

카운터 쓰기

다음의 인터페이스를 이용해 카운터 값을 씁니다.

void UpdateCounter(const string &counter_type,
                  const string &counter_path,
                  const string &description,
                  const int32_t &value);
void UpdateCounter(const string &counter_type,
                  const string &counter_path,
                  const string &description,
                  const int64_t &value);
void UpdateCounter(const string &counter_type,
                  const string &counter_path,
                  const string &description,
                  const double &value);
void UpdateCounter(const string &counter_type,
                  const string &counter_path,
                  const string &description,
                  const string &value);
void IncreaseCounterBy(const string &counter_type,
                      const string &counter_path,
                      const int32_t &value);
void IncreaseCounterBy(const string &counter_type,
                      const string &counter_path,
                      const int64_t &value);
void IncreaseCounterBy(const string &counter_type,
                      const string &counter_path,
                      const double &value);
void DecreaseCounterBy(const string &counter_type,
                      const string &counter_path,
                      const int32_t &value);
void DecreaseCounterBy(const string &counter_type,
                      const string &counter_path,
                      const int64_t &value);
void DecreaseCounterBy(const string &counter_type,
                      const string &counter_path,
                      const double &value);
public static class Counter
{
  public static void Update(string type, string path, Int32 value, string description = null);

  public static void Update(string type, string path, Int64 value, string description = null);

  public static void Update(string type, string path, double value, string description = null);

  public static void Update(string type, string path, string value, string description = null);

  public static void Increase(string type, string path, Int32 value);

  public static void Increase(string type, string path, Int64 value);

  public static void Increase(string type, string path, double value);

  public static void Decrease(string type, string path, Int32 value);

  public static void Decrease(string type, string path, Int64 value);

  public static void Decrease(string type, string path, double value);
}

Note

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

카운터 읽기

만들어진 카운터는 서버 안에서 Integer, String, Double 형태로 읽을 수 있습니다.

int64_t ReadCounterAsInteger(const string &counter_type, const string &counter_path);
double ReadCounterAsDouble(const string &counter_type, const string &counter_path);
string ReadCounterAsString(const string &counter_type, const string &counter_path);
public static class Counter
{
  public static Int64 ReadInteger(string type, string path);

  public static double ReadDouble(string type, string path);

  public static string ReadString(string type, string path);
}

또는 모든 카운터들의 값을 Json 형태로 읽을 수 있습니다.

Json ReadAllCounters();
public static class Counter
{
  public static JObject ReadAll();
}

외부에서 카운터를 읽는 것은 외부에서 REST 형태로 카운터 읽기 를 참고하세요.

카운터 사용 예제

 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);
}

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

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

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

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

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

전체 카운터 카테고리의 모든 카운터 목록의 값과 설명을 읽어옵니다.

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

카운터 변화 감지하기

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

void MonitorCounter(const string &counter_type,
                    const string &counter_path,
                    double threshold);
public static class Counter
{
  public static void Monitor (string type, string path, double threshold);
}

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

카운터 변화 감지 예제

 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

변화 감지 로그 출력 주기

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

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

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

Event

  • event_queue_length: 처리 대기중인 이벤트가 쌓여 있는 이벤트 큐의 길이가 지정된 threshold 값을 넘었는지 모니터링합니다.

ORM

  • outstanding_fetch_query: 처리 대기중인 오브젝트 가져오기 관련 쿼리 개수가 지정된 threshold 값을 넘었는지 모니터링합니다.

  • outstanding_update_query: 처리 대기중인 오브젝트 변경하기 관련 쿼리 개수가 지정된 threshold 값을 넘었는지 모니터링합니다.

Tip

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

기본 제공되는 카운터 목록

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

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

카운터가 갱신되는 주기(초)입니다.

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 타입 종류입니다.

funapi

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

Counter 이름

설명

concurrent_user

서버에 접속 중인 유저 수입니다. AccountManager 를 이용해 로그인 처리를 해야합니다.자세한 내용은 클라이언트와 아이펀 세션 연동 / 해제 (로그인 / 로그아웃) 를 참고해주세요.

sessions

서버에 접속 중인 세션 수입니다.

sessions.by-tag.<tagname>

<tagname> 으로 태깅한 서버에 접속 중인 세션 수입니다.

object

서버에 캐싱되어있는 오브젝트 수, 처리 대기 중인 읽기 / 쓰기 쿼리 수를 조회합니다.

object_database_stat

각 데이터베이스 별 오브젝트 관련 쿼리 처리 시간 통계 입니다. 자세한 내용은 (고급) ORM 프로파일링 을 참고해주세요.

rpc_stat

RPC 성능 통계입니다.

zookeeper_stat

오브젝트 공유를 위해 사용되는 Zookeeper 처리 시간 통계입니다. 자세한 내용은 Zookeeper 처리 프로파일링 을 통해 참고해주세요.

event/performance/queue

서버의 이벤트 유입량, 처리량, 대기 중인 이벤트 수 등을 조회합니다.

event/profiling/all

이벤트 별 처리 시간 통계입니다. 자세한 내용은 이벤트 프로파일링: 상세 정보 을 통해 참고해주세요.

event/profiling/summary

이벤트 전체 처리 시간 통계입니다. 자세한 내용은 이벤트 프로파일링: 요약 정보 을 통해 참고해주세요.

event/profiling/outstanding

처리 중인 이벤트 통계입니다.

event/profiling/reset

이벤트 프로파일링을 리셋합니다.

funapi_object_model

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

(고급) Callback 형 카운터

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

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

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

typedef boost::function<
    http::StatusCode(const string &, const string &, Json *)> CounterCallback;

void RegisterCallableCounter(const string &counter_type,
                             const string &counter_path,
                             const string &desc,
                             const CounterCallback &value_cb);
public static class Counter
{
  public delegate http.StatusCode Callback (string type, string path, JObject output);

  public static void RegisterCallableCounter (string type, string path, string description, Callback callback);
}

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_path,   // "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_path,   // "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() 가 호출되어 평균값을 계산해서 반환하게 됩니다.

카운터의 설정 파라미터

  • 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)