データベースライブラリTkrzwのgRPCサービスにJavaで接続して操作するためのライブラリを実装した。これにて、当初予定していたC++、Java、Python、Ruby、Goの全てを網羅したので、gRPCのクライアントライブラリを書きまくる旅は終了だ。この記事では、簡単な使い方と性能評価を紹介する。Echo操作の結果を1、2、4スレッドで変えてスループットを測定した場合の結果は以下のようになる。
前回の記事にて、gRPCのJavaアプリケーションを手動でビルドする手順を確立した。あとはそれをMakefileで自動化すればパッケージが作れる。gRPCの依存パッケージはバイナリパッケージからインストールするのが楽だ。Ubuntuの場合、aptで以下のパッケージを入れればよい。
- libprotobuf-java
- libgrpc-java
- protobuf-compiler-grpc-java-plugin
- libgoogle-common-protos-java
- libguava-java
- libnetty-java
- libperfmark-java
サーバを動かすシステムには、TkrzwとTkrzw-RPCをインストールする必要がある。クライアント側のシステムには必ずしもTkrzwとTkrzw-RPCをインストールする必要はない。とはいえ今回はサーバと組み合わせてテストするので、TkrzwとTkrzw-RPCはインストールしてあるものとする。それぞれソースパッケージを落として、make && make install すれば入るはずだ。クライアントで側は、上述の依存パッケージを入れた上で、Tkrzw-RPCのJavaパッケージを make && make install すればよい。
Tkrzw-RPCのサーバは以下のコマンドで起動できる。デフォルトでは、オンメモリのデータベースを起動する。デバッグログも表示するようにしよう。
$ tkrzr_server --log_level debug
Javaの最も簡単なクライアントアプリケーションは、以下のようなコードになる。ローカルのTkrzwではDBMクラスを使っていたが、その代わりにRemoteDBMクラスを用いる。また、ファイルを開いたり閉じたりするopen/closeメソッドの代わりにネットワークコネクションを開いたり閉じたりするconnect/disconnectメソッドを用いる。それ以外のAPIはほとんどローカルと同じである。
// Tkrzw-RPCのシンボルをインポートする import tkrzw_rpc.*; public class Example1 { // メイン関数 public static void main(String[] args) { // データベースサーバへの接続を準備する RemoteDBM dbm = new RemoteDBM(); dbm.connect("localhost:1978", -1); // レコードを格納する // キーと値は文字列で指定できるが、バイト列として格納される。 dbm.set("first", "hop"); dbm.set("second", "step"); dbm.set("third", "jump"); // レコードを検索する // 検索に失敗した場合、nullが返される System.out.println(dbm.get("first")); System.out.println(dbm.get("second")); System.out.println(dbm.get("third")); System.out.println(dbm.get("fourth")); // データベース内の各レコードに横断的にアクセスする // 作ったイテレータはdestructで破棄すること Iterator iter = dbm.makeIterator(); iter.first(); while (true) { String[] record = iter.getString(); if (record == null) { break; } System.out.println(record[0] + ": " + record[1]); iter.next(); } iter.destruct(); // データベース接続を破棄する dbm.disconnect(); dbm.destruct(); } }
細かいことはJava版のAPI文書をご覧いただきたい。以前の記事を参考にすれば、メッセージキューとしても使える。
以前やったベンチマークテストにJava版の結果も加えよう。Echo、Set、Getの各操作を10万回行い、そのスループットを測る。Java版はUNIXドメインソケットをサポートしていないので、全言語でIPv4を使って測定しなおした。
1スレッドのスループットは以下。
Echo | Get | Set | |
C++ | 20389 | 21111 | 20936 |
Java | 17512 | 16786 | 16504 |
Python | 9748 | 9300 | 9341 |
Ruby | 11556 | 11476 | 11266 |
Go | 14262 | 14148 | 14200 |
2スレッドのスループットは以下。
Echo | Get | Set | |
C++ | 28025 | 27861 | 27800 |
Java | 24830 | 23752 | 23969 |
Python | 13160 | 12643 | 12319 |
Ruby | 16752 | 16249 | 16128 |
Go | 22670 | 22375 | 22420 |
4スレッドのスループットは以下。
Echo | Get | Set | |
C++ | 34542 | 34399 | 34859 |
Java | 34806 | 33169 | 33259 |
Python | 10689 | 10307 | 10115 |
Ruby | 15623 | 15287 | 14954 |
Go | 32366 | 32250 | 32284 |
Echoの結果に着目すれば、これはTkrzw-RPCのベンチマークというより、素のgRPCのベンチマークとみなすことができる。ということで、gRPCのクライアントでC++に次いで高速なのはJavaということになった。4スレッド並列にすると性能はC++とほぼ同じになる。とはいえ、何度か述べていることだが、クライアント側はマシンの台数を増やしてスループットを上げられることが多いので、この結果を気にする必要はあまりない。サーバ側はC++で実装してスループットを最大化しつつ、クライアント側では多くの言語をサポートしてインターオペラビリティを向上させることが重要だ。