Norikraで遊んでみた

> Norikraも0.1になり、この内容もかなり古くなりました。
> Norikra0.1にあわせて新しく書き直しているので下記内容をご参照ください！
> [Norikra 0.1を使ってみた](http://qiita.com/harukasan/items/a7c1dd1a11a61cd1ad75)

Norikraで遊んでみた。

RubyKaigiで[@tagomoris](http://twitter.com/tagomoris)さんが発表していたNorkraをとりあえず手元で動かしてみた。すごく簡単に説明するとFluentdみたいなデータストリームをSQLライクな言語でごにょごにょできるものである。
NorikraのスライドはSlideShareにあるので是非見ておこう。

- [Complex Event Processing on Ruby, Fluentd and Norikra #rubykaigi](http://www.slideshare.net/tagomoris/rubykaigi-2013-111130)

この記事はおそらく一瞬で古くなると思うが、環境は次のような感じだ。

| product          | version |
|:------------------|:--------------|
| CRuby(MRI)   | 2.0.0-p195 |
| JRuby             | 1.7.4 |
| Norikra            | v0.0.4 |
| Norikra::Client | v0.0.3 |
 

CRuby、JRubyはrbenvとruby-buildを使ってインストールしている。

## JRubyとNorikraをインストールする

```bash
$ rbenv install jruby-1.7.4
$ rbenv shell jruby-1.7.4 # このシェルだけJRubyに切り替える
$ ruby -v 
jruby 1.7.4 (1.9.3p392) 2013-05-16 2390d3b on Java HotSpot(TM) 64-Bit Server VM 1.6.0_45-b06-451-11M4406 [darwin-x86_64]

$ gem install norikra
# …snip…
Successfully installed norikra-client-jruby-0.0.3-java
Successfully installed norikra-0.0.4-java
15 gems installed
```

こんなかんじでインストールできた。JRubyはいっこいっこのコマンドがすごくもっさりなので注意だ。

## とりあえず起動する

```bash
$ rbenv rehash # 一旦rehashを叩く
$ norikra start
log4j:WARN No appenders could be found for logger (com.espertech.esper.core.service.EPServiceProviderImpl).
log4j:WARN Please initialize the log4j system properly.
log4j:WARN See http://logging.apache.org/log4j/1.2/faq.html#noconfig for more info.
"Norikra server started."

```

## norikra-clientを使ってみる

起動したようなので別のシェルからnorikra-clientを叩いてみる。
JRubyだとうまく動かなかったけど、requireが異なるから起こってただけだったのでさくっと書き換えた。

```diff
--- .rbenv/versions/jruby-1.7.4/lib/ruby/gems/shared/gems/norikra-client-jruby-0.0.3-java/lib/norikra/client.rb.original        2013-06-04 10:22:08.000000000 +0900
+++ .rbenv/versions/jruby-1.7.4/lib/ruby/gems/shared/gems/norikra-client-jruby-0.0.3-java/lib/norikra/client.rb 2013-06-04 10:12:44.000000000 +0900
@@ -1,6 +1,6 @@
 require "norikra/client/version"

-require 'msgpack-rpc-over-http'
+require 'msgpack-rpc-over-http-jruby'
```

これで動くようになった。CRubyだとこの問題起こらないし、動作さくさくなので普通にCRubyにnorikra-client入れた方が良い。

```bash
$ norikra-client
Commands:
  norikra-client event CMD ...ARGS   # send/fetch events
  norikra-client field CMD ...ARGS   # manage target field/datatype definitions
  norikra-client help [COMMAND]      # Describe available commands or one specific command
  norikra-client query CMD ...ARGS   # manage queries
  norikra-client target CMD ...ARGS  # manage targets

Options:
  [--host=HOST]
                 # Default: localhost
  [--port=N]
                 # Default: 26571
```

Norikraでは所謂テーブルの事をtarget、テーブルのカラムのことをfield、クエリのことをquery、入力するログや出力される結果のことをeventと呼ぶ。

とりあえずnorikra-clientで、適当なtargetを作ってみる。

```bash
$ norikra-client target open access user:string path:string
$ norikra-client target list
TARGET
access
1 targets found.
```

で、適当なqueryを書いて登録する。NorikraのクエリはEsperのEPLというSQLライクな言語を使って書く。

```bash
$ norikra-client query add access_count "SELECT user, count(*) AS access_count FROM access.win:time_batch(10 sec) GROUP BY user"
```

あとは適当なeventを流す。

```bash
$ echo '{"user":"kame", "path":"http://example.com/"}' | norikra-client event send access
$ echo '{"user":"kame", "path":"http://example.com/"}' | norikra-client event send access
$ echo '{"user":"kame", "path":"http://example.com/"}' | norikra-client event send access
$ echo '{"user":"usagi", "path":"http://example.com/"}' | norikra-client event send access
$ echo '{"user":"usagi", "path":"http://example.com/"}' | norikra-client event send access
$ echo '{"user":"usagi", "path":"http://example.com/"}' | norikra-client event send access
$ echo '{"user":"usagi", "path":"http://example.com/"}' | norikra-client event send access
```

で、てきとうに結果のeventを見てみる。

```bash
$ norikra-client event fetch access_count
{"time":"2013/06/04 10:50:20","user":"kame","access_count":3}
{"time":"2013/06/04 10:50:20","user":"usagi","access_count":4}
$ norikra-client event fetch access_count
{"time":"2013/06/04 10:50:30","user":"kame","access_count":0}
{"time":"2013/06/04 10:50:30","user":"usagi","access_count":0}
```

こんな感じで結果が返ってくるのがわかる。こんなかんじでSQLライクなクエリでGROUP BYとかが簡単に行えるのである。

## Fluentdからログを流したい

実際に使うのであればFluentdからログを流したい。Norikraはfluent-plugin-norikra
を使うことでFluentdとメッセージのやりとりができる。

```bash
$ gem install fluent-plugin-norikra
```

fluent.confに直接Norikraのクエリを記述することもできるが、直接クエリを書いてしまうと変更する度にFluentdを再起動しなくてはいけなくなる。できるのであればFluentdの設定には、データをNorikraに流して結果を受け取ることだけを書きたい。

これを実現できる機能がEvent sweepingのようだ。`<event>`セクションを記述することでこれが実現できる。

```fluent.conf
<source>
  type forward # listen port 24224
</source>

<match input.*>
  type copy

  <store>
    type stdout # 標準出力で確認できるように
  </store>

  <store>
    type norikra
    norikra localhost:26571 # デフォルト設定

    target_map_tag yes      # tagをtarget名に使う
    remove_tag_prefix input # tagのprefixを除去する

    <event>
      method sweep
      tag    query_name        # query名をタグにする
      tag_prefix norikra.event # tag: norikra.event.QUERYNAME
      sweep_interval 5s        # 出力間隔
    </event>
  </store>
</match>


<match norikra.**>
  type stdout # 結果も標準出力で確認する
</match>
```

fluent-plugin-norikraにはNorikraを自動起動する設定とかも書けるが、裏側でエラーが出てないか確認したかったので、とりあえず最低限の設定だけ書いている。Norikraは起動したままでFluentdを起動する。

```bash
$ fluentd -c fluent.conf
# …snip…
2013-06-04 10:55:22 +0900 [info]: adding source type="forward"
2013-06-04 10:55:22 +0900 [info]: adding match pattern="input.*" type="copy"
2013-06-04 10:55:22 +0900 [info]: adding match pattern="norikra.**" type="stdout"
2013-06-04 10:55:22 +0900 [info]: listening fluent socket on 0.0.0.0:24224
```

起動したら別のシェルを開いて、fluent-catからデータを流してみる。流すデータは先程と同じだ。

```bash
$ echo '{"user":"kame", "path":"http://example.com/"}' | fluent-cat input.access
$ echo '{"user":"kame", "path":"http://example.com/"}' | fluent-cat input.access
$ echo '{"user":"kame", "path":"http://example.com/"}' | fluent-cat input.access
$ echo '{"user":"usagi", "path":"http://example.com/"}' | fluent-cat input.access
$ echo '{"user":"usagi", "path":"http://example.com/"}' | fluent-cat input.access
```


```bash
2013-06-05 10:58:42 +0900 input.access: {"user":"kame","path":"http://example.com/"}
2013-06-05 10:58:46 +0900 input.access: {"user":"kame","path":"http://example.com/"}
2013-06-05 10:58:51 +0900 input.access: {"user":"kame","path":"http://example.com/"}
2013-06-05 10:58:53 +0900 norikra.event.access_count: {"user":"kame","access_count":3}
2013-06-05 10:58:55 +0900 input.access: {"user":"usagi","path":"http://example.com/"}
2013-06-05 10:58:56 +0900 input.access: {"user":"usagi","path":"http://example.com/"}
2013-06-05 10:59:03 +0900 norikra.event.access_count: {"user":"kame","access_count":0}
2013-06-05 10:59:03 +0900 norikra.event.access_count: {"user":"usagi","access_count":2}
```

こんなかんじで結果セットをFluentdに流すことができた！

## まとめ

ということで、出たばかりのNorikraを使ってみた。Norikra使うとSQLライクな感じでログをフィルタできて楽しいし、なにより動的にクエリを追加したり削除したりできるのが嬉しい。

まだまだ、登録したqueryの削除が実装されてないとか、上記以外の動作だと結構エラー出たりとか実用するにはちょっと足りてなさそうだけど、今後に期待である。