Thrift 0.9を使用してPHPでCassandraにアクセスする方法

Thrift とは,Facebookが開発したRPCフレームワークです.

参考: http://ja.wikipedia.org/wiki/Apache_Thrift

「スケーラブルな言語間サービス開発」と書かれているように,CassandraはJavaで動いていますが,それをJavaでなく,クライアントはPHPでアクセスしたいといったような場合に,このThriftが,いわゆるインターフェースとなるわけです.

前回は,RubyからCassandraにアクセスする方法を説明しましたが,今回はPHPでアクセスする方法を説明します.

ネット上では,Thriftの旧バージョン(例えば,0.2とか)でのサンプルソースは多く見かけるのですが,バージョン0.9では,色々と機能も増えているようで,Thriftのライブラリの構成(ディレクトリパスとか名前空間とか)が変わっていて,旧バージョン用のサンプルが利用できなくなってしまってます.

では,バージョン0.9に対応したサンプルは無いのかというと,色々探しましたが見つけきれませんでした...

そこで,今回,色々調査して,自分なりにサンプルとなるものを作ってみました.

01. Apache Thriftのセットアップ

【参考】http://thrift.apache.org/docs/install/centos/

上記ページの説明に習って,必要なパッケージ(依存するパッケージ)のセットアップを行います.

$ sudo yum install automake libtool flex bison pkgconfig gcc-c++ 
boost-devel libevent-devel zlib-devel python-devel ruby-devel/

OpenSSLの開発環境もインストールしておきます.

$ sudo yum install openssl-devel

ここでやっとThriftの出番です.ソースパッケージをダウンロードします.

公式ダウンロードサイトは次のURLです.
http://thrift.apache.org/docs/install/

今回は,ミラーサイト(理研)よりwgetを利用してダウンロードします.

ホームディレクトリにあらかじめ適当なディレクトリ(ここでは「tmp」)を作っておいて...

$ mkdir ~/tmp
$ cd ~/tmp
$ wget ftp://ftp.riken.jp/net/apache/thrift/0.9.1/thrift-0.9.1.tar.gz

tmpディレクトリ上で展開します.

$ tar xvzf thrift-0.9.1.tar.gz
$ cd thrift-0.9.1

ビルドの前作業,Python用は使わないので生成しないものとします.(Pythonを有効にするとエラーが出ました.)これにより,C++とPHPのインターフェースのみ生成されます.

$ ./configure --without-python
$ make
$ sudo make install

実行を確認してみます.

$ thrift --version
Thrift version 0.9.1

02. Cassandraのサンプルデータの作成

以下のようなデータを作成してみます.

{
    'sample': // キースペース
    {
        'students': // カラムファミリー
        {
            's_id': // ローキー
            {
                'taro': '001', // カラム: 値
                'hana': '002'
            },
            'full_name':
            {
                'taro': 'Taro Kagoshima',
                'hana': 'Hana Satsuma'
            },
            'e_mail':
            {
                'taro': 'taro@kago.jp',
                'hana': 'hana@kago.jp'
            }
        }
    }
}

Cassandraクライアントツールを起動します.

$ cassandra-cli

キースペース「sample」の生成

[default@unknown] create keyspace sample;

キースペース「sample」を使用

[default@unknown] use sample;

カラムファミリー「students」の生成

[default@sample] create column family students with comparator=UTF8Type 
AND key_validation_class=UTF8Type AND default_validation_class=UTF8Type;

続けてデータを追加していきます.

[default@sample] set students['taro']['s_id'] = '001';
[default@sample] set students['taro']['full_name'] = 'Taro Kagoshima';
[default@sample] set students['taro']['e_mail'] = 'taro@kago.jp';
[default@sample] set students['hana']['s_id'] = '002';
[default@sample] set students['hana']['full_name'] = 'Hana Satsuma';
[default@sample] set students['hana']['e_mail'] = 'hana@kago.jp';

ローデータを1つ取得して表示させてみます.

[default@sample] get students['taro'];

exitコマンドで終了します.

[default@sample] exit;

03. Thrift利用環境のセットアップ

CassandraのThriftインターフェースを生成します.

$ cd
$ cp /opt/cassandra/interface/cassandra.thrift .
$ thrift --gen php cassandra.thrift

※カレントディレクトリ(ホームディレクトリ)に「gen-php」ディレクトリが生成されます.

04. サンプルPHPファイルによる接続確認

ホームディレクトリ上で,サンプルPHPファイルを作成します.

$ cd
$ vi connect_test.php

Cassandraサーバへの接続を確認するだけのソースは,以下のとおり.

// gen-phpディレクトリのパスを指定
$GEN_PHP = realpath(dirname(__FILE__)).'/gen-php';
require_once '/usr/lib/php/Thrift/ClassLoader/ThriftClassLoader.php';
// ThriftやCassandraのライブラリをロードしスタックに登録する
$loader = new Thrift\ClassLoader\ThriftClassLoader();
$loader->registerNamespace('Thrift', '/usr/lib/php');
$loader->registerDefinition('cassandra', $GEN_PHP);
$loader->register();

use Thrift\Protocol\TBinaryProtocol;
use Thrift\Transport\TSocket;
use Thrift\Transport\TFramedTransport;
use Thrift\Exception\TException;

try {
  $socket = new TSocket('127.0.0.1', 9160);
  $transport = new TFramedTransport($socket);

  printf("Start Client!!\n");
  $transport->open();
  $protocol = new TBinaryProtocol($transport);
  $client = new cassandra\CassandraClient($protocol);

  $version = $client->describe_version();
  $cluster_name = $client->describe_cluster_name();
  $partitioner = $client->describe_partitioner();

  printf("Version: %s\n", $version);
  printf("Cluster name: %s\n", $cluster_name);
  printf("Partitioner: %s\n", $partitioner);

  var_dump($client->describe_schema_versions());
  $transport->close();
  printf("Finish Client\n");
}
catch(TException $tx) {
  print 'TException: '.$tx->getMessage()."\n";
}

Thrift(PHP)からCassandraへアクセスするサンプルソースは,他の方のサイトでも色々と公開されていますが,Thriftのバージョンが0.2だったりと,最新の0.9に対応したものが無く,色々とドキュメントやチュートリアルを調べた結果,ThriftClassLoader.phpだけをrequire_onceすれば良いことが分かりました.

前のバージョンですと,このrequire_onceの行がたくさんあるサンプルソースを良く見かけますが,0.9では,このThrift\ClassLoader\ThriftClassLoaderクラスを利用することで,あらかじめライブラリをスタックに登録することができるようです.ThriftClassLoaderは内部的に,registerファンクションにおいて,「spl_autoload_register」ファンクションを呼び出しているようです.

従って,require_onceの記述はこれだけ.

require_once '/usr/lib/php/Thrift/ClassLoader/ThriftClassLoader.php';

ThriftClassLoaderクラスのregisterNamespaceメソッドに,Thriftの標準ライブラリのあるディレクトリパスと,namespace名である「Thrift」を指定し,registerDefinitionメソッドにThriftの拡張ライブラリ,即ちここではCassandra用のThriftインターフェースを登録します.「gen-php」の絶対パスとnamespace名(’cassandra’)を指定しておきます.registerメソッドを実行することで,登録が完了します.

// ThriftやCassandraのライブラリをロードしスタックに登録する
$loader = new Thrift\ClassLoader\ThriftClassLoader();
$loader->registerNamespace('Thrift', '/usr/lib/php');
$loader->registerDefinition('cassandra', $GEN_PHP);
$loader->register();

これをしておかないと,例えば,ThriftのTSocketクラスからはTTransportクラスを参照しているので,TSocketクラスを利用するのに,TSocket.phpをrequireするだけでなく,事前にTTransport.phpもrequireしておかなければならなかったり,Cassandraクラスの中でも,TMassageTypeやらThriftのクラスを参照しているので,TMassageType.phpやらをrequireしなくてはならなかったりで大変になります.

ThriftClassLoaderを使えば,それが解消されます.後は,使いたいクラスを,以下のようにuseでnamespace名付きで指定すれば良いだけです.

use Thrift\Protocol\TBinaryProtocol;
use Thrift\Transport\TSocket;
use Thrift\Transport\TFramedTransport;
use Thrift\Exception\TException;

try {
  $socket = new TSocket('127.0.0.1', 9160);

別にuseで指定しなくても.

  $socket = new Thrift\Transport\TSocket('127.0.0.1', 9160);

と指定して利用してもOKです.

実行結果は次のようになります.

$ php -f connect_test.php
Start Client!!
Version: 19.36.2
Cluster name: Test Cluster
Partitioner: org.apache.cassandra.dht.Murmur3Partitioner
array(1) {
  ["41befe11-302a-39e4-b2b2-03ded79fa7d4"]=>
  array(1) {
    [0]=>
    string(9) "127.0.0.1"
  }
}
Finish Client

つづいて,実際に,先ほど作成した,キースペース「sample」の,カラムファミリー「students」に,具体的にアクセスする場合も同様で,用意する最初の記述(おまじない)は一緒です.

$GEN_DIR = realpath(dirname(__FILE__)).'/gen-php';
require_once '/usr/lib/php/Thrift/ClassLoader/ThriftClassLoader.php';
$loader = new Thrift\ClassLoader\ThriftClassLoader();
$loader->registerNamespace('Thrift', '/usr/lib/php');
$loader->registerDefinition('cassandra', $GEN_DIR);
$loader->register();

use Thrift\Protocol\TBinaryProtocol;
use Thrift\Transport\TSocket;
use Thrift\Transport\TFramedTransport;
use Thrift\Exception\TException;

try {
  $socket = new TSocket('127.0.0.1', 9160);
  $transport = new TFramedTransport($socket);

  printf("Start Client!!\n");
  $transport->open();
  $protocol = new TBinaryProtocol($transport);
  $client = new cassandra\CassandraClient($protocol);

  $keyspace = "sample";
  $columnFamily = "students";
  $consistency_level = cassandra\ConsistencyLevel::ONE;

  $client->set_keyspace($keyspace);
  $columnParent = new cassandra\ColumnParent();
  $columnParent->column_family = $columnFamily;

  $predicate = new cassandra\SlicePredicate();
  $sliceRange = new cassandra\SliceRange();

  $sliceRange->start = "e_mail";
  $sliceRange->finish = "s_id";
  $predicate->slice_range = $sliceRange;

  $result = $client->get_slice('hana', $columnParent, $predicate, $consistency_level);

  var_dump($result);

  $transport->close();
  printf("Finish Client\n");
}
catch(TException $tx) {
  print "TException: ".$tx->why." Error: ".$tx->getMessage()."\n";
}

そして,その実行結果が以下のとおりです.

$ php -f sample.php 
Start Client!!
array(3) {
  [0]=>
  object(cassandra\ColumnOrSuperColumn)#11 (4) {
    ["column"]=>
    object(cassandra\Column)#12 (4) {
      ["name"]=>
      string(6) "e_mail"
      ["value"]=>
      string(12) "hana@kago.jp"
      ["timestamp"]=>
      int(1391397411902000)
      ["ttl"]=>
      NULL
    }
    ["super_column"]=>
    NULL
    ["counter_column"]=>
    NULL
    ["counter_super_column"]=>
    NULL
  }
  [1]=>
  object(cassandra\ColumnOrSuperColumn)#13 (4) {
    ["column"]=>
    object(cassandra\Column)#14 (4) {
      ["name"]=>
      string(9) "full_name"
      ["value"]=>
      string(12) "Hana Satsuma"
      ["timestamp"]=>
      int(1391397398663000)
      ["ttl"]=>
      NULL
    }
    ["super_column"]=>
    NULL
    ["counter_column"]=>
    NULL
    ["counter_super_column"]=>
    NULL
  }
  [2]=>
  object(cassandra\ColumnOrSuperColumn)#15 (4) {
    ["column"]=>
    object(cassandra\Column)#16 (4) {
      ["name"]=>
      string(4) "s_id"
      ["value"]=>
      string(3) "002"
      ["timestamp"]=>
      int(1391397384256000)
      ["ttl"]=>
      NULL
    }
    ["super_column"]=>
    NULL
    ["counter_column"]=>
    NULL
    ["counter_super_column"]=>
    NULL
  }
}
Finish Client

色々ネットで探してみて,現時点(2014/02/06)で,Thrift Version 0.9のPHPインターフェースを使って,Cassandraサーバにアクセスする例が,少なくとも日本語サイトでは見つかりませんでしたので,この記述が妥当なのかわかりませんが,おそらく色々やってみた結果,このスタイルが一番良いのではないかと思います.

広告

One thought on “Thrift 0.9を使用してPHPでCassandraにアクセスする方法

コメントを残す

以下に詳細を記入するか、アイコンをクリックしてログインしてください。

WordPress.com ロゴ

WordPress.com アカウントを使ってコメントしています。 ログアウト / 変更 )

Twitter 画像

Twitter アカウントを使ってコメントしています。 ログアウト / 変更 )

Facebook の写真

Facebook アカウントを使ってコメントしています。 ログアウト / 変更 )

Google+ フォト

Google+ アカウントを使ってコメントしています。 ログアウト / 変更 )

%s と連携中