linear rpcについて
先日、社内で開発・利用しているソフトウェア群をOSSとして公開させていただきました。その背景や経緯については、こちらをご参照ください。
こちらのblogでは主に、利用方法や技術的な点について記述していきます。
このソフトウェアで何が出来るようになるのか?
このソフトウェアで出来ることはたった3つです。
- リクエスト-レスポンス
クライアント、もしくはサーバからメッセージの送信とそれに対する返信が可能となります。 - イベント通知
クライアント、もしくはサーバから何らかのイベントを他方に任意のタイミングで通知することが可能となります。 - グループへの配信
サーバもしくはクライアントを任意のグループとしてまとめ、そのグループに対して同じイベントを通知することが可能となります。
目指したところは、様々な機器間での(IP上で出来る限りの)リアルタイム・オブジェクト伝送です。現時点でリリースさせていただいているソフトウェアでは、一般的なxNix系OS、Windows、iOS、そしてブラウザ上で動作するアプリケーション間で、任意のデータが送受信可能になります。そして弊社では、これを主に機器制御の一部に利用しています。
大規模なサービスなどを作る場合には、この上にセッション管理などを行うレイヤを作っていく必要がありますが、比較的小さなシステムであれば、このソフトウェアだけで、様々な機器間でのデータ共有が可能となります。
msgpack-rpcについてのおさらい
linear-rpcはmsgpack-rpcを利用しており、既存実装と協調動作が可能となっています。ここでは、その仕様をおさらいしておきます。
- Request Message
RPCということで、ある関数を実行するために必要なメソッド名とパラメータを送信します。
受け取った側のノードは、メソッド名とパラメータに基づいた処理を行い返事 - 'Response Message' を返します。
メッセージフォーマットは、以下の配列をmsgpackでシリアライズしたものとなります。
[0, msgid, method, params]
- Response Message
リクエストメッセージに対する返答となります。メッセージフォーマットは、以下の配列をmsgpackでシリアライズしたものとなります。 -
[1, msgid, error, result]
- Notification Message
イベント通知、返事を期待しないvoid関数の実行といったときに用いるメッセージフォーマットとなっています。
リクエスト、レスポンスと同様に以下の配列をmsgpackでシリアライズして送受信します。
[2, method, params]
既存のmsgpack-rpc実装を利用、もしくはmsgpackのシリアライザ・デシリアライザを用いてソフトウェアを作ることによって、リモートノード間でデータ共有可能なアプリケーションとして動かすことが可能になっています。
msgpack-rpc-goと繋いでみよう
https://github.com/sonyでは、golangのrepositoryが最初に公開されました。
せっかくなので、ここはgolangのmsgpack-rpc実装と繋いでみましょう。
- linearのサンプルまでmake
$ git clone --recursive https://github.com/linear-rpc/linear-cpp
$ cd linear-cpp $ ./bootstrap && ./configure --with-sample && make - linear tcpserverの起動
$ cd sample
$ ./tcp_server_sample - golangのmsgpack-rpc clientの作成
ここからサーバサイドのコードを削り、linear tcp serverのポート番号37800に繋ぐように変更します。$ mkdir -p $HOME/go
$ export GOPATH=$HOME/go
$ go get github.com/msgpack-rpc/msgpack-rpc-go/rpc
$ cd $GOPATH/src/github.com/msgpack-rpc/msgpack-rpc-go/example
package main import (
"fmt"
"net"
"github.com/msgpack-rpc/msgpack-rpc-go/rpc"
) func main() {
conn, err := net.Dial("tcp", "127.0.0.1:37800")
if err != nil {
fmt.Println("fail to connect to server.")
return
}
client := rpc.NewSession(conn, true) retval, xerr := client.Send("echo", "World")
if xerr != nil {
fmt.Println(xerr)
return
}
fmt.Println(retval.String())
}
$ go build sample_mprpc.go - golangのecho client起動
$./sample_mprc <[]reflect.Value Value>
何か返答は来ましたが、値がおかしいです。linear tcp server側のコンソールを見てみましょう。
recv Request: msgid = 1, method = "echo", params = ["world"] from ...
golangのecho clientからは、msgpack-rpcの仕様通り、methodとして"echo"、parameterとしてtupleにstringが1つ入れられた、["world"]が流れてきています。
linear tcp serverのエコーサーバのサンプルでは、クライアント側から与えられたparameterをそのままの形でエコーバックしていますが、golangのclientでは返り値として、stringを期待しているようです。tupleにstring型のデータが1つだけなので、linear tcp server側を少し手抜きしつつ、以下に該当する部分を書き換えてみます。
if (request.method == "echo") {
std::cout << "do echo back: " << request.params.stringify() << std::endl;
// parameterをvector<string>としてdecodeし、配列の1つ目を"Hello, "と結合
std::string reply = "Hello, " + request.params.as<std::vector<std::string> >()[0];
// 結合したstringをresponseとして返答
linear::Response response(request.msgid, reply);
response.Send(socket); }
$ cd /path/to/linear-cpp/sample $ make
$ ./tcp_server_sampleさて、どうでしょうか?今度は期待する返答がPrintlnされているはずです。
このように、既存実装とつなぎ、Request - Responseが実現できるようになっていますので、既に皆様がご利用されているmsgpack-rpcの実装と繋いで見ていただけると幸いです。
その他の機能については、doc以下でmake docしていただければマニュアルが生成されるようになっていますのと、sample以下にあるコードを見ていただければ、何ができるか、は分かるようになっているはずです。(objcだけちょっと不足気味ですが)
また、時間ができましたら、それぞれの言語ごとの実装について、このサイトで少しづつご紹介させていただきますので、ご興味がありましたらご訪問くださいませ。では、また。