[Geode] Apache Geodeハンズオンに参加してきました #geode_jp

こむろです。ただいま花粉真っ只中の関東に帰ってきています。

2017/03/14にPivotalにて Apache Geodeハンズオン が開催されていたので参加してきました。 某氏から4ヶ月ほど前に宿題として積まれていて、面白そうな技術だなと思っていたのですが、なかなか手をつけられておらず、実際に手を動かして使ってみる良い機会だったので思い切って参加してみました。

基本的な機能のみですが、実際に動かしてみつつ、疑問に思ったことを色々と聴いてきました。

かなり長いため、面倒な人はまとめくらいまで飛ばしてしまって良いかもしれません。面白そうだなと思ったら実際に手を動かして見たほうが良いと思います。アプリケーションに簡単に組み込める(後述)ため、気軽にさっと始めることができます。

Apache Geodeって?

Apache Geodeとはざっくりいうとインメモリデータグリッドを構成できる分散KVSのソフトウェアです。元々Gemfire がベースとなっており、Apache Foundationにソースコードが移管されオープンソース版として公開されたようです。インメモリーデータストアという特性上、低レイテンシーな高速な処理が期待できます。反面メモリー上のデータ故に永続化などについてはそこまで過度な期待はできません。(とは言え、データストアを分散しているため耐障害性、高信頼性は高いですが)

自分が持つKVSのイメージとはまた少し異なる特徴や機能も持っているようです。KVSですがOQLというSQLライクなクエリによる検索が可能であったり、Transaction機能を有しています。複数のキャッシュサーバーによってデータグリッドを組むため耐障害性にも優れており、複数のキャッシュサーバーが落ちたとしても全体の動作に影響することはほとんどありません。 *1

またデータの更新や作成、削除などといった処理に応じてイベントを発火できるのも大きな特徴の一つではないでしょうか。DynamoDB StreamsのEvent Triggerに近いものというイメージでしょうか。データ更新のイベントを検知してデータ更新イベントに応じたListenerを実行することができます。

まとめると以下になります。

  1. KVSですがOQLというSQLライクなクエリによるデータの検索が可能です
  2. 更新の原子性を担保するTransaction機能があります(未確認)
  3. データの状態変更に応じたイベントの発火が可能です

これらの機能の初歩的な部分をハンズオンでは触ることができました(Transaction以外)

ハンズオンではどのようなことをやったか?

まずはじめにApache Geodeの概要をざっと解説していただきました。その後、実際にチャットアプリケーションを作成しながらデータの保存、イベント発火によるデータ更新の受信を実装するまでを体験しました。

  1. Embeddedモードでチャットアプリケーションを作成。キャッシュサーバーを内包させ、複数のアプリケーションでデータがReplicationされていること、自分が発言すると即座にチャットに参加している他のアプリケーションにその情報が表示されることを確認。いわゆるP2Pチャットアプリケーション。
  2. クライアント・サーバー型のキャッシュサーバーに置き換えて同じくチャットアプリケーションを作成。同様の内容が実現出来ることを確認
  3. 最後にチャットをCLIからJavaFXのGUIへ置き換え

上記3つのアプリケーション作成によってApache Geodeの簡単な使い方を学びました。

まずは基礎知識から

Apache Geodeを利用する上で最低限知っておくべきところを確認しました。まずは登場人物から行きましょう。

登場人物

  • キャッシュサーバー : その名の通り、キャッシュのデータストアです。ここに基本的にデータは格納されていきます。
  • ロケーター : ロケーターはどのデータがどの場所にあるかなどを管理しています。そのため、クライアントプログラムはロケーターに問い合わせを行い、どのキャッシュサーバーにデータが格納されているかを知る必要があります。
  • クライアント : キャッシュサーバーを利用してデータを操作するクライアントプログラムです。今回の場合は、Javaのプログラムがこちらにあたります

スクリーンショット 2017-03-18 13.09.01

雑なイメージですがこんな感じの関係になります。

以前はロケーターは必須ではなかったのですが、最近必須となったようでロケーターがないと動作しないそうです。

データReplicationの種類

Replicationの戦略を選ぶことができます。Full ReplicationとPartitioningです。

Full Replicationはその名の通り、全てのキャッシュサーバーが全く同じデータのコピーを持っています。そのため耐障害性はとても高く、さらにどのキャッシュサーバーも同じものを持っているわけですから、低レイテンシーなレスポンスを期待することができます。ただし、こちらは全てのサーバーへのコピーが必要になるため、更新やデータ作成の場合、スケールするほどコストが高くなるという欠点もあります。

Partitioningはデータをキャッシュサーバーに分散して管理する方式です。Primaryなデータはどこかのキャッシュサーバーに存在します。データのバックアップはいくつかのキャッシュサーバーに分散して保持しています。全てのキャッシュサーバーが同じデータを持っているわけではないので、クライアントの問い合わせに対して「そのデータはここのキャッシュサーバーにありますよ」というのを教えてあげる必要があるなど、Full Replicationに比べて読み込み、参照の性能は劣りますが、データ更新や作成の際にはたとえスケールしても高速に処理が可能という特徴があります。

キャッシュサーバー構成

こちらも2種類あるようです。通常のクライアント・サーバー構成のようにキャッシュサーバーのみを構成する専用のサーバーを構築する方法もありますが、Apache Geodeではもう一つEmbeddedモードでアプリ内にキャッシュサーバーを内包してしまう方法を取ることができます。Running in Embedded Mode

アプリケーション内にキャッシュサーバーを内包することで専用のサーバーを立てなくてもアプリケーションを複数立ち上げるだけで、内包されたキャッシュサーバーが自動的に同期します。そのため、自分の中に内包するキャッシュサーバーにデータを書き込むと、他のアプリケーションに内包されているキャッシュサーバーに瞬時に同期され、あたかも外部のデータストアを利用しているように振る舞うことができます。分かりやすい画像が公式にありましたのでこちらを転記いたします。

https://cwiki.apache.org/confluence/display/GEODE/Running+in+Embedded+Mode

the embedded Geode Topology

ハンズオン1. Embeddedモードでキャッシュサーバーを構築し、CLIチャットアプリケーションを完成させる

資料はこちらになります。プロジェクトの中から1つ目の課題では geode.handson.cui.P2PChat.java を編集します。

基本的にEclipseベースだったのですが自分はIntelliJ IDEAを利用しました。環境は以下になります。

  • OS: Mac OSX 10.10.5
  • Java: java version "1.8.0_91"
  • IntelliJ IDEA: 2016.3.5

基本的な部分は割愛しますが、whileループを利用したとても基本的なCLIチャットアプリケーションですね。 最終的に以下のような形になりました。

/**
 * 標準入力より受け取ったメッセージをチャットリージョンへ登録します。<br>
 * リージョン登録時のキーフォーマットは、{ユーザー名}@{現在日時}@{メッセージ番号}とします。<br>
 */
public class P2PChat {
    private static final DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss.SSS");

    public static void main(String[] args) throws Exception {

        Properties props = new Properties();

        // Embeddedモードの場合はRootとなるLocatorが必要なので、 start-locatorで起動する必要がある
        if (args.length > 0 && "embeddedLocator".equals(args[0])) {
            props.setProperty("start-locator", "localhost[10335]"); // 10335ポートでStartLocatorを起動
            System.out.println("Start Start-Locator");
        } else {
            // 2個目以降のアプリケーションはStartLocatorに隷属させる(ポートで指定)
            props.setProperty("locators", "localhost[10335]");  // 10335のStartLocator以下に従属させる
            System.out.println("Start Locators");
        }

        // [Embedded] 必ずアプリケーションの初期化時にFactoryでCacheを作成する必要がある
        CacheFactory factory = new CacheFactory(props);
        Cache cache = factory.create();

        Region<String, String> region = cache.getRegion("ChatMessage"); // Region名(テーブル名みたいなもの?)を作成する

        try (BufferedReader br = new BufferedReader(new InputStreamReader(System.in))) {

            System.out.println("Enter Username.");
            String user = br.readLine();

            // チャット開始 「:q」で終了
            System.out.println("Chat session start.");
            String message = null;
            int messageNo = 1;
            while ((message = br.readLine()) != null) {
                if (message.length() == 0)
                    continue;
                if (message.equals(":q")) {
                    break;
                }

                // 一意になるKeyを時間ベースで作成
                String key = String.format("%s@%s@%d", user, LocalDateTime.now().format(formatter), messageNo++);
                region.put(key, message);   // これでキャッシュサーバーへのデータ登録は完了
            }
        }
        System.out.println("Exiting... ");

        System.exit(0);
    }
}

起動引数でLocatorが一人もいない状態(つまり一つもアプリケーションが起動していない状態)なのかを指定する必要があります。Javaの起動引数で embeddedLocator を入力すればStartLocatorとして起動します。引数なしの場合は、StartLocatorが起動している 10335ポート へ接続するように設定します。これらの設定は自分で記載していますが、 実際はxmlファイルなどの設定ファイルでの記載が可能かと思います。

Cache インスタンスはFactoryから取得します。

CacheFactory factory = new CacheFactory(props);
Cache cache = factory.create();

キャッシュサーバーにはRegionという概念があります。RDBのテーブルのような理解で良さそうです。

Region<String, String> region = cache.getRegion("ChatMessage"); // Region名(テーブル名みたいなもの?)を作成する

Regionを確認すると、 java.util.concurrent.ConcurrentMap<K,V> インタフェースを継承しているので、使い方はMapとほとんど変わりません。

入力したメッセージをキャッシュサーバーに書き込みます。

// 一意になるKeyを時間ベースで作成
String key = String.format("%s@%s@%d", user, LocalDateTime.now().format(formatter), messageNo++);
region.put(key, message);   // これでキャッシュサーバーへのデータ登録は完了

メッセージが登録された際にイベントが発火して起動するListener

チャットアプリケーションなので、相手がメッセージを登録した際、きちんとチャット画面に表示してもらわなければなりません。イベント発火によって指定されたListenerを起動します。 geode.handson.cui.ChatMessageListener.java が実装クラスになります。

public class ChatMessageListener extends CacheListenerAdapter<String, String> implements Declarable {

    /**
     * このイベントハンドラクラスを初期化します。<br>
     * このクラスでは特に何も行いません。<br>
     * @param props Propertiesオブジェクト
     */
    @Override
    public void init(Properties props) {
    }

    /**
     * イベントとして取得したチャットメッセージを標準出力へ出力します。<br>
     * 出力フォーマット:{Key} > {Value}<br>
     * @param event EntryEventオブジェクト
     */
    @Override
    public void afterCreate(EntryEvent<String, String> event) {
        System.out.println(event.getKey() + " > " + event.getNewValue());
    }
}

CacheListenerAdapter<K, V>, Declarable というインタフェースを実装したクラスになります。今回はメッセージが登録された際に表示するだけなので、 afterCreate() のみを利用しています。これ以外にもデータが更新された前後や削除された前後など色々とフックするインタフェースが用意されているようです。

スクリーンショット 2017-03-18 15.36.09

Listenerをイベントに紐付けているのは、設定のxmlファイルです。 resources/cache.xml が実体です。

<?xml version="1.0" encoding="UTF-8"?>
<cache xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  xmlns="http://geode.apache.org/schema/cache"
  xsi:schemaLocation="http://geode.apache.org/schema/cache http://geode.apache.org/schema/cache/cache-1.0.xsd"
  version="1.0" lock-lease="120" lock-timeout="60" search-timeout="300"
  is-server="false" copy-on-read="false">
  <region name="ChatMessage">
    <region-attributes refid="REPLICATE">
      <cache-listener>
        <class-name>geode.handson.cui.ChatMessageListener</class-name>
      </cache-listener>
    </region-attributes>
  </region>
</cache>

こちらにListenerを登録しておくことで、イベントが発生した際に適切なListenerがコールされるようです。

ところでどこにもcache.xmlを参照するという設定を書くところがないのが謎です。デフォルトでcache.xmlを読んでくれるのか、はたまた自分の知らないところでcache.xmlを参照するようになっているのか。他2つの課題では明確に設定ファイルを指定していたのでデフォルト動作のような気がしなくもありません。

起動確認

StartLocatorを起動するアプリケーションとLocatorを起動するアプリケーションが必要です。起動設定を作成します。

StartLocator用の起動設定

スクリーンショット_2017-03-18_15_13_27

program argumentsに embeddedLocator という文字列を与えます。これで起動すればStartLocatorとして起動できます。

Locator用の起動設定

こちらはprogram argumentsには何も書かずに起動するのみです。特に設定は不要です。

上記が揃ったら起動してみます。

スクリーンショット 2017-03-18 15.22.57

もう一つLocatorで起動

スクリーンショット 2017-03-18 15.24.01

メッセージを送りあってみます。

Geode_chat

特になにもしてませんが、データがReplicationされ、データの登録と同時にListenerが起動されてメッセージが表示されているのがわかります。楽しい。

ハンズオン2. クライアント・サーバー型のキャッシュサーバーを構成してCLIチャットアプリケーションを改修

次はEmbeddedモードではなくキャッシュサーバーをアプリケーションとは別に構成します。まずは準備としてスタンドアローンで動作させるため、Geodeのインストールを行います。

[準備] gfshのインストール

こちら を参考にBinaryのインストールを行いましょう。Binaryをそのまま利用しても良いようですが、自分は最新のソースコードからビルドするのを選択しました。インストール手順は以下です。

  1. ソースコードのTARをまるごとダウンロード
  2. tar -zxvf apache-geode-src-1.1.0.tar.gz で解凍
  3. テストを実行せずにビルドのみを完了させるため $ ./gradlew build -Dskip.tests=true を実行 *2
  4. 成功したら apache-geode-src-1.1.0/geode-assembly/build/install/apache-geode/bin 以下にBinaryが作成されています。
  5. .bashrc 等に apache-geode-src-1.1.0/geode-assembly/build/install/apache-geode/bin までのパスを追加

上記手順が完了したら以下のコマンドでパスが通っていることを確認します。

$ gfsh version
1.1.0

キャッシュサーバー、ロケーターを作成する

キャッシュサーバーを作成します。まずは、先程パスを通した gfsh でシェルを起動します。

$ gfsh
    _________________________     __
   / _____/ ______/ ______/ /____/ /
  / /  __/ /___  /_____  / _____  /
 / /__/ / ____/  _____/ / /    / /
/______/_/      /______/_/    /_/    1.1.0

Monitor and Manage Apache Geode

気をつけなければならないのは、起動する際には必ず Locator -> Server とする必要があります(停止の場合は逆に Server -> Locator の順序)

Locatorの起動

Locatorの名前は MyLocator とします。

gfsh>start locator --name=MyLocator
Starting a Geode Locator in /path/to/geode/GeodeHandson/GeodeHandson/MyLocator...
.....
Locator in /path/to/geode/GeodeHandson/GeodeHandson/MyLocator on 192.168.105.1[10334] as MyLocator is currently online.
Process ID: 27478
Uptime: 3 seconds
Geode Version: 1.1.0
Java Version: 1.8.0_91
Log File: /path/to/geode/GeodeHandson/GeodeHandson/MyLocator/MyLocator.log
JVM Arguments: -Dgemfire.enable-cluster-configuration=true -Dgemfire.load-cluster-configuration-from-dir=false -Dgemfire.launcher.registerSignalHandlers=true -Djava.awt.headless=true -Dsun.rmi.dgc.server.gcInterval=9223372036854775806
Class-Path: /path/to/geode/apache-geode-src-1.1.0/geode-assembly/build/install/apache-geode/lib/geode-core-1.1.0.jar:/path/to/geode/apache-geode-src-1.1.0/geode-assembly/build/install/apache-geode/lib/geode-dependencies.jar

Successfully connected to: JMX Manager [host=192.168.105.1, port=1099]

Cluster configuration service is up and running.

192.168.105.1[10334] 10334ポートでLocatorが起動しました。

キャッシュサーバーの起動

続いてキャッシュサーバーを起動します。キャッシュサーバーの名前は MyCacheServer1 と指定しました。

gfsh>start server --name=MyCacheServer1
Starting a Geode Server in /path/to/geode/GeodeHandson/GeodeHandson/MyCacheServer1...
.......
Server in /path/to/geode/GeodeHandson/GeodeHandson/MyCacheServer1 on 192.168.105.1[40404] as MyCacheServer1 is currently online.
Process ID: 27505
Uptime: 3 seconds
Geode Version: 1.1.0
Java Version: 1.8.0_91
Log File: /path/to/geode/GeodeHandson/GeodeHandson/MyCacheServer1/MyCacheServer1.log
JVM Arguments: -Dgemfire.default.locators=192.168.105.1[10334] -Dgemfire.use-cluster-configuration=true -Dgemfire.start-dev-rest-api=false -XX:OnOutOfMemoryError=kill -KILL %p -Dgemfire.launcher.registerSignalHandlers=true -Djava.awt.headless=true -Dsun.rmi.dgc.server.gcInterval=9223372036854775806
Class-Path: /path/to/geode/apache-geode-src-1.1.0/geode-assembly/build/install/apache-geode/lib/geode-core-1.1.0.jar:/path/to/geode/apache-geode-src-1.1.0/geode-assembly/build/install/apache-geode/lib/geode-dependencies.jar

-Dgemfire.default.locators=192.168.105.1[10334] 先程起動したLocatorをデフォルトLocatorとして、接続しにいってるようです。

Regionの作成

最後にテーブルにあたるRegionを作成します。

gfsh>create region --name=ChatMessage --type=PARTITION
    Member     | Status
-------------- | -------------------------------------------------
MyCacheServer1 | Region "/ChatMessage" created on "MyCacheServer1"

万が一、プロセスが途中で切れてしまった場合、再度gfshを起動するかと思います。その際に以下のようなエラーが出ることがあります。

gfsh>create region --name=ChatMessage --type=PARTITION
"create region" is not available. Reason: Requires connection.

この場合、再度サーバーへ接続してあげる必要があります。`connect` を実行するとサーバーへ接続することが可能です。

gfsh>connect
Connecting to Locator at [host=localhost, port=10334] ..
Connecting to Manager at [host=192.168.11.7, port=1099] ..
Successfully connected to: [host=192.168.11.7, port=1099]

Locator, Serverの起動状態を確認

以下のコマンドで起動しているLocatorやServerの状態を確認できます。

gfsh>list members
     Name      | Id
-------------- | ---------------------------------------------------
MyLocator      | 192.168.105.1(MyLocator:27478:locator)<ec><v0>:1024
MyCacheServer1 | 192.168.105.1(MyCacheServer1:27505)<v1>:1025

PulseというWebベースのGUI管理画面も用意されています。

gfsh>start pulse
Launched Geode Pulse

スクリーンショット 2017-03-19 15.53.58

admin/admin でログインできます。

スクリーンショット 2017-03-19 15.55.09

コードを修正

ようやく準備が完了したので、先ほどと同様にチャットアプリケーションのコードを修正します。 geode.handson.cui.ChatClient.java こちらに記述を行っていきます。資料はこちらです。

/**
 * 標準入力より受け取ったメッセージをチャットリージョンへ登録します。<br>
 * リージョン登録時のキーフォーマットは、{ユーザー名}@{現在日時}@{メッセージ番号}とします。<br>
 */
public class ChatClient {
    private static final DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss.SSS");

    public static void main(String[] args) throws Exception {

        Properties props = new Properties();
        props.setProperty("cache-xml-file", "clientcache.xml"); // clientcache.xmlを指定
        ClientCacheFactory factory = new ClientCacheFactory(props);  // ClientCacheFactoryを使う
        ClientCache cache = factory.create();
        Region<String, String> region = cache.getRegion("ChatMessage");

        // 全てのキーを監視対象(これを指定しないとイベントを検知しない)
        region.registerInterest("ALL_KEYS");

        try (BufferedReader br = new BufferedReader(new InputStreamReader(System.in))) {

            System.out.println("Enter Username.");
            String user = br.readLine();

            // チャット開始 「:q」で終了
            System.out.println("Chat session start.");
            String message = null;
            int messageNo = 1;
            while ((message = br.readLine()) != null) {
                if (message.length() == 0)
                    continue;
                if (message.equals(":q")) {
                    break;
                }

                String key = String.format("%s@%s@%d", user, LocalDateTime.now().format(formatter), messageNo++);
                region.put(key, message);
            }
        }
        System.out.println("Exiting... ");

        System.exit(0);
    }
}

プログラムの中身はほとんど変わっていないように見えます。Embeddedでは起動引数によってLocatorの起動の設定が入っていましたが、今回はLocator及びServerは既に外側で起動しているため不要です。その代わり、 cacheclient.xml という設定ファイルを使うように指定する設定が入っているのが確認できます。

props.setProperty("cache-xml-file", "clientcache.xml"); // clientcache.xmlを指定

この設定によりハンズオン1のP2PChatでは cache.xml を設定として使っていましたが、clientcache.xml という設定ファイルを利用するように変更されています。clientcache.xml の内容を確認します。

<?xml version="1.0" encoding="UTF-8"?>
<client-cache xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  xmlns="http://geode.apache.org/schema/cache"
  xsi:schemaLocation="http://geode.apache.org/schema/cache http://geode.apache.org/schema/cache/cache-1.0.xsd"
  version="1.0" copy-on-read="false">

  <!-- Cache Poolの作成 -->
  <pool name="ClientPool" subscription-enabled="true">
    <locator host="localhost" port="10334" />
  </pool>

  <!-- 監視するプールの設定とListenerの設定 -->
  <region name="ChatMessage">
    <region-attributes refid="CACHING_PROXY" pool-name="ClientPool">
      <cache-listener>
        <class-name>geode.handson.cui.ChatMessageListener</class-name>
      </cache-listener>
    </region-attributes>
  </region>
</client-cache>

regionの設定内容がいくつか変更されているのとPoolの設定が追加されています。 Poolの設定では ポート10334 を接続先としていますが、これは先程起動したLocatorのポート番号になります。

regionの設定内容も若干変更されているようです。cache.xmlの region-attributes の設定では refid="REPLICATE" となっていた箇所が refid="CACHING_PROXY" という指定に変わっています。さらに pool-name="ClientPool" の指定が追加になっていました。これらの詳しい内容はこちらに記載されているようです。追々深く確認してみたいと思います。

Cacheインスタンスを取得するFactoryは ClientCacheFactory に変更されていることにも注意が必要です。

最後に以下を必ず記述します。

// 全てのキーを監視対象(これを指定しないとイベントを検知しない)
region.registerInterest("ALL_KEYS");

特定のキーのデータ更新イベントをフィルタするかが指定できるようです。今回は全てのキーを検出対象にしています。この指定がない場合、あるクライアントがキャッシュサーバーにデータを追加しても他のクライアントは全くイベントを検知することができません。

動作確認

起動を確認します。Embeddedのときとは異なり、特に起動設定を分ける必要もありません。

chat_geode_multi

キャッシュサーバーに登録されているデータを確認します。Pulseの画面からクエリを叩いてデータを確認します。

スクリーンショット_2017-03-19_16_49_27

2つのクライアントから投稿したメッセージが表示されています。さらに右側のペインでは様々なイベントが発行されていることが確認できます。

スクリーンショット 2017-03-19 16.49.32

これでクライアント・サーバー型のハンズオンは完了です。

最後にGUI版のハンズオンもあったのですが、ほとんど内容が変わらないため割愛します。念のため起動しているスクリーンショットのみ。

スクリーンショット 2017-03-14 20.49.23

トラブルシューティング

  • コンソールでRegionを作らずにアプリケーションを実行しようとすると以下のような例外が発生し、アプリケーションが終了します。Regionを作成してあるかを確認してください。
Exception in thread "main" org.apache.geode.cache.client.ServerOperationException: remote server on 192.168.11.7(67010:loner):50205:9f0971e5: org.apache.geode.cache.client.internal.RegisterInterestOp$RegisterInterestOpImpl@6179e425: While performing a remote registerInterest
    at org.apache.geode.cache.client.internal.RegisterInterestOp$RegisterInterestOpImpl.processResponse(RegisterInterestOp.java:243)
    at org.apache.geode.cache.client.internal.AbstractOp.attemptReadResponse(AbstractOp.java:159)
    at org.apache.geode.cache.client.internal.AbstractOp.attempt(AbstractOp.java:382)
    at org.apache.geode.cache.client.internal.ConnectionImpl.execute(ConnectionImpl.java:266)
    at org.apache.geode.cache.client.internal.QueueConnectionImpl.execute(QueueConnectionImpl.java:165)

質疑応答

覚えている限りになりますが、色々と疑問に思ったことをざっくばらんに聴いてきました。

Q. アプリ内にEmbeddedする場合、メモリ空間はアプリと共有すると思うんですけどヒープ全部食いつぶしたりする?

  • する可能性はある。Full GCを避けるならJavaのヒープの外(Off-heap memory)にデータを出すことで回避も可能。

Q. マルチリージョンは当然だとしてマルチクラウド出来るの?

  • 実績あるよ。キャッシュサーバー同士が疎通できれば特に制限なくなんでもいける。AWSにクラスターを組んでいるところにローカルのマシンを参加させたりとかできる

Q. OQLのパフォーマンスは?

  • OQLはリージョンをまたぐ複雑なもの(要するにRDBでいうテーブルをまたいだクエリとか?)でなければ、膨大なデータ(数億?うろ覚えです・・・)を突っ込んでてもパフォーマンスは余裕で出る。

Q. パフォーマンスを出すためには?

  • パフォーマンスを出すためにはデータの設計が大事。当然だけど、Full ReplicationやPartitionningなどの戦略を用途に応じてきちんと使い分けること。

Q. 実際使うならどれくらい覚えればいいの?

  • 所詮データストアだからPutとGetくらいの理解で大体OK(単なるPub/Subとして使ってる人もいる)
  • 裏側でDBの永続化とかそういう話は実用させるならばほぼ必ず出てくる。単体で解決するのではなく組み合わせてうまく解決する
  • キャッシュの設定ファイルの属性値が大量にあるので、結構大変
  • gemfireの設定が特に膨大な量があるため、細かくカスタマイズしたいならばこのあたりを確認すると良い
  • Geodeの公式にチュートリアルがあるのでそこを見ておくと良いかも

Q. ハマりどころとかある?

  • 上げる順番、落とす順番等々、使っていると色々あるよ(まあ、でもそれはどんなもの使ってても同じでは)
  • 3日くらいで大体の機能は把握できる。あとは実践あるのみ!

Q. 他に似たようなものってある?

実際はこちらは講師の方から、「なんで皆さんIgniteとかじゃなくてGeodeなんですか?」という質問から派生した内容です。

  • 派生プロダクトでSnappydataというのがある。gemfireの人たちが出ていって作られた。 SnappyData
  • Apache Igniteは元々のバックグラウンドは全然違うんだけど、結果的に同じような機能のプロダクトになった。 Apache Ignite

以上です。

まとめ

こうしてやった内容を復讐しながら記述してみると、知っておくべきことや新しく知ることが盛り沢山でした。

  • インメモリーデータグリッドを構成する
  • データ更新のイベント発火をListenすることができる
  • OQLクエリによってKVSだが検索ができる
  • Locator, CacheServerという登場人物がいる
    • クライアントは全てLocatorへ問い合わせを行う(REST以外)
  • デフォルトでは cache.xml を利用して起動される(?)
  • Embeddedモードとクライアント・サーバーモードがある
  • Embeddedモード
    • 必ず1つ目のアプリケーションはStartLocatorを起動する。2つ目以降は、StartLocatorを指定し、通常のLocatorを起動する
    • コードでは、 CacheFactory を利用してCacheインスタンスを取得する
  • クライアント・サーバーモード
    • コードでは、 ClientCacheFactory を利用してCacheインスタンスを取得する
    • registerInterest で必ずキーのフィルター設定を記述する
    • CachePoolの設定が必要になる
  • コンソール
    • 必ずLocator -> Serverの順序で起動する(停止する場合はその逆)
    • サーバーへ再接続する場合は、 connect を実行する
    • PulseというWebベースの管理画面が存在する

短い時間で実際に動くものを体験できたので、とても刺激的で楽しかったです。まだまだ機能のほんの一部しか使ってない上、設定もほんの一部しか触っていません。自分が困っている箇所で少しでも活用できるような場面があれば、積極的に活用できるよう色々と調査を行っておこうかと思います。

参照資料

脚注

  1. このあたりは公式の受け売り的な感じですが・・・(実際にはまだ体感できてません。)
  2. テストを実行しても良いですが、割りと長い時間かかります。