서버 관리 Part 1: RESTful APIs 추가¶
아이펀 엔진은 game server 내에 손쉽게 RESTful API 를 구현할 수 있는 기능을 제공합니다. 이 기능을 이용하면 HTTP 프로토콜을 이용하여 쉽게 다양한 외부 시스템과 연동할 수 있습니다.
REST API URL 과 핸들러 등록¶
아래의 함수를 이용하여 특정 URL 또는 URL Pattern 과 이를 처리하는 핸들러를 등록할 수 있습니다.
static void ApiService::RegisterHandler(const http::Method &method,
const boost::regex &path_pattern,
const Handler &handler)
static void ApiService::RegisterHandler2(const http::Method &method,
const boost::regex &path_pattern,
const Handler2 &handler)
static void ApiService::RegisterHandler3 (const http::Method &method,
const boost::regex &path_pattern,
const Handler3 &handler)
public static void ApiService.RegisterHandler (http.Method method,
string path,
Handler handler)
public static void ApiService.RegisterHandler (http.Method method,
string path,
AsyncHandler handler)
public static void ApiService.RegisterHandler (http.Method method,
Regex path_pattern,
HandlerForRegexPath handler)
public static void ApiService.RegisterHandler (http.Method method,
Regex path_pattern,
AsyncHandlerForRegexPath handler)
method:
http::kGet
,http::kPut
,http::kPost
,http::kDelete
,http::kHead
.path: 처리하려는 URL 문자열. 특정 URL 하나만을 지칭할 때 사용됨.
path_pattern: 처리하려는 URL pattern.
(?<NAME>pattern)
형태로 URL 에 parameter 를 포함하고 코드에서NAME
으로 읽을 수 있습니다. (아래 예제 참고)handler: 해당 URL 을 처리하는 handler 함수.
namespace http {
// HTTP GET 메소드의 인자 목록.
typedef std::map<string, string, impl::util::CaseInsensitiveLess> GetParameter;
// 중복되는 key 를 허용하는 HTTP GET 메소드의 인자 목록.
typedef std::map<string, std::vector<string>,
impl::util::CaseInsensitiveLess> GetParameter2;
// HTTP GET 으로 넘어오는 인자 중에 중복되는 key 를 허용하지 않는 경우 요청 객체
struct FUNAPI_DLL_VISIBILITY Request {
Method method; // HTTP 메소드
string request_ip; // Request IP
string request_uri; // Request URI
Version version; // HTTP 버전
GetParameter get_parameter; // GET 메소드인 경우 인자 목록
Header header; // Header 필드
string body; // Request 바디
};
// HTTP GET 으로 넘어오는 인자 중에 중복되는 key 를 허용하는 경우 요청 객체
struct FUNAPI_DLL_VISIBILITY Request2 {
Method method; // HTTP 메소드
string request_ip; // Request IP
string request_uri; // Request URI
Version version; // HTTP 버전
GetParameter2 get_parameter; // GET 메소드인 경우 인자 목록
Header header; // Header 필드
string body; // Request 바디
};
struct FUNAPI_DLL_VISIBILITY Response {
Version version; // HTTP 버전
uint32_t status_code; // 응답 코드
string status_message; // 응답 코드에 해당하는 메시지
Header header; // Header 필드
string body; // Response 바디
};
}
typedef boost::function<
void(const Ptr<http::Response> &)>
ResponseWriter ASSERT_NO_ROLLBACK;
typedef boost::function<
void(Ptr<http::Response> ret,
const http::Request &request,
const MatchResult ¶ms)> Handler;
typedef boost::function<
void(Ptr<http::Response> ret,
const http::Request2 &request,
const MatchResult ¶ms)> Handler2;
typedef boost::function<
void(const http::Request2 &request,
const MatchResult ¶ms,
const ResponseWriter &finisher)> Handler3;
namespace http
{
public struct Request
{
public Method method; // HTTP 메소드
public string path; // Request 경로
public Version version; // HTTP 버전
public NameValueCollection query; // GET 메소드인 경우 인자 목록
public NameValueCollection header; // Header 필드
public string body; // Request 바디
}
public struct Response
{
public Version version; // HTTP 버전
public StatusCode status_code; // 응답 코드
public string status_message; // 응답 코드에 해당하는 메시지
public NameValueCollection header; // Header 필드
public string body; // Response 바디
}
}
public delegate void ResponseWriter(http.Response response);
public delegate http.Response Handler (http.Request request);
public delegate void AsyncHandler (http.Request request,
ResponseWriter response_writer);
public delegate http.Response HandlerForRegexPath (http.Request request,
MatchCollection matches);
public delegate void AsyncHandlerForRegexPath (http.Request request,
MatchCollection matches,
ResponseWriter response_writer);
Important
만약 HTTP 응답을 바로 보내지 않고 비동기적으로 처리해야 한다면 (다른 처리를 비동기로 진행한 후에 HTTP 응답을 보내고 싶다면) 아래 함수로 핸들러를 등록하시면 비동기로 처리 할 수 있습니다.
static void ApiService::RegisterHandler3 (const http::Method &method,
const boost::regex &path_pattern,
const Handler3 &handler)
public static void ApiService.RegisterHandler (http.Method method,
string path,
AsyncHandler handler)
public static void ApiService.RegisterHandler (http.Method method,
Regex path_pattern,
AsyncHandlerForRegexPath handler)
핸들러 작성¶
handler 함수는 아래와 같이 작성할 수 있습니다.
HTTP GET 으로 넘어오는 인자 중에 중복되는 key 를 허용하지 않는 경우
void MyHandler( Ptr<http::Response> response, const http::Request &request, const ApiService::MatchResult ¶ms) { response->status_code = http::kOk; response->body = ... }
HTTP GET 으로 넘어오는 인자 중에 중복되는 key 를 허용하는 경우
void MyHandler2( Ptr<http::Response> response, const http::Request2 &request, const ApiService::MatchResult ¶ms) { response->status_code = http::kOk; response->body = ... }
Response body 를 비동기로 생성하는 경우 (HTTP GET 의 인자는 중복 key 를 허용)
void MyHandler3( const http::Request2 &request, const ApiService::MatchResult ¶ms, const ApiService::ResponseWriter &finisher) { some_async_func(finisher); } void some_async_func(const ApiService::ResponseWriter &finisher) { Ptr<http::Response> response(new http::Response); finisher(response); }
단일 URL 을 동기식으로 처리
funapi.http.Response MySyncHandler1(funapi.http.Request request) { funapi.http.Response response = new funapi.http.Response (); response.status_code = funapi.http.StatusCode.kOk; response.body = ... return response; }
URL 이 pattern 으로 주어졌을 때 동기식으로 처리
funapi.http.Response MySyncHandler2(funapi.http.Request request, MatchCollection matches) { funapi.http.Response response = new funapi.http.Response (); response.status_code = funapi.http.StatusCode.kOk; response.body = ... return response; }
단일 URL 을 비동기식으로 처리
void MyAsyncHandler1(funapi.http.Request request, ResponseWriter finisher) { some_async_func(finisher) } void some_async_func(ResponseWriter finisher) { funapi.http.Response response = new funapi.http.Response (); response.status_code = funapi.http.StatusCode.kOk; response.body = ... finisher(response); }
URL 이 pattern 으로 주어졌을 때 비동기식으로 처리
void MyAsyncHandler2(funapi.http.Request request, MatchCollection matches, ResponseWriter finisher) { some_async_func(finisher) } void some_async_func(ResponseWriter finisher) { funapi.http.Response response = new funapi.http.Response (); response.status_code = funapi.http.StatusCode.kOk; response.body = ... finisher(response); }
API Service 등록 예제¶
아래는 다음과 같은 두 개의 API 를 등록하는 예제입니다.
-
GET
/example/hello
¶
-
GET
/example/hello2/*/*
¶
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 26 27 28 29 30 31 32 33 34 35 36 37 38 | // request: http://localhost:8014/example/hello
// response: hello!
void OnHello(Ptr<http::Response> response,
const http::Request &request,
const ApiService::MatchResult ¶ms) {
response->status_code = http::kOk;
response->header.insert(std::make_pair("Content-Type", "text/plain"));
response->body = "hello!";
}
// request: http://localhost:8014/example/hello2/bob/24
// response:
// {
// "name": "bob",
// "age": "24"
// }
void OnHello2(Ptr<http::Response> response,
const http::Request &request,
const ApiService::MatchResult ¶ms) {
Json msg;
msg["name"] = params["name"];
msg["age"] = params["age"];
response->status_code = http::kOk;
response->header.insert(std::make_pair("Content-Type", "application/json"));
response->body = msg.ToString();
}
static bool Install(const ArgumentMap &arguments) {
...
ApiService::RegisterHandler(
http::kGet, boost::regex("/example/hello"), OnHello);
ApiService::RegisterHandler(http::kGet,
boost::regex("/example/hello2/(?<name>\\w+)/(?<age>\\w+)/"), OnHello2);
...
}
|
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 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 | using System.Text.RegularExpressions;
using System.Collections.Specialized;
using funapi;
// request: http://localhost:8014/example/hello
// response: hello!
public static funapi.http.Response OnHello(funapi.http.Request request)
{
funapi.http.Response response = new funapi.http.Response ();
response.status_code = funapi.http.StatusCode.kOk;
response.body = "hello"; // response body
return response;
}
// request: http://localhost:8014/example/hello2/bob/24
// response:
// {
// "name": "bob",
// "age": "24"
// }
public static funapi.http.Response OnHello2(
funapi.http.Request request,
MatchCollection collection)
{
funapi.http.Response response = new funapi.http.Response ();
response.header = new NameValueCollection ();
response.header.Add ("Content-Type", "application/json");
response.status_code = funapi.http.StatusCode.kOk;
JObject msg = new JObject ();
GroupCollection groups = collection [0]. Groups;
msg ["name"] = groups ["name"].Value;
msg ["age"] = groups ["age"].Value;
response.body = msg.ToString ();
return response;
}
public static void Install(ArgumentMap arguments)
{
...
ApiService.RegisterHandler (
funapi.http.Method.kGet, "/example/hello", OnHello);
ApiService.RegisterHandler (
funapi.http.Method.kGet,
new Regex("/example/hello2/(?<name>\\w+)/(?<age>\\w+)/"),
OnHello2);
...
}
|
ApiService 기능 설정 파라미터¶
api_service_port: 관리용 RESTful API 서비스를 돌리기 위한 로컬 TCP 포트 번호. (type=uint64, default=8015)
api_service_logging_level: 관리용 RESTful API 메시지들의 로그 레벨. 0 은 로그를 남기지 않음. 1은 에러인 경우만 남김. 2는 모든 요청에 대해 로그를 남김 (type=uint64, default=2)
직접 설정을 바꿀 일이 거의 없는 설정들
api_service_event_tags_size: 관리용 RESTful API 서비스를 동시에 돌리는 개수 (type=uint64, default=1)