読者です 読者をやめる 読者になる 読者になる

Block Rockin’ Codes

back with another one of those block rockin' codes

第一回 Erlang 基礎勉強会 に行ってきました -1-

Erlang 勉強会

Erlangもそうですが、並列・分散処理について興味があり、来年はそこを中心に掘り下げたいと思っています。関数型は大学でLISPを少しやりましたが、結局その後使わないので色々と忘れていますし、MapReduceアーキテクチャ等を学ぶ上でもErlangを少し勉強したいなと思っていたところに、id:@cooldeamon氏が勉強会を開催されると聞きつけ、参加してきました。おそらく今年最後のイベント・勉強会でしょう。
内容は思ったよりも濃く、Erlangに関してgihyo等の記事で読んだ程度の知識の人間にもわかりやすく教えていただきました。
Erlangの環境はCouchDBをインストールした時一緒に入っているので、特に準備は必要ありませんでした。今回は、内容がとても濃く、よくあるセミナのように変にこじんまり詰め込まず、持っているノウハウ・経験談を色々聞かせていただけたので大満足でした。せっかくなので、当日の資料とメモをもとに頑張って復習した内容を記録します。内容がとても多いので、大きく以下のように分けます。

  1. Erlangの概要とShell、JCL等の話
  2. 関数型言語としてのErlangの話

今回は前者です、後者はサンプルソースを見ながらの説明が多かったのですが、後日そのソースが公開されるそうなので、それを見ながら復習して、その後元気があったら書こうと思います。

2009/12/28
Erlang基礎勉強会
参考資料 Basic Study for Erlang #1

Erlangとは

  • いろいろなオープンソースErlangで開発されている。
  • ミドルウエアを作るのに向いている。
  • 並列、負荷分散に強い。
    • メニーコア(複数CPU)のスペックをうまく使える。
  • 障害耐性もある。
  • パターンマッチによりハンドリングできる。
    • ミドルを作るときに威力を発揮
    • 入ってきた文字列を見て、処理を振り分けられる。
    • バイナリにも対応しているので、ビット値を見て処理を振り分けたりできる。
    • RabbitMQはバイナリプロトコルなので、バイナリパターンマッチを使用している。

OTP

  • Erlangのライブラリ集のようなもの。
  • これがあるからこそ。
  • perlでいうcpanのようなもの。
    • Mnegia等のErlangで作られた分散DBMSなどもここに入っている。

「アーランを勉強する=OTPを勉強する」といってもいいぐらい重要らしい。(この辺りが詳しそう erlang @ Wiki - Introduction)

習得が簡単

Erlang は環境

  • シェルがあり、それを通してVMと対話する
  • erlコマンドは、実際は「ノードを起動し、同時にシェルを呼び出す」とういう動作
  • ErlangVM=Node
  • help().でヘルプ
  • erl -man 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(履歴の番号). は値だけを返すので、副作用のある関数等を実行しないでも値を取得できる。
    • ところで、erlangは副作用が無いというのは間違い。ErlangHaskellの用に純粋関数型では無いので副作用がある。

副作用 (プログラム) - Wikipedia

プロセス
  • 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 -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について
    • .erlang.cookieにはランダムな文字列が入っている。
    • .erlang.cookieに同じ文字列が入っていて、名前解決が出来れば、認証なしで接続できてしまう。
    • .これがErlangはセキュリティー的に弱いといわれる由縁。


ShellとJobの違いは良くわかってないです。とりあえずノードを各PCに起動しておけばリモートシェルで繋いだ状態は全くOSを意識しないですむので、簡易クラウドみたいなものを構成することが出来るとのこと。ただしセキュリティ的な問題からあまりパブリックに公開するのは危険だとのこと。成る程、分散に強いと言われる由縁が少しわかった気がする。もし間違い等あったらご指摘ください。
後半は関数型言語の話です、サンプルのソースが公開されたら書こうと思います。

あと、こういう記事はHatenaではどう書いたら見やすいのだろう。。