第一回 Erlang 基礎勉強会 に行ってきました -1-
Erlangもそうですが、並列・分散処理について興味があり、来年はそこを中心に掘り下げたいと思っています。関数型は大学でLISPを少しやりましたが、結局その後使わないので色々と忘れていますし、MapReduceのアーキテクチャ等を学ぶ上でもErlangを少し勉強したいなと思っていたところに、id:@cooldeamon氏が勉強会を開催されると聞きつけ、参加してきました。おそらく今年最後のイベント・勉強会でしょう。
内容は思ったよりも濃く、Erlangに関してgihyo等の記事で読んだ程度の知識の人間にもわかりやすく教えていただきました。
Erlangの環境はCouchDBをインストールした時一緒に入っているので、特に準備は必要ありませんでした。今回は、内容がとても濃く、よくあるセミナのように変にこじんまり詰め込まず、持っているノウハウ・経験談を色々聞かせていただけたので大満足でした。せっかくなので、当日の資料とメモをもとに頑張って復習した内容を記録します。内容がとても多いので、大きく以下のように分けます。
今回は前者です、後者はサンプルソースを見ながらの説明が多かったのですが、後日そのソースが公開されるそうなので、それを見ながら復習して、その後元気があったら書こうと思います。
2009/12/28
Erlang基礎勉強会
参考資料 Basic Study for Erlang #1
Erlangとは
OTP
「アーランを勉強する=OTPを勉強する」といってもいいぐらい重要らしい。(この辺りが詳しそう erlang @ Wiki - Introduction)
習得が簡単
- 逐次処理が中心。
- これだけでも入りやすい。
- アクターモデルが中心。
- アクターモデルとは、プロセス同士がメセージをやり取りしていく設計。
- Javaのスレッド等とは違い、メモリを共有するのではなく、メッセージの交換のみ。
- 協調的並列処理はアクターモデルだと設計しやすい。(ここを参照 アクターモデル - Wikipedia)
- 余談としてhigeponさんのSkip-Graphの話をされていたがメモりきれず。(多分このへん 「skip graph」の検索結果 - Higepon’s blog)
Erlang は環境
ErlangShell
値の扱い
- b(). 束縛された値一覧
- Erlangは「値を束縛する」=「一度定義したら変えられない」ので変数ではない。
- f(). 束縛された値を全て解放
- これはShellテスト的にで使うのが基本。プログラム内では使えない。束縛が基本なので、解放が必要なプログラムは、そもそも見直すべき。
- f(解放したい値). も可能。
2> X=1. 1 3> b(). X = 1 ok 4> f(). ok 5> b(). ok
Shellとしての機能
Shellなのでコマンドの履歴が残っている。
- h(). コマンド履歴を番号付きで出力
- e(履歴の番号). 過去のコマンドを再実行
- X=1.を実行した結果をe(N).で再実行すると、もう一度Xに1を代入するのではなく、パターンマッチをしている。
- v(履歴の番号). は値だけを返すので、副作用のある関数等を実行しないでも値を取得できる。
プロセス
- i(). プロセスの一覧が表示できる。
- 自動、手動両方のプロセスが確認できる。
- i(X,Y,Z).
- i().で見れるプロセスIDで、消費してるメモリ等の情報が、タプルのリストで見られる。
- pid(X,Y,Z) プロセスにメッセージを送りたいとき等に使う。
- <>の括弧は文法上は使えないのでpidは直接書けない、pid()という関数を使用する。
- i()はプログラムでは使えない、プログラム上ではpid()を使う。
16> i(). Pid Initial Call Heap Reds Msgs Registered Current Function Stack <0.0.0> otp_ring0:start/2 610 2406 0 init init:loop/1 2 <0.3.0> erlang:apply/2 2584 170026 0 erl_prim_loader erl_prim_loader:loop/3 6 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Total 24167 378742 14 17> i(0,0,0). [{registered_name,init}, {current_function,{init,loop,1}}, 〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜 {suspending,[]}] 18>
モジュール
- c(ファイルパス). 書いたErlangShellを、コンパイルし、ロードしてくれる。
- 相対パスの場合は、ワーキングディレクトリから。
- ワーキングディレクトリは、erlコマンドを起動したディレクトリ。
- pwd(). で確認できる。
- l(モジュール名). ロードだけする。リロードにも使う。
- m(). 現在ロードされているモジュール。ここに無ければロードする。
- m(モジュール名). モジュールの持ってる関数等を確認できる。
- code:get_path(). c,lでロードされるモジュールが設置されたディレクトリ。
- この順番でロードされる。
- add_patha() 順番の先頭にモジュールを加える。
- add_pahtz() 順番の末尾にモジュールを加える。
- この3つでモジュールを操作。
ディレクトリ
- ls(). unixのlsと同じ
- cd(). unixのcdと同じ
- memory(). erlangVMがどのくらいのメモリを消費しているか。
- q(). shellとVMを「両方」停止する。
- Shellはあくまでもインターフェース、shellが無くてもVMがあるという状態もある。
shellを色々カスタマイズすることも出来る。
JCL(Job Control Language)
Ctl-GでJCLモードに入る。(windowsでは、DOM窓がctl-Gを使っている(ビープ音が鳴る)ので、別にある専用ソフトを使用する。)
- 一つのノード上で複数のシェルを操作できる。
- jobとshellの違いについてはこちらを読むべきらしい Erlang -- shell
- 基本的にjob=プロセスの集まり。
- h ヘルプ、このコマンドで調べる。(JCLのコマンドなど覚えてない by 飛行機本)
- j ジョブの一覧、つまりシェルの名前がかえってくる。
- *が付いているのがカレント。
- 頭の数字で識別。
- s ローカルにシェルを起動する。
- c ジョブに接続。
- c 数字 で切り替え(引数省略でカレント)。
- (切り替えると戻ってこなかった。もう一度returnすると、戻ってくるけどJCLを抜けてしまう。。)
- k 数字 ジョブを終了=Shellを終了
- 全てのジョブをkすると、VMはあるけど対話するためのShellが無い状態になる。プログラムを実行しているときはこの状態が多い。
- i 数字 ジョブの中断(引数省略でカレント)
- 終了と中断は違う
- iでshellは終了しない。。
- 無限ループしたりした時、帰ってこなくなったら、JCLに入ってiで止める等。
- q ShellもVMも消す。
- r リモートShellを起動
リモートシェル実験
リモートシェルで別のノードにアクセスする実験。
二つのシェルを立ち上げて実験。
- ノード名は -snameで付ける
- hostname -s の結果を、ノード名の末尾に@付きで付加
一つ目のシェルを起動
%1つ目のシェルでノードをaにします。 %-snameで名前を付けてノードとシェルを起動 $erl -sname a (a@hostname)1>
もう一つシェルを起動
%2つ目のノードをbにします $erl -sname b (b@hostname)1> %ここでaに接続します。 (b@hostname)>net_adm:ping('a@hostname'). pong
pong:成功
pang:失敗
です。
失敗した場合は名前解決が出来ていない場合があるので、/etc/hosts 等に加えてあげるといいでしょう。
簡単に実験する上で、こんな感じにしました。
/etc/hosts
127.0.0.1 localhost hostname
ここでnodes().を見ると、
(b@hostname)3> nodes(). [a@hostname] %自分が知っているノードのリストが出る。aを知っていることになります。 %ここで、bでJCLに入ります(Ctl-G)。 (b@hostname)4> User switch command --> j 1* {shell,start,[init]} %ここでリモートシェルを起動し、aのノードに接続する。 --> r a@hostname %カレントがaになりました。 --> j 1 {shell,start,[init]} 2* {a@hostname,shell,start,[]}
これで、b上に起動したshellからaのノードに接続している状態になります。
なお、-sname で起動したノードと -name で起動したノードは通信できない仕様です。-nameと -snameの違いはよくわからない。-snameの方が使用頻度は多いらしい。
erlコマンドのオプション
- erl -man モジュール名 モジュールの詳細。
- erl -man erlang
- erl -noshell ノードだけを起動する。
- ノードは起動されているが、専用のシェルは起動されていないので、erlで両方起動してシェルを終了したのと同じ状態。
- Shellが無いので、JCLで入って操作する。
- erl -noshell -s apply(M,F[]). -sの後ろにモジュール名 関数名の順で書くと、実行してくれる。
- erl -noshell -s init stop 実行してすぐ終了( init stop = q(). )
- elr -noshell -s "ワンライナーで実行したいソースを書く" -s init stop で実行してすぐに終了できる。Shellを使わない実行が可能。
- erl -evel 式 式を評価
- erl -detached ノード起動後、すぐにデタッチする。
- noshellであがった後、すぐにデタッチする。
- スクリーンのデタッチと同じ
- -snameで名前付けておかないと、見つけられなくなり、killしないといけなくなる。
- erl -remsh
- JCL起動からrする動作を一発で出来る。
- erl -remsh a@hostname -sname b でbというノードを起動しつつaに接続するshellを起動してくれる。
-
- .erlang.cookieや/etc/hostosやDNSの設定がきちんとできていないと、-snameは使えない。
- remshは以下のgihyoのkaiの特集を参照 http://gihyo.jp/dev/feature/01/kai
ShellとJobの違いは良くわかってないです。とりあえずノードを各PCに起動しておけばリモートシェルで繋いだ状態は全くOSを意識しないですむので、簡易クラウドみたいなものを構成することが出来るとのこと。ただしセキュリティ的な問題からあまりパブリックに公開するのは危険だとのこと。成る程、分散に強いと言われる由縁が少しわかった気がする。もし間違い等あったらご指摘ください。
後半は関数型言語の話です、サンプルのソースが公開されたら書こうと思います。
あと、こういう記事はHatenaではどう書いたら見やすいのだろう。。