Esperを動かしてみた
K.N.です。久しぶりに技術ブログを書きます。
今回は『Esper』について。意外に古めのライブラリですが、Esperそのもについての日本語の情報があまりなかったので、動作環境を作ってみました。
Esperについて
- EsperはCEP(Complex Event Processing)
 - CEP(complex event processing)とは、時系列で発生するストリームデータをコンピュータのインメモリーに展開し、あらかじめ設定した分析シナリオの条件に合致したら「特定のイベントが発生した」と判断し、それに対応するアクションを実行すること
 - Esperはイベントを処理するアプリケーションのためのJavaコンポーネントです。経時的なイベントにも論理的なイベントにマッチさせるこのとできるイベントパターン言語(EPL)により、イベントの相関関係を扱うことができます。イベントストリームの結合、統計的な解析とフィルタリング、グルーピング、それらすべてをリアルタイムで処理することができます。
 
使い方的には
- Java or .NETのライブラリであり、zipファイルを展開し、java等のコードから呼び出せば使える
 - イベントの型を定義、EPLクエリのインスタンスに実行したい処理のイベントリスナーを登録しておく
 - イベントを送信(イベント型のインスタンスを生成・登録)すると、リスナーが呼ばれる
 
といった感じです。
実行環境の作成
Oracle Virtual Boxを使って、Windows上でUbuntu仮想環境を作成します。
VirtualBoxの最新 5.0.16をインストール
ダウンロード(Ubuntu 14.04 TLS)
http://cdimage.ubuntulinux.jp/releases/14.04/ubuntu-ja-14.04-desktop-amd64-vhd.zip
[参考] VirtualBoxでの仮想環境作成方法
https://www.ubuntulinux.jp/download/ja-remix-vhd
[参考] ホストとのファイル共有方法
http://qiita.com/HirofumiYashima/items/6044cfc64cfa3e84f97c
Javaのインストール
EsperはJavaで実装されているのでJVMをインストールします。
$ sudo apt-get install default-jdk
open-jdk/jre 7 がインストールされる
JAVA_HOMEを設定する
[参考] http://forco.hateblo.jp/entry/2015/04/05/035621
bashrcへJAVA_HOMEの設定を追加する
Versionの切り替えに応じて自動でパスを切り替える
$ vim ~/.bashrc
#一番下へ↓4行を追加
JAVA_HOME=$(readlink -f /usr/bin/javac | sed "s:/bin/javac::")
export JAVA_HOME
PATH=$PATH:$JAVA_HOME/bin
export PATH
$ source ~/.bashrc
Esperのインストールと設定
インストール
Esper for Java 5.3.0 の OpenSource版を以下からダウンロードする
http://www.espertech.com/esper/download.php
[参考] ソースが欲しい場合はgithubから
https://github.com/espertechinc/esper
zip(またはtar.gz)を展開し、任意のフォルダに配置するだけで、インストール作業は完了。
exampleの実行
同梱されているexample/autoidを実行してみる。
流れとしてはexample/autoid/etc内のシェルスクリプトをキックしてコンパイル、実行する形になる。
$ cd example/autoid/etc
シェルスクリプトの改行コードがCRLFになっていて、実行時にエラーになるので改行コードをLFに変更しておく
- run_autoid.sh
 - setenv.sh
 - compile.sh
 
nkf を使って改行コードを変更
$ sudo apt-get install nkf
$ nkf -Lu --overwrite *.sh
run_autoid.sh内の. setevn.shは実行時に「not found」となったので. ./setenv.shまたはsource setenv.shに書き換える。
同様にcompile.sh内も変更する
$ sed -i -e 's/. setenv.sh/. .\/setenv.sh/g' compile.sh
$ sed -i -e 's/. setenv.sh/. .\/setenv.sh/g' run_autoid.sh
compile.sh でコンパイルする
$ sh compile.sh
警告: [options] ブートストラップ・クラスパスが-source 1.6と一緒に設定されていません
警告1個
run_autoid.sh で実行する。1000以下の数値を引数として指定する。
$ sh run_autoid.sh 5
15:21:20,436 INFO  [RFIDTagsPerSensorListener] Sensor urn:epc:1:4.16.30 totals 3.0 tags
15:21:20,438 INFO  [RFIDTagsPerSensorListener] Sensor urn:epc:1:4.16.32 totals 1.0 tags
15:21:20,441 INFO  [RFIDTagsPerSensorListener] Sensor urn:epc:1:4.16.38 totals 2.0 tags
15:21:20,443 INFO  [RFIDTagsPerSensorListener] Sensor urn:epc:1:4.16.38 totals 4.0 tags
15:21:20,445 INFO  [RFIDTagsPerSensorListener] Sensor urn:epc:1:4.16.32 totals 3.0 tags
動いた
処理を実装してみる
以下の記事を元に(真似して)コードを動かしてみる(感謝)
[参考]http://fits.hatenablog.com/entry/20081126/1227660571
Gradleのインストール
ビルドを簡単にするために、apt-get でGradleをインストールする
$ sudo apt-get install gradle
gradleがインストールされたか gradle -v でバージョン表示して確認
$ gradle -v
------------------------------------------------------------
Gradle 1.4
------------------------------------------------------------
Gradle build time: 2013年9月9日 20時44分25秒 UTC
Groovy: 1.8.6
Ant: Apache Ant(TM) version 1.9.3 compiled on April 8 2014
Ivy: non official version
JVM: 1.7.0_95 (Oracle Corporation 24.95-b01)
OS: Linux 3.13.0-24-generic amd64
プロジェクトの作成
プロジェクト用フォルダを作成する
$ mkdir esper_sample
$ cd esper_sample
$ mkdir -p src/main/java/sample
esperのライブリをプロジェクト/libにコピーしておく
$ cp -a ../esper-5.3.0/esper/lib .
$ cp -a ../esper-5.3.0/*.jar lib
Gradleのビルドファイル build.gradle をプロジェクトの直下に作成
$ vi build.gradle
apply plugin: 'java'
dependencies {
    compile fileTree(dir: 'lib', include: '*.jar')
}
POJOの単純なイベントクラス SampleEvent を作成。
$ vi src/main/java/sample/SampleEvent.java
package sample.event;
public class SampleEvent {
    private String name;
    private int point;
    public SampleEvent(String name, int point) {
        this.name = name;
        this.point = point;
    }
    public String getName() {
        return this.name;
    }
    public int getPoint() {
        return this.point;
    }
}
処理クラス SampleProcessor を記述。
$ vi src/main/java/sample/SampleProcessor.java
Esperライブラリがバージョンアップのために、参考サイトのソースで修正した箇所は以下。
- EventBeanのパッケージ参照 com.espertech.esper.event -> com.espertech.esper.client
 - config.addEventTypeAlias -> config.addEventType
 
package sample;
import com.espertech.esper.client.Configuration;
import com.espertech.esper.client.EPServiceProvider;
import com.espertech.esper.client.EPServiceProviderManager;
import com.espertech.esper.client.EPStatement;
import com.espertech.esper.client.UpdateListener;
import com.espertech.esper.client.EventBean;
import sample.event.SampleEvent;
public class SampleProcessor {
    public static void main(String[] args) {
        //イベント名とイベントクラスのマッピング定義
        Configuration config = new Configuration();
        config.addEventType("SampleEvent", SampleEvent.class);
        EPServiceProvider serv = EPServiceProviderManager.getDefaultProvider(config);
        //EPL から EPStatement オブジェクトを生成
        EPStatement st = serv.getEPAdministrator().createEPL("select * from SampleEvent(point >= 5)");
        //以下でも可
        //EPStatement st = serv.getEPAdministrator().createEPL("select * from SampleEvent where point >= 5");
        //イベントリスナーの設定
        st.addListener(new UpdateListener() {
            //イベント処理(point が 5以上のもののみ処理される)
            public void update(EventBean[] newEvents, EventBean[] oldEvents) {
                if (newEvents != null && newEvents.length > 0) {
                    System.out.println("event : " + newEvents[0].get("name") + ", " + newEvents[0].get("point"));
                }
            }
        });
        //イベント送信
        for (int i = 0; i < 10; i++) {
            int val = (int)(Math.random() * 10);
            //ランダムな値を設定したイベント送信
            serv.getEPRuntime().sendEvent(new SampleEvent("test" + i, val));
        }
    }
}
gradle compilejava でコンパイルする
$ gradle compilejava
BUILD SUCCESSFUL
gradleでサンプルを実行できるように設定ファイル build.gradle を書き換える
$ vi build.gradle
apply plugin: 'application'
dependencies {
    compile fileTree(dir: 'lib', include: '*.jar')
}
mainClassName = 'sample.SampleProcessor'
gradle run で実行する
$ gradle run
:compileJava UP-TO-DATE
:processResources UP-TO-DATE
:classes UP-TO-DATE
:run
log4j:WARN No appenders could be found for logger (com.espertech.esper.util.ObjectInputStreamWithTCCL).
log4j:WARN Please initialize the log4j system properly.
log4j:WARN See http://logging.apache.org/log4j/1.2/faq.html#noconfig for more info.
event : test0, 9
event : test2, 7
event : test3, 5
event : test4, 9
event : test5, 9
event : test8, 9
event : test9, 9
BUILD SUCCESSFUL
Total time: 12.247 secs
動いたので目的は達成。あとはじっくりサンプルコードを読み解く。
情報
参考情報(EsperTech)
QuickStart
http://www.espertech.com/esper/quickstart.php
Tutorial
http://www.espertech.com/esper/tutorial.php
API JavaDoc
http://www.espertech.com/esper/release-5.3.0/esper-javadoc/index.html
動的にクエリ(EPL)を追加する方法
動的にEsperにクエリ(EPL)を登録する方法がないのでは?という疑問が湧いてくる。大抵ソースコード上にEPLを書いて、Javaとして実行させるような例ばかりなので、自前で実装する必要があるんではなかろうか。
Norikra のソースを見ると、JRubyを利用してJavaのEsperライブラリを呼び出し、動的にEPLとリスナーを登録している。Norikraではクエリの追加が動的に行えるので、Norikraが使えるのであれば、よい選択肢になりそう。
なお、どのような手法でもクエリを追加する前のデータは保持されていない。
保持されるデータ=クエリにマッチしたデータとなる
