創屋ぷれす

Esperを動かしてみた

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が使えるのであれば、よい選択肢になりそう。

なお、どのような手法でもクエリを追加する前のデータは保持されていない。
保持されるデータ=クエリにマッチしたデータとなる

創屋のホームページはこちらから

Comments are closed.