Quitada ブログ HAX

Hatena Blog でも Quitada ブログ

Apache Geode で gfsh 使って積極的に JSON 形式のデータを扱ってみる

Apache Geode に gfsh というコマンドラインツールが付属してますが、ドキュメントによると以下のようなイメージで JSON 形式でのデータ投入ができるようなんですよね。

put --key=1 --value=('name':'quitada','title':'Inchiki Developer','age':44) --region=/MyRegion

JSON のフォーマットに使用する、ダブルクォートとか波括弧は gfsh のコマンドラインでは別の意味をもつ記号なんで、代わりにシングルクォートと括弧に置き換えてますが、ようするに以下の JSON 形式データを Apache Geode に作ってあるであろう MyRegion というリージョンに対して "1" というキーに対するバリューとして投入するイメージです。

{
  "name":"quitada",
  "title":"Inchiki Developer",
  "age":44
}

キーにもバリュー同様、JSON 形式で指定することできますが、まー、普通はキー・バリューストア系のデータベースのキーに複雑な構造を指定することはないでしょう。

ただ、上述の例だと String 型の文字列としてリージョンに入ってしまい、クエリーとかもナイスにできないのです。ドキュメントに詳しく書いてないのですが、どうやら --value-class にデータマッピングを行う対象のドメインクラス名を指定すると、対象ドメインクラスのフィールド変数に指定した値を割り当てドメインオブジェクトを生成、リージョンに格納してくれるようです。そうすると、表向き JSON 形式でデータを取り扱い、Java オブジェクトとして扱うことで Apache Geode のクエリー機能とかも使えるようになります。

例えば、上述の例だと以下のように、Javaドメインクラスを記述します。そして、コンパイルしてキャッシュサーバーのクラスパスに入れます(あるいは、jar でかためて gfsh deploy コマンドでサーバーに配備)。

package quitada;

/**
 * Created by quitada on 17/05/20.
 */
public class MyData {
    private String name = null;
    private String title = null;
    private Integer age;

    public MyData(){}

    public MyData(String name, String title, Integer age){
        this.name = name;
        this.title = title;
        this.age = age;
    }

    public String getName(){
        return this.name;
    }

    public String getTitle(){
        return this.title;
    }

    public Integer getAge(){
        return this.age;
    }

    public void setName(String name) {
        this.name = name;
    }

    public void setTitle(String title) {
        this.title = title;
    }

    public void setAge(Integer age) {
        this.age = age;
    }

    public String toString() {
        return new String("[MyData(name = " + name + "/title = " + title+"/age = " + age.toString() + ")]");
    }
}

で、以下のコマンドを実行すると、フィールド変数 name に quitada、title に Inchiki Developer、age に 44 と代入してリージョンに投入してくれるわけです。

put --key=1 --value=('name':'quitada','title':'Inchiki Developer','age':44) --region=/MyRegion --value-class=quitada.MyData

ドキュメントに詳しく書いてなくて、ソースコードよくみてないのでよくわからないのですが、配列とかネストした JSON 形式データからドメインオブジェクトへのマッピングは無理っぽかったです。少なくとも、RDBMS のようなフラットなテーブルみたいな構造はマッピングできます(値は、文字列とか数値に限定されますが)。

そうなってくると、クライアント Java アプリケーションからも JSON 形式前提でデータの出し入れとかクエリーをしたくなります。Apache Geode では、org.apache.geode.pdx.JSONFormatter というクラスが用意されていて、JSON 文字列から Apache Geode に格納するための Java オブジェクト、あるいはその逆の変換が可能です。それを使ってやりましょう。

せっかくなんで、gfsh で一通りの環境を作ってみましょうか。

まずはロケーターとサーバー x2 を起動します(サーバー x1 でももっと多くでもどうでもいいんですが、この例では Partitioned リージョン使うのでなんとなく)。gfsh を使って起動できますが、それは割愛します。ドキュメントみましょう。

次に、クライアントアプリケーションで JSONFormatter を使うということは、対象リージョンは PdxInstance でオブジェクトを扱うことが前提となるので PDX の設定にて --read-serialized=true、また上述の例ででてきた quitada.MyData というクラスを PDX による自動シリアライズ対象にするため --auto-serializable-classes も設定します。以下、gfsh コマンド(インタラクティブモード)からのコマンド実行例です(リージョンデータと PDX メタデータも永続化してあります)。

create disk-store --name=pdx --dir=pdx
configure pdx --read-serialized=true --disk-store=pdx --auto-serializable-classes=quitada.*
create region --name=MyRegion --type=PARTITION_REDUNDANT_PERSISTENT

#ちなみに、上述の MyData クラスですが、シリアライズする必要がありますが、implements Serializable とかつけてません。PDX のオートシリアライザー(?)が自動的によろしくやってくれます。

#ちなみに、configure pdx コマンドの設定内容は、起動中のサーバーには反映されないようなので、上述のコマンドラインにて PDX やリージョン設定したら、サーバーを再起動して configure pdx コマンドの設定内容を反映させる必要があるようです。gfsh からのキャッシュやリージョンの設定内容はロケーターの方に永続化され、サーバー再起動時にはその設定内容を読み込みます。

それでもって、上述の --value-class=quitada.MyData を付与した put コマンドを実行し、get コマンドを実行すると値が取得できるのがわかります。以下、実行例です。

gfsh>get --key=1 --region=/MyRegion
Result      : true
Key Class   : java.lang.String
Key         : 1
Value Class : org.apache.geode.pdx.internal.PdxInstanceImpl

 name   |       title       | age
------- | ----------------- | ---
quitada | Inchiki Developer | 44

read-serialized=true 設定なので、バリュークラスが実際には PdxInstanceImpl となっていることがわかります。

クエリーもできますよー。以下、実行例です。

gfsh>query --query="select * from /MyRegion mr where mr.name='quitada'"

Result     : true
startCount : 0
endCount   : 20
Rows       : 1

 name   |       title       | age
------- | ----------------- | ---
quitada | Inchiki Developer | 44

クライアント Java アプリケーションからも JSON 形式にてデータの出し入れができます。以下、コード抜粋です。

ClientCache ccache = new ClientCacheFactory()
    .set("cache-xml-file", "./xml/cache-client.xml")
    .set("log-level","config")
    .create();
Region<String, PdxInstance> myRegion = ccache.getRegion("MyRegion");

String key = "1";

// JSON 形式でデータ投入
myRegion.put(key,JSONFormatter.fromJSON("{\"name\":\"quitada\",\"title\":\"Inchiki Developer\",\"age\":44}"));

// PdxInstance オブジェクトを JSON 形式に変換・出力
System.out.println("get json string (key=" + key + ")=\n" + JSONFormatter.toJSON(myRegion.get(key)));

read-serialized=true 設定なので、get をすると必ず PdxInstance 型のオブジェクトがかえってきます。Java アプリケーション内でデータを扱う場合は、わざわざ返り値を JSON 形式に戻すことはないと思うので、かえってきた PdxInstance 型オブジェクトから直接値を取り出すことになります。なので、フィールド変数の値を取り出すだけだったら、gfsh から JSON 型データを投入して値をオブジェクトにマッピングするため作成した quitada.MyData クラスはいらないんですね。PdxInstance の使い方について詳しくはドキュメント参照してみてください。