このエントリーをはてなブックマークに追加

riak_coreを使ってみる その1

riakはBasho社が作成している分散データベースです。実はRiakはriak_coreという分散処理を行う機能と、riakというデータベース機能(Riak K/V)とに分かれています。

そして、riak_coreは汎用的なフレームワークになっており、riakというデータベース以外の用途にも使えるようになっています。

今回は、途中までではありますが、触ってみたところを書きます。

riak_coreが提供する機能

http://www.slideshare.net/argv0/riak-coredevnation参照

  • Virtual Node
  • Preference Lists
  • Ring Event Watcher
  • Node Event Watcher

参考文献

最初に読む場所
http://basho.com/where-to-start-with-riak-core/
サンプルアプリケーション
http://docs.basho.com/riak/1.4.0/references/appendices/community/Sample-Applications/
37ページからBuilding an Application on Riak Core
http://www.erlang-factory.com/upload/presentations/294/MasterlessDistributedComputingwithRiakCore-RKlophaus.pdf
riak_core 入門 by kuenishiせんせい
https://github.com/kuenishi/riak_scr_jp/blob/master/%234/ss-riak-core-intro.rst
riak_core モジュールドキュメント
http://basho.github.io/riak_core/

サンプルコード

try-try-try サンプルコード
https://github.com/rzezeski/try-try-try
howl
https://github.com/project-fifo/howl

古いものなのですが、そのまま通用するのがすごいところ。

作ってみる

riak_coreを使ったアプリケーションを作成するにはまずhttps://github.com/basho/rebar_riak_coreを使ってテンプレートを作成します。(rebarは事前に入れておいてください)

% git clone git://github.com/basho/rebar_riak_core.git
% mkdir -p ~/.rebar/templates
% cp rebar_riak_core/* ~/.rebar/templates
% mkdir <プロジェクト名>
% cd <プロジェクト名>
% rebar create template=riak_core appid=<プロジェクト名>

これでテンプレートの出来上がりです。以下のようなファイル構造が出来上がります。src以下は重要なので中身も書いてみました。

今回は、プロジェクト名をdpingにしたので、そのつもりで読んでください。

apps/
Makefile
README.md
rebar
rebar.config
rel/
src/
├── dping_app.erl
├── dping.app.src
├── dping_console.erl
├── dping.erl
├── dping.hrl
├── dping_node_event_handler.erl
├── dping_ring_event_handler.erl
├── dping_sup.erl
├── dping_vnode.erl
└── dping_wm_ping.erl

そのあとは、以下のようにすれば動きます。

% make rel
% ./rel/dping/bin/dping console

14:13:42.486 [info] Waiting for application dping to start (0 seconds).
14:13:42.489 [info] Application dping started on node 'dping@127.0.0.1'
Eshell V5.9.1  (abort with ^G)
(dping@127.0.0.1)1> 14:13:42.588 [info] Wait complete for
application dping (0 seconds)

(dping@127.0.0.1)2> dping:ping().
{pong,959110449498405040071168171470060731649205731328}

consoleでdping:ping()を実行して、というのが出れば成功です。q()で終わります。

これは単一ノードだけですが、devrelを使うと4つのノードを作ってくれます。

% make devrel
mkdir -p dev
rel/gen_dev dev1 rel/vars/dev_vars.config.src
rel/vars/dev1_vars.config
Generating dev1 - node='dping1@127.0.0.1' http=10018 handoff=10019
(cd rel && ../rebar generate target_dir=../dev/dev1
overlay_vars=vars/dev1_vars.config)
==> rel (generate)
mkdir -p dev
rel/gen_dev dev2 rel/vars/dev_vars.config.src
rel/vars/dev2_vars.config
Generating dev2 - node='dping2@127.0.0.1' http=10028 handoff=10029
(cd rel && ../rebar generate target_dir=../dev/dev2
overlay_vars=vars/dev2_vars.config)
==> rel (generate)
mkdir -p dev
rel/gen_dev dev3 rel/vars/dev_vars.config.src
rel/vars/dev3_vars.config
Generating dev3 - node='dping3@127.0.0.1' http=10038 handoff=10039
(cd rel && ../rebar generate target_dir=../dev/dev3
overlay_vars=vars/dev3_vars.config)
==> rel (generate)
mkdir -p dev
rel/gen_dev dev4 rel/vars/dev_vars.config.src
rel/vars/dev4_vars.config
Generating dev4 - node='dping4@127.0.0.1' http=10048 handoff=10049
(cd rel && ../rebar generate target_dir=../dev/dev4
overlay_vars=vars/dev4_vars.config)
==> rel (generate)

こうしておいて、

以下のようにしてstartとjoinをします。

shirou@rudi:~/Works/dping$ for d in dev/dev*; do $d/bin/dping start;
done
shirou@rudi:~/Works/dping$ dev/dev3/bin/dping-admin join dping1@127.0.0.1
Sent join request to dping1@127.0.0.1
shirou@rudi:~/Works/dping$ for d in dev/dev*; do $d/bin/dping stop;
done

書いてみる

rebarで作成された中のdping_vnode.erlをみてみると、こんな感じの行があります。これらを埋めていけばいいわけですね。

%% Sample command: respond to a ping
handle_command(ping, _Sender, State) ->
    {reply, {pong, State#state.partition}, State};
handle_command(Message, _Sender, State) ->
    ?PRINT({unhandled_command, Message}),
    {noreply, State}.

handle_handoff_command(_Message, _Sender, State) ->
    {noreply, State}.

handoff_starting(_TargetNode, State) ->
    {true, State}.

handoff_cancelled(State) ->
    {ok, State}.

handoff_finished(_TargetNode, State) ->
    {ok, State}.

handle_handoff_data(_Data, State) ->
    {reply, ok, State}.

encode_handoff_item(_ObjectName, _ObjectValue) ->
    <<>>.

is_empty(State) ->
    {true, State}.

delete(State) ->
    {ok, State}.

handle_coverage(_Req, _KeySpaces, _Sender, State) ->
    {stop, not_implemented, State}.

handle_exit(_Pid, _Reason, State) ->
    {noreply, State}.

terminate(_Reason, _State) ->
    ok.

nodeの追加/更新が起きた場合にnodeのリストを取る場合にはdping_ring_event_handler.erlにこう書きます。

handle_event({ring_update, _Ring}, State) ->
    Members = riak_core_ring:active_members(_Ring),
    io:format("ring update ~p~n",[Members]),
    {ok, State}.

こうしておいて、joinすると、attachしておいた端末にこう表示されます。

(dping1@127.0.0.1)1> 14:40:46.977 [info] 'dping3@127.0.0.1' joined cluster with status 'valid'
ring update ['dping1@127.0.0.1','dping3@127.0.0.1']
ring update ['dping1@127.0.0.1','dping3@127.0.0.1']
ring update ['dping1@127.0.0.1','dping3@127.0.0.1']

出てきた疑問点

  1. なんでring updateが複数回呼ばれるの?

    joined cluster with ... と書かれている部分はgosip部分で、それはAPIとしては呼べない、のかな?

  2. handoff_staringが何度も呼ばれる

    なのに、handof_finishedが呼ばれてない。

うーん。もう少し挙動を調べる必要がある様子。

まとめ

  • riak_coreという分散処理のフレームワークがある
  • rebarでテンプレートを作成できる
  • テンプレートで作成された関数を埋めていけばいい

まだまだ理解不足ですが、一旦ここまで。

続きます。次はコマンドの作成です。