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}));'
簡単だ!
Published: 2011-01-01(Wed) 00:30