6. 분산 시스템 기능으로 서버 여러대 띄우기

이번 챕터에서는 앞의 Flavor 기능을 이용해 서버 기능 분리하기 챕터에서 설명한 분산 시스템 기능을 사용하도록 설정해서 로비 Flavor게임 Flavor 를 동시에 실행하고, 분산 시스템 기능의 동작을 클라이언트 중복 로그인 상황을 통해서 설명하겠습니다.

6.1. 분산 기능 켜기

먼저, 분산 시스템 기능을 사용하도록 각 서버의 설정을 변경해 보겠습니다.

동시에 튜토리얼 환경에서는 하나의 서버 호스트에 여러 서버 인스턴스를 띄우기 때문에 서버들끼리 사용하는 포트가 중복되면 서버가 실행되지 않습니다.

따라서 게임 Flavor 는 사용하는 포트도 함께 변경하겠습니다.

hello_world-source/src/MANIFEST.game.json 을 열어서 다음 항목들을 수정 해 주세요.

"SessionService": {
  ...
  "tcp_json_port": 9012,
  ...
  "http_json_port": 9018,
  ...
},

"ApiService": {
  "api_service_port": 9014
},

"RpcService": {
  "rpc_enabled": true,
  "rpc_port": 9015,
  ...
},

로비 Flavor 는 포트를 변경하지 않아도 되므로 분산 기능만 켜면 됩니다. hello_world-source/src/MANIFEST.lobby.json 을 다음처럼 수정합니다.

...
"rpc_enabled": true,
...

끝으로 아이펀 엔진 의 분산 기능을 Redis 서버를 사용하기 때문에 Redis 서버가 설치 및 실행되고 있는지 확인 해 주시기 바랍니다.

$ sudo apt-get install redis-server

$ dpkg --list redis-server
ii  redis-server                  5:4.0.9-1ubuntu0.2  amd64               Persistent key-value database with network interface

$ sudo netstat -atnp | grep 6379
tcp        0      0 0.0.0.0:6379            0.0.0.0:*               LISTEN      1906/redis-server 0

만일, 외부 호스트에 있는 Redis 서버를 사용하고자 하는 경우에는 각 Flavor 의 MANIFEST.json 파일을 열어서 아래 설정을 외부 호스트의 IP 주소로 변경 해 주시기 바랍니다.

"RpcService":
  "rpc_redis_hosts": [
    {
      "host": "127.0.0.1:6379",
      "database": 0,
      "auth": ""
    }
  ]

6.2. 테스트용 클라이언트 패킷 추가하기

저희가 로비 서버에 로그인 한 사용자가 게임 서버에서 이를 확인할 수 있음을 테스트해보기 위해서 signin 이라는 클라이언트 패킷과 check 라는 클라이언트 패킷을 추가해보겠습니다. hello_world-src/src/event_handlers.cc 에 다음을 추가해주세요.

// 아래 함수는 ifun 이라는 유저를 로그인 처리합니다.
void OnSignin(const Ptr<Session> &session, const Json &message) {
  bool r = AccountManager::CheckAndSetLoggedIn("ifun", session);
  Json response;
  response["result"] = r;
  session->SendMessage("singin_reply", response);
}

// 아래 함수는 ifun 이라는 유저가 어느 서버에 로그인했는지 체크합니다.
// 만일 로그인 한 서버가 없으면 서버 아이디는 0000-00... 형태의 Null UUID 가 됩니다.
void OnCheck(const Ptr<Session> &session, const Json &message) {
  Rpc::PeerId server_id = AccountManager::Locate("ifun");
  Json response;
  response["result"] = boost::lexical_cast<string>(server_id);
  session->SendMessage("check_reply", response);
}


void RegisterEventHandlers() {
  ...
  // 두 패킷 핸들러를 등록합니다.
  HandlerRegistry::Register("signin", OnSignin);
  HandlerRegistry::Register("check", OnCheck);
  ...
}

자 이제 준비가 다 되었습니다. 로비 서버는 8018 번 포트, 게임 서버는 9018 번 포트로 사용되게 되었습니다. 각각의 script 로 이들을 실행합니다.

# 한 터미널에서
$ ./hello_world.lobby-local
# 다른 터미널에서
$ ./hello_world.game-local

6.3. 테스트 해보기

자, 이제 8018 번 로비 서버에 signin 을 보내서 ifun 을 로그인하겠습니다.

$ wget -qO- --post-data="{}" http://localhost:8018/v1/messages/signin
{"result":true,"_sid":"497844e8-1885-40ed-b7d7-29f2931ddac2","_msgtype":"singin_reply"}

기대한대로 결과가 true 네요. 중복 로그인을 시도해볼까요?

$ wget -qO- --post-data="{}" http://localhost:8018/v1/messages/signin
{"result":false,"_sid":"f0a511c0-ad16-4b1b-917f-f86da7809814","_msgtype":"singin_reply"}

이미 로그인되었으므로 결과가 false 입니다. 9018 번 게임 서버에 signin 을 보내면 어떨까요?

$ wget -qO- --post-data="{}" http://localhost:9018/v1/messages/signin
{"result":false,"_sid":"52a48a4a-58ef-462c-a08b-92371d298382","_msgtype":"singin_reply"}

예상대로 이미 로그인한 것으로 인지됩니다. 그럼 각각 어느 서버에 로그인했다고 알고 있을까요? 로비 서버와 게임 서버에 각각 check 요청을 보내보겠습니다.

$ wget -qO- --post-data="{}" http://localhost:8018/v1/messages/check
{"result":"aa561332-8aad-4f1f-0000-000000000000","_sid":"622e4ae2-3793-41ac-97ab-708840416a6d","_msgtype":"check_reply"}

$ wget -qO- --post-data="{}" http://localhost:9018/v1/messages/check
{"result":"aa561332-8aad-4f1f-0000-000000000000","_sid":"8bff8116-5583-40f7-b363-c18929ac9400","_msgtype":"check_reply"}

응답의 result 값이 같은 것을 뽈 때, 둘 다 ifun 이 같은 서버에 있는 것으로 인식하고 있네요. 로비 서버와 게임 서버간에 정보가 잘 공유되고 있다는 뜻입니다.

이제 분산 시스템으로 서비스 확장까지 했습니다. 다음 챕터에서는 게임 서버 프로그램의 패키지 관리와 관리용 RESTful API 를 추가해보겠습니다.