MessagePack RPC フレームワークの設計案
プロトコルにMessagePackを、IO戦略にmp::iothreadsを使ったRPCフレームワークの設計案。RPCのメソッドと、それに対応するコールバック関数の関連づけをリンク時に行う。google-gflagsと同じ戦略。
プロトコルは以下の3種類:
- Request
- [true, メソッドID, 引数, リクエストID] 型は[true, 8bit整数, 何か, 32bit整数]のtuple。典型的には引数はtuple。
- Response
- [false, 返り値, エラー, リクエストID] 型は[false, 何か, 何か, 32bit整数]のtuple。返り値とエラーのどちらかはnilにする。
- Notification
- [true, メソッドID, 引数, nil] 型は[true, 8bit整数, 何か, nil]のtuple。
非同期プロトコルで、リクエストの順番通りにレスポンスを返さなくても良い。その代わりレスポンスには「どのリクエストに対するレスポンスか」を示すID(リクエストID)を含める。Notificationはレスポンスを受け取らないリクエスト。
このプロトコルは JSON-RPC とほぼ同じ。JSON-RPC では 連想配列を使っているところを tuple にしている。tuple の方が速い。MessagePackでは要素が16個までのtupleはオーバーヘッドが1バイトしかないので濫用して良い。
#include <mp/rpc.h> #include <msgpack.hpp> // これは実装済み@MessagePack namespace protocol { using msgpack::define; using namespace msgpack::type; struct Get : define< tuple<raw_ref, std::vector<raw_ref, raw_ref>> > { raw_ref& path() { return get<0>(); } std::vector& header() { return get<1>(); } }; struct Post : define< tuple<raw_ref, std::vector<raw_ref, raw_ref>, raw_ref> > { raw_ref& path() { return get<0>(); } std::vector& header() { return get<1>(); } raw_ref& body() { return get<2>(); } }; } // これはただのクラス class Server { public: Server(); void Get (protocol::Get& request, mp::rpc::responder* res, std::auto_ptr<mp::zone>& objlife); void Post(protocol::Post& request, mp::rpc::responder* res, std::auto_ptr<mp::zone>& objlife); }; // これは案 MP_RPC_PROTOCOL(1, protocol::Get, Server, &Server::Get); MP_RPC_PROTOCOL(2, protocol::Post, Server, &server::Post); int main(int argc, char* argv[]) { // mainはこれだけ google::ParseCommandLineFlags(&argc, &argv, true); mp::rpc::run(); return 0; } // あとはServerクラスを実装する Server::Server() { } // ここで初期化処理 void Server::Get (protocol::Get& request, mp::rpc::responder* res, std::auto_ptr<mp::zone>& objlife) { std::string body("foobar"); res->result(body); } void Server::Post(protocol::Post& request, mp::rpc::responder* res, std::auto_ptr<mp::zone>& objlife) { // RPC呼び出し protocol::Post param; mp::rpc::get_node("127.0.0.1:9000")->call(1, param, objlife, mp::bind(&Server::PostResponse, _1, _2)); // クロージャはbindで }