basic study for erlang #2

80
第二回 Erlang 基礎勉強会 M.Ikuta (@cooldaemon) 2010/7/28

Upload: cooldaemon

Post on 20-Aug-2015

2.084 views

Category:

Technology


0 download

TRANSCRIPT

Page 1: Basic Study for Erlang #2

第二回 Erlang 基礎勉強会M.Ikuta (@cooldaemon)

2010/7/28

Page 2: Basic Study for Erlang #2

本日の流れ

• 準備(心構え)

• Erlang プロセス概要

• プロセスのデザインパターン

• ハンズオン / フリートーク

Page 3: Basic Study for Erlang #2

本日の流れ

• 準備(心構え)

• Erlang プロセス概要

• プロセスのデザインパターン

• ハンズオン / フリートーク

Page 4: Basic Study for Erlang #2

• ちょっとしたコツを覚えて簡単に Erlang を習得

• 的を絞って効率的に習得

Page 5: Basic Study for Erlang #2

• Erlang は、サーバ (ミドルウェア)を書く為の DSL

• 全ての機能は、サーバを書く為にある(と割り切る)

• DSL だから言語仕様は小さい(と思い込む)

• ビジネスロジックの記述に向いていない

Page 6: Basic Study for Erlang #2

• 「Erlang は簡単だ!」の裏には・・・

• リスト、再帰、イミュータブルは初歩の初歩

• アクターモデルは、解りやすくて最高

Page 7: Basic Study for Erlang #2

• コツを掴めば Erlang は簡単だ!

• リスト、再帰、イミュータブル

• 第一回 基礎勉強会(前回)の範囲

Page 8: Basic Study for Erlang #2

• コツを掴めば Erlang は簡単だ!

• アクターモデルの基礎

• 第二回 基礎勉強会(今回)の範囲

Page 9: Basic Study for Erlang #2

• という事で今回はアクターモデルの話。ただし…

• メモリや GC、スケジューラなどの話は無し

• Supervisor, gen_*, proc_lib の話も無し

Page 10: Basic Study for Erlang #2

• という事で今回はアクターモデルの話。ただし…

• プロセスの概要と利用方法(配置)の話が中心

Page 11: Basic Study for Erlang #2

本日の流れ

• 準備(心構え)

• Erlang プロセス概要

• プロセスのデザインパターン

• ハンズオン / フリートーク

Page 12: Basic Study for Erlang #2

• プロセスは、小さく単純な逐次処理の塊

• 逐次処理であるため、並列処理の複雑さが無い

• 多数のプロセスが協調して大きな仕事を行う

Page 13: Basic Study for Erlang #2

• プロセス同士は粗結合

• 共有リソースは基本持たない

• メッセージパッシングで連絡を取り合う

Page 14: Basic Study for Erlang #2

• オブジェクトのメソッド呼び出しに似ているが…

• メッセージ送信は非同期でありキューに溜まる

• メッセージに対応するハンドラが無い事もある

• 関数評価よりコストが高い

Page 15: Basic Study for Erlang #2

• オブジェクトのメソッド呼び出しに似ているが…

• オブジェクトを作る感覚で処理の塊をプロセス化すると不必要な複雑さや負荷を招く

• 並列化不要な箇所を並列化しない

Page 16: Basic Study for Erlang #2

• どのノードにプロセスがあるか意識しなくて良い

• プロセス ID 内にノード情報が含まれている

• メッセージ送受信、監視(link/1, monitor/2) の方法が共通

• 監視プロセスの起動ノードを物理的に分散

Page 17: Basic Study for Erlang #2

• どのノードにプロセスがあるか意識しなくて良い

• 信頼できる閉じたネットワーク内であれば

Erlang のアクターモデルは非常に強力

Page 18: Basic Study for Erlang #2

• プロセス情報の確認

• i/0,1, ni/0

• process_info/1,2

• pman:start/0

• etop, getop

Page 19: Basic Study for Erlang #2

• プロセス情報の確認

• Erlang Shell で ni() を評価すると nodes(). で取得できるノードのプロセス一覧も取得できる

• Mac で pman, getop を実行すると X の起動に時間が掛かり、タイムアウトする事がある

Page 20: Basic Study for Erlang #2

• プロセス情報の確認

• etop, getop の PATH は code:lib_dir/1 で取得

`erl -noshell -eval 'io:format("~s", [code:lib_dir(observer)]).' -s init stop`\/priv/bin/etop -node foo@bar

Page 21: Basic Study for Erlang #2

• プロセス情報の確認

• Msgs, MsgQ は、キュー内のメッセージ数

• 選択受信すると溜まりがち

• 溜まると負荷になる(選択受信が重くなるので負のスパイラル)

Page 22: Basic Study for Erlang #2

• プロセス情報の確認

• Reds

• Reductions の略

• コンテキストスイッチに利用

Page 23: Basic Study for Erlang #2

• プロセス情報の確認

• Reds

• 値を変更する事や、process_flag/2 で優先順位を設定する事もできるが非推奨

Page 24: Basic Study for Erlang #2

• プロセスの作成

• spawn/1,2,3,4

• spawn_link/1,2,3,4

• spawn_monitor/1,3

• spawn_opt/2,3,4,5

Page 25: Basic Study for Erlang #2

• プロセスの作成

• spawn/1,3

• spawn(fun () -> ok end).

• 新たにプロセスを生成し、そのプロセス上で引数で指定された関数を実行する

Page 26: Basic Study for Erlang #2

• プロセスの作成

• spawn/1,3

• よほどの事(プロセス数の上限など)が無ければ、プロセスの生成は成功する

• 起動した後、即座に終了する事はある

Page 27: Basic Study for Erlang #2

• プロセスの作成

• spawn/1,3

• try catch で囲う、ありがちなミス

• 別プロセス上の例外は catch できない

Page 28: Basic Study for Erlang #2

• プロセスの作成

• spawn/2,4

• リモートノード上で、新たにプロセスを生成

• リモートノードのプロセスを、自ノードのプロセスと同様に扱える(register/1 は使えない)

Page 29: Basic Study for Erlang #2

• プロセスの作成

• spawn_link や spawn_monitor など…

• spawn と、それ以外(linkなど)を不分割に実行

• spawn 直後にプロセスが終了し、その後に

リンクされるという事がない

Page 30: Basic Study for Erlang #2

• プロセスの作成

• プロセス数の上限を調べる

• erlang:system_info(process_limit).

• プロセス数の上限を設定する

• erl +P 13421772

Page 31: Basic Study for Erlang #2

• プロセスに名前を付ける

• register/2

• unregister/1

• whereis/1

• global モジュール(今回は説明しない)

Page 32: Basic Study for Erlang #2

• プロセスに名前を付ける

• register/2

• register(Name, Pid).

• プロセス ID を引き渡す手間を省略できる

Page 33: Basic Study for Erlang #2

• プロセスに名前を付ける

• register/2

• ノード内で一意であり、同じ内容のプロセスを多重起動するケースでは使えない

• 常に起動し続ける(もしくは長生きする)事を想定するプロセスに名前を付ける

Page 34: Basic Study for Erlang #2

• プロセスに名前を付ける

• register/2

• 名前を付ける行為は、利用者に依存を強要する事になる

Page 35: Basic Study for Erlang #2

• プロセスに名前を付ける

• whereis/1

• whereis(Name).

• プロセス名からプロセス ID を取得

• プロセス ID を引数とする関数の評価に利用

Page 36: Basic Study for Erlang #2

• プロセスに名前を付ける

• unregister/1 は簡単なので割愛

• global モジュールは、今回の対象外であるため各自で調査

Page 37: Basic Study for Erlang #2

• メッセージの送信

• ! で送信

• (Pid | Name) ! Message.

• 非同期(必要な結果は別途受信)

• 成功・失敗は不明(エラー処理不要)

Page 38: Basic Study for Erlang #2

• メッセージの送信

• メッセージ送信はデータのコピー

• プロセス間でデータ共有をしない

• サイズの大きいメッセージ送信は負荷になるので送らない

Page 39: Basic Study for Erlang #2

• メッセージの送信

• 関数の評価よりコストが高い

• sample/send_message_vs_call_function.erl

• メッセージ送信回数を減らし、一回のメッセージ送信で、なるべく多くの仕事を行う

Page 40: Basic Study for Erlang #2

• メッセージの送信

• メッセージ送信の成功失敗は不明

• メッセージ送信は絶対成功すると考える

• 存在しないプロセスにもメッセージは送れる

• 送信先プロセスに依存しない

Page 41: Basic Study for Erlang #2

• メッセージの送信

• ただし…存在しないプロセスの名前にメッセージを送ると例外が発生する

• プロセスの名前を利用する事は、そのプロセスに依存すると言う事

• プロセスに名前を付ける意味を考える

Page 42: Basic Study for Erlang #2

• メッセージの受信

• receive で受信

• receive Message -> ok end.

• 同期(メッセージを受信するまで待つ)

• タイムアウトを指定する事も可能

Page 43: Basic Study for Erlang #2

• メッセージの受信

• タイムアウト 0 をキュー清掃に利用

• 清掃時(_ 受信含む)は、ログを残す癖を付ける

• 送るべきではないメッセージを送っていたり、送り先を間違えたりしている証拠となる

Page 44: Basic Study for Erlang #2

• メッセージの受信

• キューに溜まってるメッセージをパターンマッチで選択受信できる

• キューにメッセージが溜まりすぎると、選択受信の効率が落ちる

Page 45: Basic Study for Erlang #2

• メッセージの送受信

• 送信後、結果が必要な場合、下記をメッセージに含める事を推奨

• 結果を返せるよう、送信元のプロセス ID

• 他のメッセージを受けないよう、メッセージ毎にユニークなキー(make_ref/1 など)

Page 46: Basic Study for Erlang #2

• 監視(エラーハンドリング)

• link/1

• erlang:monitor/2

• exit/1,2

• process_flag/1

Page 47: Basic Study for Erlang #2

• 監視(エラーハンドリング)

• link/1

• link(Pid).

• link/1 によるリンクは相互リンク

• 片方が終了すると、もう片方も終了

Page 48: Basic Study for Erlang #2

• 監視(エラーハンドリング)

• link/1

• 存在しないプロセス ID を引数にすると例外が発生する

Page 49: Basic Study for Erlang #2

• 監視(エラーハンドリング)

• erlang:monitor/2

• erlang:monitor(process, Pid).

• 監視を行う(片方向)

Page 50: Basic Study for Erlang #2

• 監視(エラーハンドリング)

• erlang:monitor/2

• 監視中のプロセスが終了するとメッセージを受信

• {‘DOWN’, Reference, process, Pid, Reason}

Page 51: Basic Study for Erlang #2

• 監視(エラーハンドリング)

• link/1, erlang:monitor/2 は、リモートノードのプロセスを監視できる

• ローカルノード上にプロセスがある場合と同様のコードで良い

Page 52: Basic Study for Erlang #2

• 監視(エラーハンドリング)

• exit/1

• exit(Reason).

• 自プロセスを終了

• リンク先に終了シグナルをブロードキャスト

Page 53: Basic Study for Erlang #2

• 監視(エラーハンドリング)

• exit/1

• normal と kill は特別な終了シグナル

Page 54: Basic Study for Erlang #2

• 監視(エラーハンドリング)

• exit/1

• 実行するべき処理が無いために終了した場合のシグナルが normal

• リンクしているプロセスは終了しない

• exit(normal). でも同様

Page 55: Basic Study for Erlang #2

• 監視(エラーハンドリング)

• exit/1

• kill を受信すると、リンクしているプロセスには killed をブロードキャストする

Page 56: Basic Study for Erlang #2

• 監視(エラーハンドリング)

• exit/2

• exit(Pid, Reason).

• Pid へ終了シグナルを送信

Page 57: Basic Study for Erlang #2

• 監視(エラーハンドリング)

• exit/2

• 自プロセスは終了しない

• 実は、終了の偽装

Page 58: Basic Study for Erlang #2

• 監視(エラーハンドリング)

• process_flag/2

• process_flag(trap_exit, true).

• システムプロセスに昇格

Page 59: Basic Study for Erlang #2

• 監視(エラーハンドリング)

• process_flag/2

• システムプロセスは、リンク先のプロセスが終了した際、終了せずにメッセージを受信

• {‘EXIT’, Pid, Reason}

Page 60: Basic Study for Erlang #2

• 監視(エラーハンドリング)

• process_flag/2

• normal もメッセージとして受信できる

• kill の場合は強制的に終了する

Page 61: Basic Study for Erlang #2

• 監視(エラーハンドリング)

• 監視を行う事で様々なエラー処理が可能

• エラーログを残す

• 終了したプロセスを再起動

• 関連プロセスを一斉終了

Page 62: Basic Study for Erlang #2

• 監視(エラーハンドリング)

• 監視の仕組みを利用し、監視の木を構成する事を、本勉強会ではプロセス配置と呼ぶ

Page 63: Basic Study for Erlang #2

本日の流れ

• 準備(心構え)

• Erlang プロセス概要

• プロセスのデザインパターン

• ハンズオン / フリートーク

Page 64: Basic Study for Erlang #2

• プロセスを用いたコードを記述する上で、よく見かけるパターンを幾つか紹介

• 基本的には OTP のライブラリとして共通化され、提供されている

• OTP で提供されているライブラリに触れる前に、そのライブラリの背景にある考え方を学ぶ

Page 65: Basic Study for Erlang #2

• Client/Server

• sample/server_client/server_client.erl

• サンプルは簡易的な Key-Value Store

Page 66: Basic Study for Erlang #2

• Client/Server

• sample/server_client/server_client.erl

• get/1 は同期

• self/0, make_ref/1 の使い方に注意

• set/2 は非同期

Page 67: Basic Study for Erlang #2

• Client/Server

• sample/server_client/server_client_pm.erl

• 機能は、server_client.erl と同様

• Parameterized Module にしてある

Page 68: Basic Study for Erlang #2

• Client/Server

• sample/server_client/server_client_pm.erl

• register/2 で名前を付けていないので…

• 多重起動可能

• リモートノードで起動可能

Page 69: Basic Study for Erlang #2

• Client/Server

• sample/server_client/server_client_tx.erl

• 参照と更新を不分割に行える

• プロセスが逐次処理である事と、メッセージとして関数が送れる事を利用

Page 70: Basic Study for Erlang #2

• Finite State Machine

• sample/fsm/finite_state_machine.erl

• 関数 = 状態

• 同じメッセージを受けても状態により処理内容が異なる

Page 71: Basic Study for Erlang #2

• Finite State Machine

• sample/fsm/mutex.erl

• Mutex Semaphore

Page 72: Basic Study for Erlang #2

• Finite State Machine

• sample/fsm/mutex.erl

• ロック中に他のプロセスからロックされるとアンロックされるまで保留される

• プロセスが逐次処理である事と、メッセージを選択受信できる事を利用

Page 73: Basic Study for Erlang #2

• Event Managers and Handlers

• sample/event/*.erl

• イベントが発生した際に、関連する処理を次々に実行

Page 74: Basic Study for Erlang #2

• Event Managers and Handlers

• sample/event/*.erl

• 自作 Behaviour の定義

• 末尾再帰によるステータス維持

• try catch

Page 75: Basic Study for Erlang #2

• Supervisor Tree

• sample/supervisor/my_supervisor.erl

• 作業プロセスの管理

• normal, undef 以外で停止すると再起動

• 一斉停止

Page 76: Basic Study for Erlang #2

本日の流れ

• 準備(心構え)

• Erlang プロセス概要

• プロセスのデザインパターン

• ハンズオン / フリートーク

Page 77: Basic Study for Erlang #2

• やる事や、質問事項が決まっていない方への課題

• server_client_tx.erl をトランザクション対応する

• ヒント:プロセスディクショナリは get/0 で全データを取得できる

Page 78: Basic Study for Erlang #2

• やる事や、質問事項が決まっていない方への課題

• sample/tcp_server を使った echo サーバを実装

• 実装が終わったら tcp_server の仕組みを確認

Page 79: Basic Study for Erlang #2

• 上級者の方は…

• Supervisor で再起動されたプロセスが、再起動前のステータスを引き継ぐ方法を考え、実装

• ヒント: ets を使うと楽

Page 80: Basic Study for Erlang #2

• 上級者の方は…

• gen_tcp や gen_udp を使用するプロセスを

Supervisor の下に配置するコードを記述

• ヒント:処理が中断するプロセスは

gen_server と相性が悪いので proc_lib を利用すると良い