ThriftをC言語で使ってみる


この記事について

C言語でThriftを使いたいと思ったのですが、結構面倒だったので備忘録&情報共有のため投稿します。

なぜThrift?

C++だけでなくC言語にも対応しているからです。
最近がgRPC(Protocol Buffers)の方が勢いがある気もしますが、今回はThriftを使ってみます。

前提

dockerとThriftの知識が少し必要です。
dockerに関しては、dockerイメージの部分を純粋なCentOSと読み替えれば大丈夫です。
この記事で使用するソース等はここに配置していますので参考にしてください。

なぜC言語?

筆者はここ20年くらい、Java中心の仕事をしていますが久しぶりにC言語が使いたくなったのです。そしてこれがやりたい!

Berkeley DBのフル機能を使うためにはJava EditionではなくC言語版が必要でした。
そして、最終的これやってみたい。

やるとは言ってません。やってみたいだけです。

開発環境の準備

C言語やC++を使う時に一番面倒なのはこれです。
多くの依存モジュールをインストールする必要があります。

以前はMacに直接環境を作っていましたが、今はContainerが便利ですね。
CentOSのDockerイメージで環境を作ります。もちろん純粋なCentOSでも構いません。

docker run -it -d --name="mycentos" centos /bin/bash
docker attach mycentos

基本的に、Thriftのマニュアル(https://thrift.apache.org/docs/install/centos)通りにインストールすればよいのですが、C言語で動作させる場合は少し追加が必要です。
以下に追加の環境設定手順を記載します。
同じことを行っているDockerfileもご活用ください。

不足モジュールのインストール

※テストを簡単に行うため、Pythonも使えるようにしておきます。

#Install glib2
yum -y install glib2-devel

#Install Pip(Pythonを使う場合)
wget https://thrift.apache.org/docs/install/centos
python get-pip.py
python -m pip -V

#Install six (Python modules)
pip install six

ビルド時にpythonとc_glibのライブラリが使えるよに指定します。

#Build and Install Thrift Compiler
git clone https://github.com/apache/thrift.git
cd thrift
RUN ./bootstrap.sh
RUN ./configure --with-lua=no --with-python=yes --with-c_glib=yes 
RUN make
RUN make install

ライブラリパスも忘れずに!

export LD_LIBRARY_PATH=/usr/local/bin

サンプルコード

ping() と hello() のインターフェースを実装します。
Thrift公式ページのTutorial(https://thrift.apache.org/tutorial)を参考にしていますが、もっとシンプルにしました。
ソースはこちら

インターフェースはsample.thriftに定義する必要があります。
仕様はこちら

sample.thrift
namespace c_glib  sample 
namespace py sample

# hello              :Sample implementation
service Basic
{
      void ping(), 
      string hello(1:string arg)
}

サーバ側の実装

|-- Makefile
|-- sample.thrift
|-- server.c

スケルトンの生成

  • まず、I/Fが定義されているsample.thriftをThriftコンパイラでコンパイルし、必要なスケルトンソースを生成します。
thrift --gen c_glib sample.thrift

gen-c_glibディレクトリが作成されています。

|-- Makefile
|-- sample.thrift
|-- server.c
|-- gen-c_glib
|   |-- sample_basic.c
|   |-- sample_basic.h
|   |-- sample_sample_types.c
|   `-- sample_sample_types.h

関数の実装

- 生成されたsample_basic.cに ping(),hello()の処理を記述します。

gboolean sample_basic_handler_ping (sampleBasicIf * iface, GError ** error)
{
  /* TODO: Implement your own code. */
  THRIFT_UNUSED_VAR (iface);
  THRIFT_UNUSED_VAR (error);
  puts ("ping()");
  return TRUE;
}

gboolean sample_basic_handler_hello (sampleBasicIf * iface, gchar ** _return, const gchar * arg, GError ** error)
{
  /* TODO: Implement your own code. */
  THRIFT_UNUSED_VAR (iface);
  THRIFT_UNUSED_VAR (error);
  GString *retValue;
  retValue = g_string_new( NULL );
  g_string_printf( retValue ,"Server received: %s \n" , arg );
  puts( retValue->str );
  *_return = retValue->str;
  return TRUE; 
}

サーバ実行

dockerイメージで実行してみます
/root/dev/c_thrift_sample の下にソースがあることを想定しています

docker run -it mycentos /bin/bash
# cd /root/dev/c_thrift_sample
# make idl (or #thrift --gen c_glib sample.thrift)
# make 
# ./server
Starting the server...

9090ポートでListenしています

クライアント側

クライアントのコードはpythonで記述します。

クライアント実装

ソースコードはこちら

client.py
print "Call ping() = %s " % (client.ping(),)
print "Call hello() = %s " % (client.hello("Hey guys!"),)

必要なPythonモジュールの生成

thrift --gen py sample.thrift  #<--- C言語のスケルトン生成時に使用したファイル

クライアント実行

docker exec -it 232db73154c2  /bin/bash  #<---  232db73154c2 はサーバ動作中のCONTAINER IDです
# cd /root/dev/c_thrift_sample/test/python  #<---  テストコードはここにある想定
# python client.py
Call ping() = None
Call hello() = Server received: Hey guys!

pythonクライアントから送った文字列がC言語で作成したサーバで受信できました。

おまけ

thriftコンパイラで生成したソースを変更しましたが、もう一度生成すると、上書きされて消えてしまいます。
そこで、変更したhandler関数を外出ししました。
ソース類をここに置きます。興味のある方はご覧ください。

まとめ

今更ですがC言語は難しい!
BerkeleyDBも少し使ってみたのですが、久しぶりに使ってみて痛感しました。
最近の言語では、あまり意識する必要がない「メモリ管理」や「UNICODE文字列の操作」などを、自分で作るか外部ライブラリに頼ることになります。
モジュールやライブラリの管理も微妙で難易度がさらに上がります。
しかし、C言語はとてもシンプルなので、性能や実行モジュールサイズはC言語に敵うものはありません。
これからも職人のツールとして残るでしょう。
最後に
 C言語は計算機の仕組みを理解するには一番良い言語です
 これを理解していれば、新しい言語や技術に対応できる能力が身につきます
筆者のようなおじさんだけでなく、若い技術者にもトライしてほしいと思います