Hatena::ブログ(Diary)

cooldaemonの備忘録 RSSフィード

2012-07-28

ZeroMQ Erlang Binding(NIF) の inproc と Erlang の素のメッセージ送信の速度を比較してみた

コードと結果は下記の通り

https://gist.github.com/3193117

Erlang で作ったサーバに LL で作ったワーカーをぶら下げようと考えており、どうせならナウでヤングな ZeroMQ を間に入れてみようと思い立ちました。

ズボラな私は、ZeroMQ にワーカーのロードバランスをして欲しかったので、Erlang の中で Queue デバイスを使用し、そこに複数の Erlang プロセスから inproc でメッセージを送信しまくる予定でした。

しかし、ここまで素のメッセージ送信と速度に差があるなら、Erlang の中で自前でロードバランスした方が良さそうかなぁと…思い直してます。

ちなみに、inproc を使ってみて初めて気がついたのですが、inproc は他のトランスポートと下記の点で異なります。

  • bind と connect に使うコンテキストは同じ物でなければいけない
  • connect の前に必ず bind を行う必要がある

2011-09-26

続:gen_server のコールバックモジュール内で badarith が発生すると supervisor ごと落とされる

二年半前に gen_server のコールバックモジュール内で badarith が発生すると supervisor ごと落とされる というメモを残したにも関わらず、すっかり内容を忘れてしまい、変な Process Design の poolboy*1 の poolboy_sup が安全か検証をするのに時間が掛かったのでメモを残す。

もともと、Erlang ML で下記のようなやり取りがあった。

erlang-questions: Supervisor does not restart Gen Server

ざっくり要約すると Erlang Shell が停止してしまい、道連れで Supervisor が停止している。回避策は try catch …という内容。

これをすっかり忘れており「badarith 例外発生 = 全 Supervisor 停止」と勘違いして覚えていたのが、そもそもの勘違いの発端。

実際は、badarith が発生すると、"何故か" Erlang Shell が停止してしまい、Erlang Shell と link 関係にある Supervisor が "何故か" 停止している…が正しい。

下記に試した内容を列挙する。

badarith を発生させるプロセスの起動方法結果
Erlang Shell -> Supervisor -> Worker(gen_server)Supervisor が Worker を再起動した後、erlang:apply/2 が停止し、link 先の Supervisor が道連れに停止
Erlang Shell -> Application -> Supervisor -> Worker(gen_server)Worker 再起動
Erlang Shell (spawn) -> システムプロセス(trap_exit = true) -> worker(gen_server)システムプロセスが Worker 停止のメッセージ受信
Erlang Shell (spawn_link) -> システムプロセス(trap_exit = true) -> worker(gen_server)システムプロセスが Worker 停止と erlang:apply/2 停止のメッセージ受信

Supervisor には、start は無く start_link しかない。Erlang Shell 上から start_link を実行すると、起動した Supervisor は Erlang Shell と link 関係になってしまう。この辺りが、関係しているようにも思えるが…下記の疑問が残る。

  • worker(gen_server) と直接 link 関係には無い Erlang Shell が、何故、worker(gen_server) の badarith を補足しているのか?
  • Supervisor ではないシステムプロセスは、Erlang Shell の EXIT メッセージを補足するのに、Supervisor の EXIT を補足する handle_info/2 は、何故、評価されていないのか?(print debug で確認済み)

疑問は残るが、とりあえず Erlang Shell が停止しなければ、Worker で badarith が発生しても平気であるため、下記の回避策が考えられる(おすすめ順)。

  • Application 化する(一番、おすすめ)
  • ML 推奨の try catch(throw や exit を補足するならともかく、error を補足するのは、ちょっと…)
  • Erlang Shell 上で catch_exception(true). を評価しておく(これの副作用は、現在、調査中)

既に Erlang 歴 3 年目なのに、こんな所で躓いているから、いつまでも Erlang 界隈の底辺なんだろうなぁ…

*1:後日、poolboy の紹介を書く予定

2011-09-02

RabbitMQ のクラスタリング機能にキューのミラーリングが追加されたので RabbitFoot (AnyEvent::RabbitMQ) から試してみる

クラスタリングやキューのミラーリングの詳細は、下記参照の事。

RabbitMQ をクラスタリングする

今回はサーバを複数用意できなかったので、一つのサーバ上で RabbitMQ を二つ起動する。

% RABBITMQ_NODE_PORT=5672 RABBITMQ_NODENAME=rabbit1 rabbitmq-server
% RABBITMQ_NODE_PORT=5673 RABBITMQ_NODENAME=rabbit2 rabbitmq-server

起動したら、rabbit2 の方を一旦停止(ErlangVM は停止しない。RabbitMQ だけ停止)して、クラスタリングの設定を行ってから再起動する。

% rabbitmqctl -n rabbit2 stop_app
% rabbitmqctl -n rabbit2 reset
% rabbitmqctl -n rabbit2 cluster rabbit1@`hostname -s`
% rabbitmqctl -n rabbit2 start_app

クラスタリング構成が正しく組まれているか確認するには、下記のコマンドを実行する。

% rabbitmqctl -n rabbit1 cluster_status
Cluster status of node rabbit1@ljob04 ...
[{nodes,[{disc,[rabbit1@ljob04]},{ram,[rabbit2@ljob04]}]},
 {running_nodes,[rabbit2@ljob04,rabbit1@ljob04]}]
...done.

ミラーリングされたキューを作る

今回は、Perl Script からキューを作る。

use Coro;
use Net::RabbitFoot;

my $rf = Net::RabbitFoot->new(
    verbose => 1
)->load_xml_spec()->connect(
    host    => 'localhost',
    port    => 5672,
    user    => 'guest',
    pass    => 'guest',
    vhost   => '/',
    timeout => 1,
);

my $ch = $rf->open_channel();

$ch->declare_queue(
    queue     => 'test_q',
    arguments => {
        'x-ha-policy' => 'all',
    },
);

$rf->close();

arguments テーブルを使用しているので AMQP 0-8 から使える。

x-ha-policy に 'all' を指定しているので、クラスタリングされている全てのノード上にキューがミラーリングされる。

x-ha-policy の詳細は、RabbitMQ - Highly Available Queues 参照の事。

キューのミラーリング状態を確認するには、下記のコマンドを実行する。

% rabbitmqctl -n rabbit1 list_queues name messages pid slave_pids synchronised_slave_pids
Listing queues ...
test_q  0       <rabbit1@ljob04.2.5022.0>       [<rabbit2@ljob04.1.4932.0>]     [<rabbit2@ljob04.1.4932.0>]
...done.

上記の場合、マスターとなるキューは rabbit1 上に存在し、コピーが rabbit2 に存在している。

念のため、幾つかメッセージをキューに追加しておく。

use Data::Dumper;
for (1..2) {
    $ch->publish(
        routing_key => 'test_q',
        body        => 'Hello HA Queue.',
        on_return   => unblock_sub {die Dumper(shift)},
    );
}

ノードを停止・起動してみる

キューのマスターが存在している rabbit1 を停止する。

% rabbitmqctl -n rabbit1 stop

状態を確認してみると…

% rabbitmqctl -n rabbit2 list_queues name messages pid slave_pids synchronised_slave_pids
Listing queues ...
test_q  2       <rabbit2@ljob04.1.4932.0>       []      []
...done.

キューのマスターが rabbit2 に移っており、コピーは空になっている。

この状態でキューにメッセージを追加しておく(この時点でメッセージ数 4)。追加の方法は、前述を参照の事。

次に、rabbit1 を起動する。

% RABBITMQ_NODE_PORT=5672 RABBITMQ_NODENAME=rabbit1 rabbitmq-server

クラスタリングの設定は保存されているので、これだけで良い。

状態を確認してみると…

% rabbitmqctl -n rabbit2 list_queues name messages pid slave_pids synchronised_slave_pids
Listing queues ...
test_q  4       <rabbit2@ljob04.1.4932.0>       [<rabbit1@ljob04.3.225.0>]      []
...done.

コピーがノード上に作られているが、同期は行われていない。

詳細は、RabbitMQ - Highly Available Queues の Unsynchronised Slaves を参照の事。

この状態で、更にキューにメッセージを追加しておく(この時点でメッセージ数 6)。

ここでメッセージを 4 つ受信すると同期状態となる(1 つずつメッセージを受信した方が解りやすい)。

for (1..4) {
    my $response = $ch->get(queue => 'test_q');
}

感想とか

クラスタリングを構築してもキューがミラーリングされるわけではなかったので、今までは、クラスタリング機能を使わずに運用していた。(2 ノード以上で運用し、1 ノード停止してもシステム全体が止まらないような設計を行っていた)

今後は、積極的にクラスタリング機能を使って行こうと思う。

2011-01-12

他のプロセスから送られてきたメッセージを誤受信しないよう制限する

刺身☆ブーメランのはてなダイアリー 2011/01/12(Wed) への返信です。

元コードの wait_server:client/3 は、receive で From を束縛しているので、この状態だとメッセージの選択受信になりません。From を Server_Pid に変更すると、ただしく動作すると思います*1

蛇足ですが、client を spawn する件と make_ref/0 を使う件に対応した添削コードを下記に掲載しておきます*2

-module(wait_server).

-export([spawn_server/0, spawn_client/3]).
-export([server/0, client/3]).
-export([test/0]).

spawn_server() ->
  spawn(wait_server, server, []).

spawn_client(Server, Time, Req) ->
  spawn(wait_server, client, [Server, Time, Req]).

server() ->
  receive
    {Client, Ref, Time, Req} ->
      timer:sleep(Time),
      Client ! {self(), Ref, Req};
    stop ->
      exit(normal);
    Unknown ->
      io:format("Server: received unknown message: ~p~n", [Unknown])
    after 60000 ->
      io:format("Server: timeout~n"),
      exit(normal)
  end,
  server().

client(Server, Time, Req) ->
  Ref = make_ref(),
  Server ! {self(), Ref, Time, Req},
  receive
    {Server, Ref, Res} ->
      io:format("Client: received ~p from ~p~n", [Res, Server]),
      {ok, Res}
    after Time + 100 ->
      io:format("Client: timeout~n"),
      {ng, timeout}
  end.

test() ->
  Data = {foo, bar, baz},
  Server = spawn_server(),
  {ok, Data} = client(Server, 100, Data),
  Client = spawn_client(Server, 100, Data),
  lists:foreach(
    fun (_) -> Client ! ignoring end,
    lists:seq(1, 10)
  ),
  timer:sleep(100),
  Server ! stop,
  ok.
1> wait_server:test().
Client: received {foo,bar,baz} from <0.33.0>
Client: received {foo,bar,baz} from <0.33.0>
ok
2> q().

*1:既に Server_Pid には値が束縛済みであるため、メッセージの選択受信となります

*2:多少、手抜きしてますが…

2010-09-02

第五回 Erlang 分散処理勉強会 無事終了

第五回 Erlang 分散処理勉強会ですが、皆さんにご助力頂き、無事終了いたしました。

発表者の皆様、運営にご協力頂いた皆様、会場をご提供頂いた日本オラクルの皆様、本当にありがとうございました。

懇談会について

今回は、開始時間が遅かったので懇談会を行いませんでした。

@ymotongpoo さんのご好意で少しだけ会場利用時間を延長し、雑談時間を設けさせて頂きましたが、まだまだ、皆様、話足りない様子でしたので、やはり懇談会は必須であると痛感しました。

今後は、懇談会を行えるように調整しようと考えております。

発表者について

申し訳ない事に、連投している発表者の方々がいらっしゃるので、そろそろ別の方々に発表して頂こうと考えております。

事前懇談会

今回は、今後の運営をどうしましょうか…という相談を行う場として考えておりましたので少人数で募集を行いました。

特に場所の確保を行わず、適当に集合場所だけ決めていたのですが、目星を付けていた場所が貸し切り状態になっており、急遽、場所の確保に走るなどハプニングもありましたが、何とか時間通りに懇談会を開催する事ができました。(iPhone と Twitter が無ければ、こんな行き当たりばったりは上手くいかなった…)

@kenji_rikitake さんにプレゼントを頂いたり、@kuenishi さんに「最近 @cooldaemon は Erlang 書いてなくね?」という厳しい追求を受けたりと楽しい一時を過ごせました。

肝心の今後の運営については、何となく切り出せずに二時間過ごしてしまったので、また改めて Erlang Users JP の ML などで相談しようと思います。

Hibari (@shinyamoto & Joe Norton)

http://www.slideshare.net/geminimobile/hibari-for-tew5-japanese

http://sourceforge.net/projects/hibari/

http://hibari-gemini.blogspot.com/2010/08/hibari-presentation-at-tokyo-erlang.html

第二回 Erlang 基礎勉強会で会場をご提供頂いたジェミナイ・モバイル・テクノロジーズ株式会社様の発表です。

前半は @shinyamoto さんによる Hibari の概略説明、後半は Joe Norton さんによる Hibari の仕組や HBase, Cassandra とのパフォーマンス比較、API の使い方などの説明が行われました。会場からの質問が多数あったのですが、時間の都合上、残念ながら途中で区切らせて頂きました。

今後、UBF や Hibari のハンズオンワークショップ開催のご予定があるらしいので、BigData を扱える高性能な Key-Value Store に興味のある方は、是非、ご参加を。

個人的には、実力が伴えば Streaming I/F や FUSE 対応をお手伝いしたいところです。

Erlangにおける疑似乱数SFMTの実装 – Implementing SFMT PRNG on Erlang (@kenji_rikitake)

http://www.ne.jp/asahi/bdx/info/depot/rikitake-tew5-sfmt-20100826.pdf

http://github.com/jj1bdx/sfmt-erlang

Erlang Factory から帰国された @kenji_rikitake さんの発表。

始めに stdlib に含まれる random モジュールの駄目っぷりを暴露、その後に SFMT ライブラリの改修、Pure Erlang での実装、NIF に置き換えと濃い発表が続きました。

個人的には、random モジュールは捨てて SFMT に乗り換えるのは確定です。ちなみに、RebarDoxygen は存在すら知りませんでした。Linked-In Driver を覚えたので、そろそろ、いい加減に NIF も覚えたい所です。

そうそう…、Erlang Factory T-shirt を羨ましがっていたところ、Erlang User Conference で発表するともらえますよとの事…ネタはあれど言語の壁が厚い orz

msgpack/msgpack-rpc in Erlang (@kuenishi)

http://www.slideshare.net/kuenishi/msgpack-erlangpublictew5

http://github.com/msgpack/msgpack-rpc

msgpack-rpc (Erlang 部分?)開発者の @kuenishi さんの発表。

お子様紹介の後、msgpack の概要説明、msgpack-rpc の紹介と Erlang Process は Object ではないかという内容の発表でした。

tcp_server を作った身としては msgpack-rpc の話はかなり頷けました。既に msgpack-rpc には、Java 実装があるっぽいので、真似すると Scala 実装が簡単作れそうな気配なので、個人的 ToDo リストにこっそり追加予定です。

実は、第二回 Erlang 基礎勉強会で Actor と Object が似ている件を話したばかりだったので、Erlang Process は Object ではないかという話にも大きく頷いていました。余談ですが、第二回 Erlang 基礎勉強会では、Hibari 開発者の Joe Norton さんが Erlang Process は状態を持っていて副作用があるのでテストが大変だという話をされています。

mnesia_frag (@voluntas)

既に Erlang に関して語り尽くした感がある @voluntas さんの発表。

やたらと資料の少ない mnesia_frag や開発環境の発表でした。

mnesia_frag は、以前調べて挫折した事があるので、大変参考になりました。Mneisa 関連の話は、@voluntas さんの BLOG にもサンプルが豊富にあるので、そちらも参照すると良いとの事です。

ところで…もし資料公開されているようでしたら URL を教えて頂けますでしょうか。

Sphinx (@shibukawa)

http://www.slideshare.net/shibukawa/erlang-and-i-and-sphinx

http://sphinx.pocoo.org/

私にとっては、Erlang Efficiency Guide の翻訳の方が馴染み深いのですが、世間的には、つまみぐい勉強法エキスパートPythonプログラミングで超有名な @shibukawa さんの発表。

Erlang から脱線し Sphinx に走り、また Erlang に戻ってくるまでの軌跡と、Sphinx の考え方や使い方についての話でした。

@shibukawa さんのお力により erlang-domain は、Sphinx を入れると即使えるようになる模様。

http://bitbucket.org/birkenfeld/sphinx-contrib/changeset/066b4cd44a7b

ありがたく使わせて頂きます。

A netpic of code:priv_dir/1 (@itawasa)

Kai 開発者の内の一人、かつ、@voluntas さんの同僚である @itawasa さんの発表。

code:priv_dir/1 という関数の話でした。

erluna の .so ファイルの配置場所を code:priv_dir/1 で取得していたので、とても頷ける話でした。

msgpack-rpc もそうですが、実際に Erlang で開発を行っているとぶつかる壁の一つです。

こちらも、もし資料公開されているようでしたら URL を教えて頂けますでしょうか。

Small Erlounge

勉強会が終わった後、懇談会が無いのは寂しいので、一杯だけ飲んで来ました。

休日を一日使い、マルチトラック形式で Erlang Workshop を開けないか?という話を @kenji_rikitake さんと @yosukehara さんに相談するなどしました。

後々、ご協力頂けそうな方々にご相談させて頂きますので、その際は、よろしくお願い申し上げます。