2
2

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 5 years have passed since last update.

Apache Geode - お手軽にサーバーサイドでロジックを実行する方法

Last updated at Posted at 2018-09-26

#はじめに
分散システム型 KVS 実装の一つである Apache Geode にて、サーバーサイドで何かロジックを実行したいときには Function Execution という機能を使うのが一般的です。ただ、 Functoin Execution は高機能であるため、設定項目や実ロジック以外のコーディング量が多くなりがちで、特定ロジックをクラスター内のデータ配置とか関係なくいずれかのキャッシュサーバーノードで一度だけ実行して返り値も要求しない、といった単純なケースに使うのはやや気が引けるかもしれません。

本記事では、Apache Geode にて、Function Execution 機能を使わずにもっとお手軽にサーバーサイドでロジックを実行する方法を紹介します。以下、任意のロジックを実行する実体を「コマンド」と呼称することにします。なお、本記事の方法は Apache Geode 1.6.0 にて動作確認済みです。

#目的
本記事では、単純なサーバーサイドでのロジック実行例として、以下の内容を Function Execution 機能を使わずに設定・実装することとします。

  • 指定したリージョンのデータ全削除コマンド(つまり、Regoin#clear の実行)を、クラスター内のいずれかのキャッシュサーバーノードで一度だけ実行、返り値なし

#仕組み
以下のような仕組みで、Function Execution 機能を使わずにサーバーサイドでロジックを実行します。

  1. ロジックを実行するために専用のリージョンを用意(以下、Command リージョン) - データ格納しないのでプロキシーリージョンで良い(refid=REPLICATED_PROXY
  2. Command リージョンに、コマンドオブジェクトをバリューとして put - キーは使わないので任意の値
  3. サーバーサイドで事前に Command リージョンに付与した CacheWriter で、バリューとして渡されたコマンドを実行

ここで、一般的によく使われる CacheListner ではなく CacheWriter を使う理由は、前者は基本的に付与されたキャッシュサーバー全てが反応してしまってコマンドが重複実行される可能性があるのに対して、後者は付与されたキャッシュサーバーのいずれか一つで実行されるからです。

参考までに、以下実行イメージ図を掲載します。
20180926-executionImage.png

#コマンドの作成
方針としてはコマンド毎に個別にクラスを用意することを前提に、CacheWriter でコマンドを実行するロジックを共通化するために、以下のようなコマンドインターフェースを定義します。

Command.java
public interface Command {
  public void process();
}

次に、これを実装する形で、指定したリージョンのデータ削除コマンドクラスを以下のように記述します。このコマンドクラスをインスタンス化する際に指定するリージョン(regionName)を元に、コマンドインターフェースで定義した process メソッドで Region#clear を実行します。

ClearRegionCommand.java
import org.apache.geode.cache.CacheFactory;
import org.apache.geode.cache.Region;

import java.io.Serializable;

public class ClearRegionCommand implements Command, Serializable {
  private String regionName;
  
  public ClearRegionCommand(String regionName) {
    this.regionName = regionName;
  }
  
  public void process() {
    Region region = CacheFactory.getAnyInstance().getRegion(this.regionName);
    if (region != null) {
      region.clear();
    }
  }
}

なお、コマンドオブジェクト自体がネットワーク経由でサーバーに送信されるので、シリアライズ可能なオブジェクトとして実装する必要があります。ここでは、java.io.Serializable を実装して済ませていますが、Apache Geode ということでオブジェクトシリアライズ技術として PDX を使ってもいいですね。

そして、コマンドを実行する CacheWriter の実装です。beforeCreate メソッド内で、put により発生するイベントから、コマンドオブジェクトを取得して process メソッドを実行するという内容です。ちなみに、CacheWriter は多くの場合キャッシュ設定ファイル(以下、cache.xml)で定義することを考えて、念のため Declarable を実装しておきます。

ProcessCommandCacheWriter.java
import org.apache.geode.cache.Cache;
import org.apache.geode.cache.CacheWriterException;
import org.apache.geode.cache.Declarable;
import org.apache.geode.cache.EntryEvent;
import org.apache.geode.cache.util.CacheWriterAdapter;

import java.util.Properties;

public class ProcessCommandCacheWriter extends CacheWriterAdapter<Integer,Command> implements Declarable {
  public void beforeCreate(EntryEvent<Integer,Command> event) throws CacheWriterException {
    Command command = event.getNewValue();
    command.process();
  }

  public void initialize(Cache cache, Properties properties) { }

  public void close() {}
}

#CacheWriter の設定
ここでは、cache.xml を使って設定します。refidREPLICATE_PROXY を設定した Command リージョンに付与する形となります。Example リージョンは、今回実装したコマンドにより Regoin#clear 実行対象となるサンプルリージョンとなります。

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">

    <cache-server port="0" />

    <region name="Example" refid="REPLICATE" />

    <region name="Command" refid="REPLICATE_PROXY">
        <region-attributes>
            <cache-writer>
                <class-name>ProcessCommandCacheWriter</class-name>
            </cache-writer>
        </region-attributes>
    </region>
</cache>

#CLASSPATH の設定
クライアントから投入されたコマンドは、サーバーで実行されるので、サーバーの CLASSPATH に各種関連クラスへのパスを設定する必要があります。コマンドクラス本体(ここでは、ClearRegionCommand)は必要に応じて gfsh deploy コマンドで動的に設定可能ですが、サーバー起動時には最低限以下のクラスへのパスを設定する必要があります。

  • CacheWriter クラス(ここでは、ProcessCommandCacheWriter クラス)
  • コマンドインターフェース(ここでは、Command インターフェース)

インターフェースは、gfsh deploy コマンドでは CLASSPATH 設定できないようです

#クライアントからコマンド投入
以下のように、コマンドオブジェクトを作成して Command リージョンにバリューとして put するようなコーディングをします。

ClientCache cache = new ClientCacheFactory()
  .set("cache-xml-file", "client-cache.xml")
  .create();
cache.getRegion("Command").put(0, new ClearRegionCommand("Example"));

参考までに、以下、クライアントサイドのサンプル cache.xml を掲載します。

client-cache.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">

    <pool name="MyPool" subscription-enabled="true">
        <locator host="xxx.xxx.xxx.xxx" port="xxxxx" />
    </pool>

    <region name="Example">
        <region-attributes pool-name="MyPool" refid="CACHING_PROXY" />
    </region>

    <region name="Command">
        <region-attributes pool-name="MyPool" refid="PROXY" />
    </region>
</client-cache>

#最後に
記事としてまとめてみると、Function Executoin の場合同様、思ったよりもコードとか設定内容が多い気がしますが、クライアントからのコマンド投入が一行で済むのは利用者にとっては、お手軽かな、と思っています。

さらに、今回ご紹介した方法ですと、Command リージョンに Gateway Sender の設定を追加することで、WAN 経由で、Gateway Receiver を設定した別クラスターでも任意のロジック実行という離れ業も可能です。ご興味と需要があれば試してみてください。

2
2
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
2
2

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?