libkyototycoon をつかって 5分で C++ で RPC サーバーを書く方法
ちょっとした RPC サーバーを C/C++ でかきたいな、なんてケースはままあるわけですが、そんなときに便利なライブラリがあったので紹介します。
KyotoTycoon をつかうと、TSVRPC over HTTP な RPC サーバーが超簡単にかけて便利だった。libkyototycoon は GPL だけど、それが問題にならない場合なら、とてもいいとおもう。商用ライセンスかえるならそれもいいとおもう。
RPC サーバーとしては他にもいくつか実装があるんだけど、HTTP の上で実装されているから、デバッグが容易だったりとか、直接 telnet ではなしたりとか tcpdump できたりして、いろいろ便利なので、よいとおもう。
SConstruct でビルドルールをかいてから
e = Environment() e.Program('ktechoserver', ['ktechoserver.cc'], parse_flags='-lkyototycoon -lkyotocabinet')
コードをかいて
// LICENSE: GPLv2 #include <ktrpc.h> #include <string> namespace kt = kyototycoon; namespace kc = kyotocabinet; static kt::RPCServer *g_serv = NULL; // get the logger into the standard stream inline kt::RPCServer::Logger* stdlogger(const char* prefix, std::ostream* strm) { class LoggerImpl : public kt::RPCServer::Logger { public: explicit LoggerImpl(std::ostream* strm, const char* prefix) : strm_(strm), prefix_(prefix) {} void log(Kind kind, const char* message) { const char* kstr = "MISC"; switch (kind) { case kt::RPCServer::Logger::DEBUG: kstr = "DEBUG"; break; case kt::RPCServer::Logger::INFO: kstr = "INFO"; break; case kt::RPCServer::Logger::SYSTEM: kstr = "SYSTEM"; break; case kt::RPCServer::Logger::ERROR: kstr = "ERROR"; break; } *strm_ << prefix_ << ": [" << kstr << "]: " << message << std::endl; } private: std::ostream* strm_; const char* prefix_; }; static LoggerImpl logger(strm, prefix); return &logger; } // stop the running server static void stopserver(int signum) { if (g_serv) g_serv->stop(); g_serv = NULL; } int main(int argc, char **argv) { kt::setkillsignalhandler(stopserver); class Worker : public kt::RPCServer::Worker { private: kt::RPCClient::ReturnValue process(kt::RPCServer* serv, kt::RPCServer::Session* sess, const std::string& name, const std::map<std::string, std::string>& inmap, std::map<std::string, std::string>& outmap) { if (name == "add") { serv->log(kt::RPCServer::Logger::SYSTEM, "concat"); const char *a = kt::strmapget(inmap, "a"); const char *b = kt::strmapget(inmap, "b"); if (!a || !b) { return kt::RPCClient::RVELOGIC; } serv->log(kt::RPCServer::Logger::SYSTEM, "%s + %s", a, b); outmap["result"] = kc::strprintf("%d", strtol(a, NULL , 10) + strtol(b, NULL, 10)); return kt::RPCClient::RVSUCCESS; } else { return kt::RPCClient::RVENOIMPL; } } }; Worker worker; kt::RPCServer::Logger* logger = stdlogger("ktechoserver", &std::cout); kt::RPCServer serv; serv.set_network("127.0.0.1:1987", 5); // 5 はタイムアウト serv.set_worker(&worker, 4); // 4 はワーカースレッドの数 serv.set_logger(logger); serv.log(kt::RPCServer::Logger::SYSTEM, "================ [START]"); g_serv = &serv; // start the server and block until its stop serv.start(); // clean up connections and other resources serv.finish(); serv.log(kt::RPCServer::Logger::SYSTEM, "================ [FINISH]"); return 0; }
動作確認は TSVRPC::Client っていう Perl のライブラリがあったので、それをつかった。
perl -E 'use TSVRPC::Client; use Data::Dumper; say Dumper(TSVRPC::Client->new(base => "http://127.0.0.1:1987/rpc/")->call("add", {a => 1, b => 2}));'
簡単だ!