Erlang に興味を持った人へ
随時加筆してます
追記
2011-06-18
2011-04-02
- rebar.config の erl_opts から debug_info を消した
- rebar.config の実際に使っているベースを公開
- Makefile に make edoc を追加した
- configure の例を hipe を使わないようにしているので native-lib を外した
- EUnit について補足を書いた
お前、誰よ
@voluntas といいます。とある零細ベンダーのコンサル/プログラマで、Erlang/OTP を使ってご飯を食べています。一人で書いているわけではなく 4 名のチームで動いています。
Erlang 歴はまだまだ日が浅く 4 年程度で、まだまだ学ぶことばかりですが、Erlang に対して自分が知っていること、感じていることを書き出してみました。
これを読んだ方で是非ともこんなのあるよ、こうしたほうがいいじゃない、これおすすめなどなど教えて頂けると嬉しいです。
ちょっとやってみたい方へ
- 飛行機本を買ってみてください、知りたいことはほとんど書いてあります
- Amazon.co.jp: プログラミングErlang: Joe Armstrong,榊原一矢: 本
学ぶ前に
勘違い
よくスケールしないのであれば Erlang を使えばいいよと言っている人がいますが、その勧めている人は Erlang を使ったことが無いうえに、スケールという概念をかなり勘違いしていると思います。なのでスケーラビリティが欲しくて Erlang を始めるのは間違いです。スケールについては下の方で追記しておきました。
本
まずは飛行機本を買いましょう、飛行機本を買えないのであれば Erlang を学ぶのはあきらめた方が良いくらい良著です
- Amazon.co.jp: プログラミングErlang: Joe Armstrong,榊原一矢: 本
- PDF でも販売されていますので、是非本と一緒に買いましょう
洋書でも良ければ以下の 2 冊をお勧めします
ネットにも文章は沢山ありますがここでは絞って紹介させて貰います
- http://shibu.jp/erlang/
- Erlang の公式ドキュメントの翻訳です
- http://ymotongpoo.appspot.com/lyse_ja/index.html
- Learn you some Erlang for great good! の翻訳です、お金がない人はこれを読むだけで十分かもしれません
ライブラリ
お勧めのライブラリです。Rebar 化されていないものは統一性が無くなるのでどんなに良いツールでも使わない方がいいと思います。
- rebar
- basho_bench
- A load-generation and testing tool for key-value stores
- KVS 用のベンチマークですがいろいろな用途に使えます
- proper
- PropEr: a QuickCheck-inspired property-based testing tool for Erlang
- QuickCheck というプロパティベース(型からランダムにテストを生成する)のテストツールです
- eper
- Erlang performance and debugging tools
- meck
- A mocking library for Erlang
- モックライブラリです EUnit と組み合わせると強力です
- getopt
- mochiweb
- webmachine
- folsom
- Expose Events and Metrics via HTTP and JSON
- 統計情報を簡単に扱えるようにするフレームワーク、JSON で簡単に統計情報が取得できるようになります
- ibrowse
- Erlang HTTP client
- HTTP クライアントはデフォルトでも入っていますが、自分の知る限りこれが一番使いやすいです
- Emysql
- Erlang MySQL driver
- Electronic Arts が公開している MySQL 用ドライバ。実戦投入されておりコネクションプールも実装済み
- statebox
スケールとか
Erlang は確かにスケールします。これは間違いありません。CPU 800 % とか使います。しかし CPU を使う事が大事なのではなく CPU をうまく使う事が大事です。さらに Erlang の世界はかなり広いのでちょっと学んだだけでスケールするシステムは書けません。
もちろんこの部分だけ Erlang で書く ... というのはありです。ただし連携するのが面倒かも知れませんのでそのあたりも考えてからの導入をお勧めします。
ただ勘違いして欲しくないのはスケールが必要だから Erlang を選ぶというのは間違いです。スケールが必要なのであれば C/C++/Java 等でマルチスレッドプログラミングを学んだ方が良いです。またボトルネックになるのが CPU である場合はそんなに無いはずです。ほとんどは I/O 周りではないでしょうか。それがわかって Erlang をやるのであれば良いですが、何もわからずただ「 Erlang スケールする」でやるのは間違いです。
なぜ Erlang なのか
とにかく特定要素に特化していることもあり、コードが短くて済みます。また文法が単純な事もあり可読性が高いです。さらに軽量プロセスを使って気軽に並列処理を書くことが出来ます。
また、軽量プロセスの監視機能が優秀です。この監視機能だけでも使うメリットがあります。
とにかくネットワークサーバを書くことに特化していてかゆいところに手が届く言語です。またエリクソン主導で完全なオープンソースとして提供され、安定したリリースをされていることも魅力の一つです。
結局の所、自分が必要としている道具としてピッタリあった、ただそれだけなのかも知れません。
OTP の壁
Erlang を学ぶ際に問題となってくるのは OTP だと思います。OTP を用いたシステム設計が出来るかどうかが一番の難関です。これは「ただ Erlang」が書けるだけでなく、システムを1から設計したことが無いと難しいです。
基本的には L4 ~ L7 まで面倒を見る必要が出てきますので、知識を得てから再挑戦するか知識を得ながら挑戦するのどちらか、自分は後者です。
OTP を学ぶにはアクターモデルをしっかり理解する必要もあります。一つ一つの軽量プロセスに役割を割り当てそれらとメッセージパッシングで通信しながら処理を行います。
それらは基本的に学べる物ではなく色々作ってみるしかありません。一番いいのは製品として使われているソースコードを読むことです。
また洋書と @shibukawa と @ymotongpoo が翻訳してくれている文章を読むのもいいかもしれません。
ですが、何より「作ってみる」事でしか学べませんので本や人から学ぶのを早めにあきらめるのがお勧めです。
ソースコードリーディング
もしソースから勉強したいのであれば mochiweb が今のところ一番お勧めです。HTTP の知識以外は求められませんし、JSON ライブラリや色々便利なライブラリが入っていますので Erlang の知識があれば読み進めることが出来るでしょう。
Erlang のインストール
Erlang のインストールは R14B03 が今の最新版です。基本的には最新版を使うようにするのが良いです。またソースから入れる習慣も付けると良いと思います。
ソースは公式は遅いので手前味噌ですが Dropbox にミラーしてありますのでどうぞ。
Mac での configure の例
./configure --prefix=/opt/erlang/R14B03 --disable-dynamic-ssl-lib --enable-threads --enable-smp-support --enable-kernel-poll --disable-sctp --disable-hipe --enable-darwin-64bit --without-javac --with-termcap
追記(2011-03-30): @akitada からアドバイスをいただきました。 --disable-hipe してるのに --enable-native-libs しても意味ないとのこと。言われてみればその通りなので、--enable-native-libs は外しておきます。
何か無い限りは 64bit でインストールするのをお勧めします。また Jinterface という Java との連携を使わなければ --without-javac するのが良いでしょう。また hipe はあまり効果が無いこともあって有効にするメリットはありません。
Erlang のフォルダ構造
まずトップレベルのフォルダ名も重要です application 名としてなります。ここではサンプルとして snowflake を使いたいと思います。
最低限のフォルダ構成
snowflake/ README Makefile rebar.config rebar
まずは何はともあれ README ですね、どんなアプリケーションなのかどうかを明記しましょう。rebar については rebar の導入を参考にしてください
よくあるフォルダ構成
snowflake/ README Makefile rebar.config rebar test/ ebin/ include/ src/
- test にはモジュールのテストファイルが入ります、テストについては eunit の導入を参考にしてください
- ebin にはコンパイル済みモジュールが入ります
- src にはソースコードが入ります
- include には .hrl という拡張子のヘッダファイルが入ります
rebar の導入
rebar とは riak という商用分散 KVS を開発している Basho が開発した Erlang ビルドツールです。これが出てきたおかげで Makefile を苦労して書いたりする必要が無くなりました。
導入方法は二つあります。 github から rebar のソースコードを持ってくるか、または github から公開されている rebar ファイルをダウンロードしてくるかのどちらかです。
ここではソースコードをダウンロードしてビルドする方法を紹介しておきます。
$ git clone git://github.com/basho/rebar.git $ cd rebar $ make
これでフォルダの中に rebar というファイルが出来たと思います。こちらを先ほど作った snowflak フォルダに追加してください。
また、rebar.config は色々あると思いますが、お勧めの rebar.config を公開しますのでこちらを使ってみてください。かなり最低限ですが十分だと思います。色々使ってみて覚えてみてください。
追記(2011-04-02): erl_opts にdebug_info はデフォルトになり、明示的に書く必要が無くなりました
rebar.config
{erl_opts, [fail_on_warning, warn_export_all]}. {xref_checks, [undefined_function_calls]}. {cover_enabled, true}. {clean_files, ["ebin/*", ".eunit/*"]}.
追記(2011-04-02): 自分が実際に使っている rebar.config のベース、そのうち github に公開します
{require_otp_vsn, "R14"}. {erl_opts, [warnings_as_errors, warn_export_all, warn_untyped_record]}. {xref_checks, [fail_on_warning, undefined_function_calls]}. {clean_files, [".qc/*", ".eunit/*", "ebin/*.beam"]}. %% Jenkins 向け %% {eunit_opts, [{report,{eunit_surefire,[{dir,"."}]}}]}. {cover_enabled, true}. {edoc_opts, [{dialyzer_specs, all}, {report_missing_type, true}, {report_type_mismatch, true}, {pretty_print, erl_pp}, {preprocess, true}]}. {validate_app_modules, true}. {deps, [{meck, ".*", {git, "git://github.com/eproxus/meck.git", {branch, "master"}}}, {proper, ".*", {git, "git://github.com/manopapad/proper.git", {branch, "master"}}} ]}.
rebar.config の設定の紹介
- erl_opts
- warnings_as_errors
- Warning をコンパイルエラーとして扱います
- warn_export_all
- export_all を設定しているも十がある場合はコンパイルエラーとして扱います
- warnings_as_errors
- xref_chekcs
- fail_on_warning
- Warning を Error 扱いにします
- undefined_function_calls
- 他のモジュールも含め存在しない関数呼び出しがあった場合コンパイルエラーとして扱います
- fail_on_warning
- cover_enabled
- ユニットテスト時にカヴァレッジファイルを生成してくれます
- clean_files
- ./rebar clean (つまり make clean) した際に指定したファイルを削除してくれます
あとは毎回 rebar ファイルを実行するのが面倒なので Makefile を作成します
追記(2011-04-02): edoc が日本語使えるようになっていたので、記載しておきます
all: clean compile xref eunit compile: @./rebar compile xref: @./rebar xref clean: @./rebar clean eunit: @./rebar eunit edoc: @./rebar doc
rebar のテンプレート機能を使う
フォルダ構成は決まりましたがこのままではソースコードがありませんね。色々書いていくのも面倒なのでここでは rebar の入っているテンプレート機能を使いましょう。snowflake フォルダ直下で以下の二つのコマンドを実行してください
$ ./rebar create-app appid=snowflake ==> snowflake (create-app) Writing src/snowflake.app.src Writing src/snowflake_app.erl Writing src/snowflake_sup.erl $ ./rebar create template=simplemod modid=snowflake ==> snowflake (create) Writing src/snowflake.erl Writing test/snowflake_tests.erl
そして make と打ってください。
$ make ==> snowflake (clean) ==> snowflake (compile) Compiled src/snowflake_sup.erl Compiled src/snowflake.erl Compiled src/snowflake_app.erl ==> snowflake (xref) ==> snowflake (eunit) Compiled src/snowflake_app.erl Compiled test/snowflake_tests.erl Compiled src/snowflake_sup.erl src/snowflake.erl:6: Warning: export_all flag enabled - all functions will be exported make: *** [eunit] Error 1
上記のようなエラーが出るはずですこのエラーは export_all が自動生成されたモジュールに書かれてしまっているからです。消すと正常にコンパイル出来ると思います。
src/snowflake.erl から -compile(export_all). を削除してください。
そして再度 make と打ってください
$ make ==> snowflake (clean) ==> snowflake (compile) Compiled src/snowflake_sup.erl Compiled src/snowflake.erl Compiled src/snowflake_app.erl ==> snowflake (xref) ==> snowflake (eunit) Compiled test/snowflake_tests.erl Compiled src/snowflake_app.erl Compiled src/snowflake_sup.erl Compiled src/snowflake.erl There were no tests to run. Cover analysis: /private/tmp/snowflake/.eunit/index.html
これで正常にコンパイルが出来ました。あとは作りたいシステムを作っていくだけです。
EUnit の導入
Erlang に標準で付いてくるテストは Common Test と EUnit があります。Common Test は Erlang 自体をテストするツールとしても使われています。ここでは EUnit を使います。理由としてはシンプルでわかりやすい。さらに日本語のドキュメントが存在するという理由です。
また EUnit は他の言語に存在する xUnit とほとんど変わりませんので勉強コストが低いというのもあります。
rebar は EUnit のテストも簡単にしてくれます。さらに Cover モジュールを使いカヴァレッジも自動で生成してくれます。
EUnit のドキュメントは @shibukawa が翻訳してくれています。この二つを読めばある程度理解できると思います。
EUnit には実はテスト以外に便利なツールがあります。まずはその紹介をしてから EUnit の紹介に写りたいと思います。
デバッグマクロというものが EUnit には入っています。これを使うには -include_lib("eunit/include/eunit.hrl"). をソースコードに書く必要があります。
-module(snowflake). -author('@voluntas'). -export([main/0]). -include_lib("eunit/include/eunit.hrl"). main() -> A = 10, ?debugVal(A), ok.
?debugVal というマクロは A = 10 というのを画面に表示してくれます。つまりプリントデバッグを簡単にしてくれるのです。またこのマクロはコンパイルオプションで NODEBUG を渡すことで無効になります。
rebar.config では erl_opts に {d, 'NODEBUG'} のように書けば NODEBUG を付けてコンパイル出来ます。
{erl_opts, [{d, 'NODEBUG'}, fail_on_warning, warn_export_all, debug_info]}.
Erlang ではよっぽど複雑に書かない限りはプリントでバッグで十分です。それを簡単にしてくれるツールを紹介させて頂きました。
さて、ユニットテストの書き方に戻ります。実は rebar の導入のところで snowflake モジュールを作ったときに test/snowflake_tests.erl というモジュールが同時に生成されます。
EUnit は test/ にある *_tests.erl というモジュールを自動的にテストモジュールとして判断してくれます。またそのモジュールの中の関数名も *_test() または *_test_() で終わる関数を自動でテスト関数と認識してくれます。
実際に EUnit の機能をもう少し見ていきましょう。
EUnit は主に二種類のテストにおける状態を管理する方法があります。
一つは setup です。初期化関数が呼ばれてから全てのテストが実行され最後にクリーンアップが呼ばれます。
もう一つは foreach です。テスト一つ一つに対して繰り返してセットアップとクリーンアップが呼ばれます。
基本的にはこちらの二つを使う事になります。
それ以外にテストの制御をする機能がいくつかあります
順番にテストするのが inorder でそれぞれのテストを並列に実行するのが inparallel です。
また、たまに使うのが timeout です。
Erlang にはメッセージパッシングの際に after を定義するとタイムアウトが指定できます。
これは指定した時間内に何かしらのメッセージを受け取らなかった場合はなんかしらの処理をするといったものです。
この場合のテストに timeout を使います。10秒後に受け取らなかったらこの値を返す場合は timeout を 20 くらいに定義しておいて、値をチェックして下さい。
EUnit のタイムアウト時間はとても短いのである程度時間がかかりそうな処理は全て timeout を使うと良いでしょう。
(書きかけです)
パッケージ管理について
私はパッケージ管理は使ったことがありません。なぜならちょちょいとつくるツールとかを Erlang で作る事がないため基本的には rebar の deps 機能を使ってライブラリを引っ張ってきているからです。ツールはもっぱら Python で書いています。
ただ無いわけではありません。Agner というのが最近の流行でそちらを日本語で紹介されている記事があります。(thx @snakeman)
Erlang パッケージ管理ツール Agner を試す
http://snakemanshow.blogspot.com/2011/03/erlang-agner.html
プロトタイプを気軽に作る、またはライブラリを簡単に使う等のお手軽さを考えるとパッケージ管理も便利です。