Skip to content

Instantly share code, notes, and snippets.

@sile
Last active August 29, 2015 14:11
Show Gist options
  • Save sile/5433cfbd51bf8d7d5cdc to your computer and use it in GitHub Desktop.
Save sile/5433cfbd51bf8d7d5cdc to your computer and use it in GitHub Desktop.
Erlangで書かれた自作ライブラリ群の紹介 ref: http://qiita.com/sile/items/05e8e1c3bd84dfc01448
%%%
%%% 基本的な使い方
%%%
%% ハッシュリングの作成
> Nodes = lists:map(fun hash_ring_node:make/1, [a,b,c,d,e]).
[{hash_ring_node,a,a,1},
{hash_ring_node,b,b,1},
{hash_ring_node,c,c,1},
{hash_ring_node,d,d,1},
{hash_ring_node,e,e,1}]
> Ring0 = hash_ring:make(Nodes).
%% キーを担当するノードを検索する
> hash_ring:find_node(key_1, Ring0).
{ok,{hash_ring_node,c,c,1}}
%% ノードの削除 (and 再検索)
> hash_ring:find_node(key_1, hash_ring:remove_nodes([c], Ring0)).
{ok,{hash_ring_node,b,b,1}}
%%%
%%% 処理性能目安
%%%
%% 構築: ノード数=100
> {Time1, Ring1} = timer:tc(fun() -> hash_ring:make(lists:map(fun hash_ring_node:make/1, lists:seq(1, 100))) end).
> Time1 / 1000000. % 秒単位に変換
0.144733 % 0.144秒
%% 検索: 検索回数=10万回
> Keys = lists:seq(1, 100000).
> {Time2, _} = timer:tc(fun () -> lists:foreach(fun (Key) -> hash_ring:find_node(Key, Ring1) end, Keys) end).
> Time2 / 1000000.
0.399199 % 0.399秒 (25万/秒)
%%%
%%% バラつき度目安
%%%
> lists:sort(
dict:to_list(
lists:foldl(fun (K, Acc) -> dict:update_counter(K, 1, Acc) end,
dict:new(),
[begin {ok, N} = hash_ring:find_node(Key, Ring1), hash_ring_node:get_key(N) end || Key <- Keys]))).
[{1,1068}, % おおよそ均等?
{2,923},
{3,942},
{4,1006},
{5,1084},
{6,928},
{7,969},
{8,1031},
{9,954},
{10,995},
{11,962},
{12,1009},
{13,974},
{14,975},
{15,1032},
{16,997},
{17,1030},
{18,1012},
{19,994},
{20,953},
{21,1038},
{22,955},
{23,1033},
{24,959},
{25,950},
{26,1019},
{27,980},
{28,...},
{...}|...]
%% 構築
> Trie0 = hashtrie:new().
{hashtrie,0,64,0,
{[],[],[],[],[],[],[],[],[],[],[],[],[],[],[],[]}}
%% 要素追加
> Trie1 = hashtrie:store(key, value, Trie0).
{hashtrie,1,64,0,
{[],[],[],[],[],[],[],[],[],
[{key,value}],
[],[],[],[],[],[]}}.
%% 要素検索
> hashtrie:find(key, Trie1).
{ok,value}
> hashtrie:find(hogge, Trie1).
error
%% デコード
> jsone:decode(<<"[1,2,3]">>).
[1,2,3]
%% エンコード
> jsone:encode([1,2,3]).
<<"[1,2,3]">>
$ make start
%% 名前管理サーバプロセスを起動
> local:start_name_server(sample_ns).
ok
> local:which_name_servers().
[sample_ns].
%% 名前登録
> self().
<0.33.0>
> local:register_name({sample_ns, hoge}, self()). % 名前は {名前管理サーバ名, プロセス名} の形式で指定する
yes
> local:register_name({sample_ns, [a,b,c]}, spawn(timer, sleep, [infinity])). % アトム以外のプロセス名も可
yes
%% 検索
> local:whereis_name({sample_ns, hoge}).
<0.33.0>
> local:whereis_name({sample_ns, fuga}).
undefined % 未登録
%% 登録済みプロセス一覧を取得
> local:which_processes(sample_ns).
[{[a,b,c],<0.42.0},{hoge, <0.33.0>}].
> local:which_processes(sample_ns, ['_','_','_']). % パターン(ets:match_pattern/0)で絞り込むことも可
[{[a,b,c],<0.42.0>}]
%% 登録解除
> local:unregister_name({sample_ns, hoge}).
ok
> local:whereis_name({sample_ns, hoge}).
undefined
%% local:name_server_child_spec/1は、監視ツリーに名前管理プロセスを登録する際の推奨ChildSpecを返す
> local:name_server_child_spec(sample_ns).
{sample_ns,{local_name_server,start_link,[sample_ns]},
permanent,5000,worker,
[local_name_server]}
erl -pa ebin /path/to/logi_tty/ebin
> applicatoin:ensure_all_started(logi_tty).
{ok, [logi, logi_tty]}
> logi:info("hello"). % まだ何も表示されない
logi_default_logger
> logi_tty:install(info). % infoレベル以上のログは、端末に出力されるようにバックエンドを登録
ok
> logi:info("hello"). % 表示される
2014-12-14 03:24:24.310 [info] nonode@nohost <0.33.0> undefined:0 [] hello
logi_default_logger
> logi:debug("hello"). % infoレベル以下は表示されない
logi_default_logger
%% ヘッダ設定
> logi:set_headers([{key1, val1},{key2,val2}]).
> logi:info("hello").
2014-12-14 03:25:24.410 [info] nonode@nohost <0.33.0> undefined:0 [key1=val1,key2=val2] hello
logi_defaul_logger
%%% ログ出力先の変更
> logi:start_logger(hoge_logger). % 別のロガーを起動 (logi_default_loggerは最初に自動で起動している)
ok
> logi:which_loggers().
[hoge_logger,logi_default_logger]
> logi:info(hoge_logger, "hello", []). % 出力先ロガーを指定して実行: まだ何も表示されない
hoge_logger
> logi_tty:install(hoge_logger, debug). % debugレベルでバックエンドを登録
ok
> logi:debug(hoge_logger, "hello", []). % こっちはdebugレベルでも出力される
2014-12-14 03:29:16.868 [debug] nonode@nohost <0.54.0> undefined:0 [] hello
hoge_logger
$ ./rebar shell
%% 実プロセスの起動時刻を指定する
> After = fun (Seconds) -> {Mega, Sec, Micro} = now(), {Mega, Sec + Seconds, Micro} end.
> proxy:spawn(erlang, apply,
[fun (Start) -> io:format("~p hello: ~p sec\n", [self(), timer:now_diff(now(), Start) / 1000000]) end,
[now()]], % 呼び出し時刻を渡す
[{proxy_lifetime, [{start_time, After(5)}]}]). % 五秒後に起動するように指定
<0.119.0> % 呼び出し元に返されるのはプロキシプロセスのPID
<0.121.0> hello: 5.001993 sec % 実プロセスは五秒後に起動
%% 再起動回数を指定する
> proxy:spawn(io, format, ["hello\n"],
[{proxy_restart, [{max_restart, 3}]}]). % 三回までは再起動する
<0.122.0>
hello
hello
hello
%% プロキシプロセスは、受け取ったメッセージを実プロセスに転送する
> Pid = proxy:spawn(fun () -> receive Msg -> io:format("~p received: ~p\n", [self(), Msg]) end end, []).
> Pid ! hello.
<0.131.0> received: hello
hello
$ ./rebar shell
%%%
%%% erlang:open_port/2の場合の挙動
%%%
%% 適当に長い時間を指定して、sleepコマンドを起動
> Port0 = erlang:open_port({spawn_executable, "/bin/sleep"}, [{args, ["1000"]}]).
#Port<0.5076>
> erlang:port_info(Port0).
[{name,"/bin/sleep"},
{links,[<0.39.0>]},
{id,40608},
{connected,<0.39.0>},
{input,0},
{output,0},
{os_pid,11118}] % プロセスIDは'11118'
> io:format(os:cmd("ps | grep sleep")).
11118 ? 00:00:00 sleep % sleepは起動している
ok
%% ポート停止
> erlang:port_close(Port0).
true
> erlang:port_info(Port0).
undefined % ポートは閉じている
> io:format(os:cmd("ps | grep sleep")).
11118 ? 00:00:00 sleep % sleepは生存している
ok
%% 呼び出し元プロセス停止
> exit(kill).
** exception exit: kill
> io:format(os:cmd("ps | grep sleep")).
11118 ? 00:00:00 sleep % まだsleepは生存している
ok
%% 仕方がないのでkillコマンドで停止
> os:cmd("kill 11118").
> io:format(os:cmd("ps | grep sleep")).
ok
%%%
%%% safeexec:open_port/2の場合の挙動
%%%
> Port1 = safeexec:open_port({spawn_executable, "/bin/sleep"}, [{args, ["1000"]}]). % 指定する引数は同様
#Port<0.5769>
> erlang:port_info(Port1).
[{name,"/path/to/safeexec/priv/safeexec"}, % Erlangが直接起動するのはsafeexecコマンド
{links,[<0.76.0>]},
{id,46152},
{connected,<0.76.0>},
{input,0},
{output,0},
{os_pid,11282}] % プロセスIDは'11282'
> io:format(os:cmd("ps | grep -E 'safeexec|sleep'")).
11282 ? 00:00:00 safeexec
11283 ? 00:00:00 sleep % sleepは、safeexecの子プロセスとして起動
ok
%% ポート停止
> erlang:port_close(Port1).
true
> io:format(os:cmd("ps | grep -E 'safeexec|sleep'")). % 外部コマンドも終了
ok
%% 呼び出し元プロセスが終了した場合
> safeexec:open_port({spawn_executable, "/bin/sleep"}, [{args, ["1000"]}]).
> io:format(os:cmd("ps | grep -E 'safeexec|sleep'")).
11339 ? 00:00:00 safeexec
11340 ? 00:00:00 sleep
ok
> exit(kill).
** exception exit: kill
> io:format(os:cmd("ps | grep -E 'safeexec|sleep'")). % 外部コマンドも終了
ok
$ ./rebar shell
%%%
%%% 基本的な使い方 (インタフェース的にはdictに近い)
%%%
%% 構築
> Tree0 = splay_tree:new().
nil
%% 要素追加
> Tree1 = splay_tree:store(key1, value1, Tree0).
{key1,value1}
> Tree2 = splay_tree:store(key2, value2, Tree1).
{key2,value2,{key1,value1},nil}
%% 要素検索
> {_, Tree3} = splay_tree:find(key1, Tree2).
{{ok,value1},{key1,value1,nil,{key2,value2}}} % スプレー木は検索時にも木の部分的なリバランシングが行われる
> Tree2 =/= Tree3.
tree
> splay_tree:lookup(key1, Tree3). % lookup/2を使うことで検索結果だけを取得することも可能だが、基本的には非推奨
{ok, value1}
%%%
%%% 優先順位付きキューとして使用する例
%%%
> Queue0 = splay_tree:from_list([{N, N} || N <- lists:seq(1, 10)]).
{10,10,
{9,9,
{8,8,
{7,7,
{6,6,{5,5,{4,4,{3,3,{2,2,{1,1},nil},nil},nil},nil},nil},
nil},
nil},
nil},
nil}
%% take_smallest/1で先頭要素(= 優先順序が高い要素)を取り出せる
> {Front, Queue1} = splay_tree:take_smallest(Queue0), Front.
{ok,1,1}
%% split/2を使うことで任意の地点で分割可能
> {Queue2, Queue3} = splay_tree:split(4, Queue1).
> splay_tree:to_list(Queue2).
[{2,2},{3,3}]
> splay_tree:to_list(Queue3).
[{4,4},{5,5},{6,6},{7,7},{8,8},{9,9},{10,10}]
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment