Skip to content

Instantly share code, notes, and snippets.

@pochi
Last active August 29, 2015 14:06
Show Gist options
  • Save pochi/4081b2b0a537ec41a834 to your computer and use it in GitHub Desktop.
Save pochi/4081b2b0a537ec41a834 to your computer and use it in GitHub Desktop.
Stuff goes bad

序章

゜フトりェアを走らせるこず

Erlangは他のプログラミング蚀語に比べお゚ラヌ凊理のアプロヌチが特別です。 共通的にはプログラム、プログラム環境、方法論で゚ラヌをさけるようにしたす。 もしランタむムで゚ラヌが起きたずきは䜕が必芁か考えたす。

環境にデプロむしたあずはもし゚ラヌがあれば新しいバヌゞョンをデプロむする必芁もあるでしょう。

䞀方でErlangは開発、運甚、ハヌドりェアずいったいかなるレむダヌでの問題がおきうるように 蚭蚈されおいたす。それはシステム䞊のいかなる゚ラヌをも取り陀けるずいうこずです。 もしあなたが党おの゚ラヌを事前にさけるのではなく、゚ラヌを取り扱えるようになれるであれば、 それは予期しおいない振る舞いに぀いおそういったアプロヌチがずれるずいうこずになりたす。

これはクラッシュさせようずいう考え方からきおいたす。 なぜなら党おの゚ラヌを事前に防ぐずいうのは非垞にコストが高いです。たた、゚ラヌに぀いおは プログラマヌが察凊方法を知っおいる必芁があり、それ以倖はプロセスを終わらせるかVMをそのたたにしおおくこずになりたす。

ほずんどのバグは凊理を再起動させおいい状態に戻せたす。 Erlangは人間の免疫システムず等䟡な環境を提䟛したす。他の蚀語はどうやっお䜓の䞭に病原菌を持ち蟌たないかを意識したす。 どちらの圢匏も非垞に重芁です。ほずんどの環境にさたざたな健康状態を保぀ための機胜を提䟛したす。 他の環境はランタむム時に゚ラヌチェックを行いたすが、それ以降は免疫システムのようなものは提䟛したせん。 システムが最初䜕か悪い状態になったずき厩壊しないようにErlang/OTPは医者になるこずができたす。 あなたはシステムの䞭に入り、本番環境でどのようなこずが起きおいるか泚意深く芋るこずができ、修正を詊みるこずもできたす。 Erlangは患者がそこにいるこずを必芁ずせず、掻動も䞭断しないようにしながらテストを拡倧しお様々な蚺断を行うこずができたす。

この本はErlangを利甚しお戊時䞭にどうやっお治療するかアドバむスできるようにしたす。 Erlangを利甚したデバッグの方法や倱敗がどこからきおいるか理解するための方法に぀いお蚘茉したす。

これは誰のための本

これは初心者向けの本ではありたせん。チュヌトリアルやトレヌニングセッションではなく、実際に本番環境にある 蚺断やデバッグ環境に関するものです。ガむドラむンを終えお、そこから先螏み蟌んだ䞖界に入るための暗黙的なフェヌズです。 この本を読む人はErlangずOTPに粟通しおいる人かずおもいたす。 Erlang/OTPに぀いおは私が理解しおいるようにお話ししたすが困惑した堎合は読者の人は自分で調べるこずができるず思っおいたす。 既に゜フトりェアのデバッグ方法に぀いお理解しおる人はこの本を読む必芁はないかずおもいたす。

この本をどうやっお読むか

この本は倧きく぀の章に分かれおいたす。 1぀目の倧きな話のテヌマはどのようにアプリケヌションを曞くかです。 コヌドベヌスを1章で説明しお、2章で䞀般的なノりハりを説明したす。3章に぀いおはシステムデザむンに぀いお説明したす。 2぀目の倧きな話のテヌマは蚺断に぀いおです。4章でどのようにノヌドに぀なげるかに぀いおはなしたす。 5章では基本的な蚺断方法に぀いおはなしたす。6章でクラッシュダンプに぀いお話しお7章はメモリリヌクに぀いおはなしたす。 8章はCPUに぀いおはなしたす。 最終章ではreconを利甚しお本番環境でシステムダりンする前にどのようにシステムコヌルを远跡するかに぀いおはなしたす。

どの章にも理解を助けるための質問かハンズオンを甚意しおいたす。

どのようにコヌドベヌスを読み蟌んでいくか

”゜ヌスを読むこず”がよくいわれる厄介な方法ですがErlangプログラマにずっおはそれはよく行うべきこずになりたす。 ラむブラリのドキュメントも完党はななかったり、叀かったり完璧ではありたせん。 ErlangプログラマはどちらかずいうずLisperに䌌おいお自分の問題を解決するのが最優先で、 テストやその他事情を解決するずいう傟向があたりありたせん。 あなたはコヌドを読んでなにをしおいるのかを理解し、自分でコヌドを修正しお自分のシステムで利甚できるようにする必芁がありたす。 他のどの蚀語もあなたが蚭蚈しおいない限り䌌たような問題は存圚するでしょう。 Erlangには倧きく分けお぀のタむプがありたす。䞀぀はErlangのベヌスコヌド、もう䞀぀はOTPアプリケヌション、 そしお最埌はOTPそのものになりたす。本章ではそれらをどう読み蟌んでいくか芋おいきたす。

単玔なErlang

もしErlangのコヌドを曞くずしたら、ほずんどは自分で曞くこずになるかずおもいたす。 特定の暙準方法に準拠しおいるのはたれなので、ほずんどは自分で䞭身を芋おいくこずになりたす。 README.mdを芋るずアプリケヌションのポむントは著者ぞのアクセス方法が曞いおいたりしたす。 幞運にも、初心者や玠晎らしいErlangプログラマによっおプロゞェクトはできおいるため、 滅倚に自分でから曞く必芁はなくなっおいたす。䞀般的にはrebarずいうツヌルを利甚しおOTPアプリケヌションを蚘述したす。

OTPアプリケヌション

OTPアプリケヌションの構成はシンプルです。䞀般的には以䞋のようなディレクトリ構成を持ちたす。

doc/
ebin/
src/
test/
LICENSE.txt
README.md
rebar.config

埮劙に違いたすが、構成はほずんど同じです。 それぞれのOTPアプリケヌションはebin/{AppFile}.appもしくはsrc/{AppFile}.app.srcがそれぞれ含たれおいたす。 2぀のファむルは以䞋のように動䜜したす。

{application, useragent, [
{description, "Identify browsers & OSes from useragent strings"},
{vsn, "0.1.2"},
{registered, []},
{applications, [kernel, stdlib]},
{modules, [useragent]}
]}.
{application, dispcount, [
{description, "A dispatching library for resources and task "
"limiting based on shared counters"},
{vsn, "1.0.0"},
{applications, [kernel, stdlib]},
{registered, []},
{mod, {dispcount, []}},
{modules, [dispcount, dispcount_serv, dispcount_sup,
dispcount_supersup, dispcount_watcher, watchers_sup]}
]}.

最初のケヌスはラむブラリアプリケヌションで、2぀目のケヌスは普通のアプリケヌションです。

ラむブラリアプリケヌション

ラむブラリアプリケヌションは普通appname_somethingをappnameずいうモゞュヌルで構成されたす。 これは䞀般的にむンタフェヌスモゞュヌルなのでどうやっおアクセスできるか芋るのに適しおいたす。 ゜ヌスを読むずどうやっお動䜜するか蚘述されおいたす。もしgen_serverやgen_fsm等があれば、 そこからどのようにsupervisorが動䜜するかわかりたす。 もしbehaivorが蚘茉されおいなければあなたの手で利甚可胜なステヌトレスなラむブラリです。 この䟋では自分で゚クスポヌトしお利甚可胜になるので早い理解が可胜になるでしょう。

普通のアプリケヌション

普通のアプリケヌションでは以䞋に二぀ポむントがありたす。

  • appname
  • appname_app

最初のファむルにはどのようなラむブラリをもっおいるかに぀いお蚘述し、 次のファむルにはトッププロセスがどのような振る舞いをするのか蚘述したす。 たたに最初のファむルで二぀の圹割を䞡方蚘述するずきもありたす。

もしあなたがアプリケヌションにラむブラリを远加するだけの簡単な倉曎であれば、 appnameを線集するだけです。もしアプリケヌションを修正するのであれば、 appname_appを線集する必芁がありたす。

アプリケヌションはトップレベルでsupervisorを起動し、pidを返华したす。 supervisorには今埌起動するchild processに぀いおも特城を蚘述しおおきたす。 ツリヌ構造䞊でより高い䜍眮にあるプロセスは基本的に生存確立が高くなりたす。 どのプロセスが重芁かしおいするこずも可胜です。supervisor内でプロセスを起動するず 既に開いおいるプロセスに䟝存しお配眮されたす。 さらに䟝存関係を持ったプロセスはグルヌプ化されなんにか゚ラヌが起きたずきは 䞀緒に萜ちおくれたす。これは慎重な遞択でおかしくなった状態を埩元するより 再起動したほうが懞呜な刀断だず考えられたす。 supervisorの再起動戊略はsupervisorが持぀プロセス間に䟝存したす。

  • one_for_one(simple_one_for_one) 䞀぀ず぀独立しお動きたす。倱敗はアプリケヌションに䌝えられ、トヌタルカりント数で刀断されたす。
  • rest_for_one 盎線的に互いに䟝存する堎合に利甚したす。
  • one_for_all 完党に䟝存する堎合に利甚したす。

この構造はOTPアプリケヌションでsupervisorがプロセスの扱いを意味したす。 これらを監芖するプロセスは以䞋のような圹割を持぀こずができたす。

  • gen_server リ゜ヌスを保持し、client/serverパタヌンの振る舞いをしたす。
  • gen_fsm 連続したむベントを䞀貫しお管理したす。プロトコルなどに向いおいたす。
  • gen_event コヌルバックのようなむベントドリブンに向いおいたす。通知などに利甚されたす。

これらのモゞュヌルは同様の構成をずりたす。゚クスポヌトされた公開関数やコヌルバック関数、 プラむベヌト関数が利甚できたす。 これらのsupervisorの関係ず皮類をベヌスにしお他のモゞュヌルを利甚したり、いろいろな実装が 蚘述されおいたす。

䟝存関係

党おのアプリケヌションは䟝存関係をもっおおり、それらの䟝存関係はそれぞれ䟝存関係をもっおいたす。 OTPアプリケヌションは基本的にそれらの間で状態を持ちたせんが、それらは開発者がapp fileに蚘述するずいう 正しいマナヌによっお保たれおいたす。図1.1にOTPアプリケヌションの構造を理解するのに圹立぀ものをのせおいたす。 このようなピラルキヌを䜿っおどこに䜕がおかれおいるのか理解するこずができたす。これに䌌たスクリプトずしお、 reconを利甚しおescript script/app_deps.erlを掻甚したす。䌌たような構成はobserveアプリケヌションがありたすが それぞれsupervisor構成をずっおいたす。 これらを利甚するこずでコヌドの理解を助けるこずになりたす。

OTPリリヌス

OTPのリリヌスはそこらにあるOTPアプリケヌションより理解するのは簡単です。 OTPリリヌスはリリヌス可胜なOTPアプリケヌションがパッケヌゞ化されたもので、どのアプリでも application:start/2を呌ぶこずで立ち䞊げずシャットダりンを可胜にしたす。 前のOTPのバヌゞョンでリリヌスしおいるアプリケヌションも基本的には適甚可胜です。 通垞これに蚭定ツヌルであるsystoolsやreltoolを利甚しおパッケヌゞングを行いたす。 これらを理解するために、私はhttp://learnyousomeerlang.com/release-is-the-wordを読むのをおすすめしたす。 もし可胜なら2014幎の頭にリリヌスされたrelxを利甚できるず簡単です。

Erlang゜フトりェアを構築する

ほずんどのErlangの本はどのようにしおOTPアプリケヌションを構築するかに぀いお曞かれおいたす。 しかしいく぀かの本はErlangコミュニティの話などがはいっおいたす。それらは本質ではありたせん。 この章では簡単にErlangに特化した簡単なツアヌを行うようにしたす。 OTPアプリケヌションはErlangの人が蚘述するほずんどのオヌプン゜ヌスになっおいたす。 事実、たくさんの人がOTPのリリヌスを必芁ずしおいたす。もしあなたが曞いおいる䜕かが誰かのモゞュヌルを 利甚しおいるのであればそれはおそらくOTPアプリケヌションでしょう。 あなたが構築したアプリケヌションをでプロむするのであればきっずそれはOTPリリヌスを利甚しおいるず思いたす。

メむンのビルドツヌルずしおrebarずerlang.mkをサポヌトしおいたす。 フォヌマットはErlangスクリプトでそれ自身や暙準ラむブラリをサポヌトしおいたす。erlang.mkは 冗長ですが非垞に高速にコンパむルされたす。 この章ではrebarを前提ずした解説をしたす。rebarがerlang.mkをサポヌトしおいるこずに぀いおもお話したす。

プロゞェクトの構成

OTPアプリケヌションの構成はOTPリリヌスずは少し違いたす。 OTPアプリケヌションは頂点に䞀぀のsupervisorがいおその䞋にたくさんの䟝存関係が存圚したす。 OTPリリヌスは耇数のOTPアプリケヌションがあっおそれぞれが䟝存しおいない可胜性がありたす。

OTPアプリケヌション

OTPアプリケヌションの基本構成は前章でお話ししおいたものず倧䜓同じです。

1 doc/
2 deps/
3 ebin/
4 src/
5 test/
6 LICENSE.txt
7 README.md
8 rebar.config

新しいのはdeps/ですがこれはrebarによっお自動的に生成されるものです。 これはErlangには暙準的なパッケヌゞ管理ツヌルがないためです。 人々は代わりにrebarを育おおプロゞェクトを管理するようになりたした。これはコンフリクトはなくなりたしたが それぞれで䟝存性をダりンロヌドする必芁がありたす。 これがrebar.configの䟋です。

1 {deps,
2 [{application_name, "1.0.*",
3 {git, "git://github.com/user/myapp.git", {branch,"master"}}},
4 {application_name, "2.0.1",
5 {git, "git://github.com/user/hisapp.git", {tag,"2.0.1"}}},
6 {application_name, "",
7 {git, "https://bitbucket.org/user/herapp.git", "7cd0aef4cd65"}},
8 {application_name, "my regex",
9 {hg, "https://bitbucket.org/user/theirapp.hg" {branch, "stable"}}}]}.

アプリケヌションはgitから再垰的に取埗されたす。かれらは取埗埌コンパむルオプションによっおコンパむルされたす。 そのディレクトリの䞭でOTPアプリケヌションの開発を行いたす。 それらをコンパむルするために、rebar get-deps compileを呌び出すずダりンロヌドしおアプリごずコンパむルしたす。 アプリケヌションを䞖界に公開する際、䟝存性無しで配信するこずができたす。 これによっおあなたがしたように同じアプリケヌションを構築できるし耇数回出荷する必芁もありたせん。 ビルドシステムは重耇しおいる郚分を理解し、必芁な凊理を䞀回だけ呌び出すようにしたす。

OTPリリヌス

リリヌスの堎合、構成が若干かわりたす。リリヌスはアプリケヌションの集合のため、 それが反映されたす。トップレベルにアプリを配眮しない代わりに、appsずdepsに ネストしたアプリを配眮したす。apps以䞋にアプリケヌションのコヌドを配眮しお、 deps以䞋に䟝存関係を蚘述したす。

apps/
doc/
deps/
LICENSE.txt
README.md
rebar.config

リリヌスに必芁なものを生成したす。SystoolずReltoolはこれらをカバヌしおおり、ナヌザを楜にさせたす。 さらに最近はrelxを利甚するのが䞀般的になっおいたす。 relxを利甚するず以䞋のようなファむルが生成されたす。

1 {paths, ["apps", "deps"]}.
2 {include_erts, false}. % will use currently installed Erlang
3 {default_release, demo, "1.0.0"}.
4
5 {release, {demo, "1.0.0"},
6 [members,
7 feedstore,
8 ...
9 recon]}.

relxを呌ぶずリリヌス甚のパッケヌゞを_rel以䞋に配眮したす。 もしrebarが奜きならrebar.configに以䞋のように远蚘しおおくずより䟿利になりたす。

{post_hooks,[{compile, "./relx"}]}.

こうしおおくずrebar comileするずその際にrelxもよばれるようになりたす。

Supervisorずstart_linkの意味

耇雑なシステムではほずんどの倱敗ず゚ラヌはその状態でのみおきるもので、リトラむするのが いい方法だずいわれおいたす。Jim Grayの論文ではMean Times Between Failuresによるず䞊蚘のバグは supervisorのリスタヌトをしないより4倍優れおいるずでおいたす。 倧事なのはErlangのsupervisorsずその子プロセスの起動は同期的ずいうこずです。 各OTPプロセスが起動から関連プロセスを守る可胜性があるずいうこずになりたす。 プロセスが死んだ堎合、あたりに頻繁に倱敗するたでリトラむされるようになりたす。 非垞にミスが起こりやすい堎所がありたす。supervisorがプロセスのクラッシュ埌に再起動する前に 埅ち時間はありたせん。もしネットワヌクベヌスのアプリケヌションが初期化フェヌズ䞭にリモヌト先のサヌビスが 萜ちた堎合、そのアプリケヌションは倧量に無駄な再起動を行うでしょう。そしお、そのシステムはシャットダりンしたす。 倚くのErlang開発者がsupervisorが冷华期間を持った方がいいのではないかず䞻匵しおいたす。 私は単玔な保蚌に関する理由でそれを匷く反察しおいたす。

保蚌に぀いお

プロセスを再起動するこずはよく知られた状態に戻すためです。そこから物事はリトラむされたす。 先ほどの䟋のように初期化が安定しおいない堎合、supervisorのも぀メリットは小さいです。 初期化プロセスは䜕があっおも安定すべきです。子プロセスたちは事前に確認したうえで起動するこずで しすおむが健党な状態で起動するこずができたす。 もしあなたがそういった構成を手䟛しない堎合try...catch節でルヌプするだけでメリットをほずんど 享受できないでしょう。supervisorのプロセスは初期化のなかでベスト゚フォヌトではなく、 完党に保蚌すべきです。これは䟋えばあなたがデヌタベヌスやサヌビスに接続するクラむアントを蚘述する堎合、 問題がないこずを保蚌できるたでは初期化の䞭で接続を確立しおはいけないずいうこずです。

䟋えばあなたがErlangシステムを立ち䞊げる前にロヌカルのデヌタベヌスを立ち䞊げおいるので、 初期化ののなかで接続を確立できたずするず、再起動するず正垞に動䜜したす。 前提条件の保蚌ができない堎合、ノヌドはクラッシュしたす。それはシステム党䜓の怜蚌ずしおは倱敗です。

もしデヌタベヌスがリモヌトにある堎合接続に倱敗する堎合を考えなければいけたせん。 それは分散システムが萜ちおいるずいうこずになりたす。この状態であなたが保蚌できるのは 接続リク゚ストを送るこずだけですが、{error, not_connected}が出るだけです。 デヌタベヌスぞの再接続はシステムの安定性に圱響を䞎えるこずなく、あなたが最適だず信じる冷华期間を通しおから 行うこずになりたす。最適化等を行う初期化フェヌズでは詊せたすが、プロセスの堎合、䜕かしらの理由で接続が切れお その埌再接続できるようにしなれければないらないためです。

もし倖郚接続で障害を想定する堎合、あなたのシステム自䜓で保蚌を行うようにしないでください。 私たちは珟実䞖界を取り扱うため、䟝存する倖郚システムは垞にオプションであるべきです。

副䜜甚

もちろんラむブラリやプロセスが呌び出したクラむアントがデヌベヌスなしで動くこずを期埅しおいない堎合、 ゚ラヌになりたす。ビゞネス䞊のルヌルやクラむアントに察しおできる/できないを蚭定するこずず 察凊するこずは同じ問題でも党く違う方針の問題です。 䟋えばクラむアントはシステム党䜓に圱響しないような゚ラヌを無芖できるようにする運甚を怜蚎すべきです。

初期化ずsupervisorsのアプロヌチで違う点はクラむアントを呌びだす偎はクラむアント自身ではないので、 どの皋床倱敗を蚱容できるのか決めるこずができたす。 これはフォルトトレラントをでデザむンするのに非垞に重芁です。supervisorsが再起動する堎合は、 安定した状態ではないずいけたせん。

䟋: 保蚌のない初期化

以䞋にプロセスの状態をの䞀郚で接続を保蚌するコヌドを蚘述したす。

1 init(Args) ->
2 Opts = parse_args(Args),
3 {ok, Port} = connect(Opts),
4 {ok, #state{sock=Port, opts=Opts}}.
5
6 [...]
7
8 handle_info(reconnect, S = #state{sock=undefined, opts=Opts}) ->
9 %% try reconnecting in a loop
10 case connect(Opts) of
11 {ok, New} -> {noreply, S#state{sock=New}};
12 _ -> self() ! reconnect, {noreply, S}
13 end;

かわりに曞き盎したコヌドが以䞋になりたす。

1 init(Args) ->
2 Opts = parse_args(Args),
3 %% you could try connecting here anyway, for a best
4 %% effort thing, but be ready to not have a connection.
5 self() ! reconnect,
6 {ok, #state{sock=undefined, opts=Opts}}.
7
8 [...]
9
10 handle_info(reconnect, S = #state{sock=undefined, opts=Opts}) ->
11 %% try reconnecting in a loop
12 case connect(Opts) of
13 {ok, New} -> {noreply, S#state{sock=New}};
14 _ -> self() ! reconnect, {noreply, S}
15 end;

曞き盎したこずによっお、初期化の䞭でいく぀かの保蚌を行うようにしたした。 これで接続ができるこずを確認しおから接続マネヌゞャを利甚可胜にするずいう動きになりたす。

芁玄

システムに察しお2぀のアプロヌチで怜蚎を行いたした。 蚭定ファむルのようなファむルシステムぞのアクセス、ログでUDPポヌトを開くようなロヌカルリ゜ヌスぞのアクセス、 ネットワヌクやディスクからの埩元等supervisorは芁件ずしおどの皋床同期的に埅぀のが問題ないのか蚭定するこずに なるでしょう。基本的には起動から10分かかるのはたれですが、GBレベルの同期などは問題ないはずです 䞀方で、コヌドがロヌカルにないデヌタベヌスや倖郚サヌビスに䟝存する堎合は、通垞の操䜜䞭におきる可胜性があり、 それは今でも埌でも代わりがないので、supervisorを郚分的に起動する方針にするずいいでしょう。 あなたは同様に凊理すべきですがシステム内での制玄は枛らした方がいいでしょう。

アプリケヌション戊略

倱敗が連続するこずがノヌドが死んだずいう考えるのは正しくありたせん。䞀床システムがOTPアプリケヌションずしお 皌働したらその埌はそれ自身がノヌドが生存可胜かどうか刀断すべきです。 それぞれのOTPアプリケヌションは3぀の方法で立ち䞊げるこずができたす。permanent, transient, temporaryで これらはapplication:start/2で呌び出すこずもできるしあなたのリリヌス蚭定ファむルに蚘述するこずも可胜です。

  • permanent: アプリが停止した堎合、手動で停止するもの以倖の党䜓が停止する。
  • transient: 正垞終了の堎合䜕もしない。他の理由の堎合システム党䜓を停止する。
  • temporary: どんな理由でも党䜓を停止したせん。レポヌトはされたすが䜕もしたせん。

もちろんOTP配䞋にあるアプリケヌションを再起動するこずも可胜です。

負荷に察する蚈画

昔は、私が䞀番遭遇しおいた゚ラヌの原因はメモリ䞍足によるものでした。 さらにいうず、それはメッセヌゞキュヌによるものでした。 これに察する扱い方はたくさんありたすが、どう察応するかに぀いおは、 うごいおいるシステムを理解する必芁がありたす。 物事を単玔化しおいくず、ほずんどのプロゞェクトはバスルヌムのシンクにみえおきたす。 ナヌザずデヌタの入力は蛇口からきおいたす。Erlangシステムはシンクずパむプで、 出力は䞋氎道システムず考えるこずができたす。Erlangのノヌドが䜕かしらの理由で あふれおしたった堎合、䜕が原因か考えるこずがきわめお重芁です。 誰かが倧量に氎をシンクにいれおいたせんか䞋氎道システムは正垞にうごいおいたすか

小さすぎるパむプを蚭蚈したしたか キュヌが壊れるようにするのはそんなに難しいこずではありたせん。クラッシュダンプでこの情報を芋぀けるこずができたす。 しかし、なぜ壊れたかを知るのは少しトリッキヌです。プロセスの圹割や実行時の情報をもずに、 氎が急激に流れおきたのか、プロセスがブロックされおいないかなど考えるこずができたす。 䞀番難しい郚分はこれをどのようにしお盎すかです。 シンクが倚くの廃棄物で぀たった堎合、シンク自䜓を倧きくしようずしたす。 シンクの排氎が小さすぎるようであればそれを最適化したす。それからパむプ自䜓が狭くないか確認しお そうであれば最適化したす。䞋氎道がそれ以䞊察応できなくなるようになるたでは負荷はシステムの䞭で 察応するこずになりたす。ここでのポむントは、シンクやバスルヌムの远加を党䜓の負荷を芋お決めるこずです。

そしおバスルヌムのレベルでそれ以䞊改善できない点もありたす。ログを送りすぎおいたり、 デヌタベヌスの䞀貫性で埅぀必芁があったり、組織の䞭で十分な知識が持おおいないケヌスも考えられたす。 ポむントを探すこずによっお、本圓のボトルネックポむントを探すこずができ、それたでの最適化も必芁だったかずは 思いたすが、幟分無駄になっおしたっおいるこずもわかりたす。私たちはより賢明になる必芁がありたす。 よりシステムを軜くするために情報をよく蚭蚈する必芁がありたす。 さらに負荷が高すぎるずシステムの制玄をいれおそこを廃棄するかサヌビスの質を䞋げるか難しい刀断が必芁になりたす。 これらのメカニズムに関する二぀の戊略が"back-pressure"ず"load-shedding"になりたす。

この章ではErlangシステムが共通的に負荷がこえおしたう原因に぀いお芋おいきたす。

共通的な負荷リ゜ヌス

数少ない原因ではありたすが、どのような蚭蚈を仕様ずも負荷があふれおしたうケヌスがありたす。 それらはスケヌルアップやシステムが成長したずきや、想定より困難な問題が発生した堎合に芋られたす。

error_logの爆発

皮肉なこずですが、゚ラヌログは最も壊れやすいものの䞀぀です。Erlangをむンストヌルしたずきのデフォルトでは、 ゚ラヌログのネットワヌクもしくはディスク䞊に曞き出すこずぱラヌの発生より倚くの時間がかかりたす。 これはナヌザが生成したログは倧芏暡なプロセスのクラッシュに特に圓おはたりたす。 前者の堎合、゚ラヌログは継続的に入っおいるメッセヌゞを期埅しおいたせん。こういったケヌスは䟋倖的で 基本的に倚くのトラフィックを期埅しおいたせん。埌者の堎合、プロセス党䜓の状態がログにコピヌされたす。 これだけでメモリを逌迫するメッセヌゞを受け取り、それがOutOfMemoryになりえない堎合、 远加の凊理に時間がかかる可胜性がありたす。執筆時点での最善の解決策はlagerずいうラむブラリを利甚するこずです。 lagerは党おの問題を解決する蚳ではありたせんが、しきい倀をこえたOTPの゚ラヌログメッセヌゞを切り捚お、 動的にナヌザメッセヌゞの同期ず非同期方匏で必芁に応じお切り替えたす。 これはナヌザが送信しおきた倧量のメッセヌゞを捌くようなこずはできたせんが、そういった堎合は プログラマが制埡しおいるものです。

ロックずブロック呜什

ロックずブロック呜什はプロセス間で継続的にタスクをやり取りしおいる䞭であるプロセスが予想以䞊に長く 時間がかかっおいるずきによく問題になりたす。よくある䟋の䞀぀ずしお、TCP゜ケットからメッセヌゞを受け取るのを 埅っおいる堎面をよく芋たす。このタむプのブロック呜什は、メッセヌゞキュヌにメッセヌゞが積み䞊がりたす。 特に悪かった䟋はlhttpcずいうforkラむブラリにかかれおいたプヌル管理ラむブラリです。 これはテストはほずんど通っおいたしたし接続タむムアりトも10ミリセカンドをこえないように蚭蚈しおいたした。 しかし最初の数週間完璧に皌働したあず、あるリモヌトサヌバがダりンしたこずが原因でクラむアントが萜ちたした。 この原因は10ミリセカンド凊理にかかるずしおいたものが突然党おの接続を諊めなければならなくなったからでした。 秒間5ミリセカンド皋床のリク゚ストで9000メッセヌゞ/秒凊理しおいたものが䞊蚘が原因で18,000メッセヌゞ/秒の負荷がかかり 手に負えなくなりたした。 私たちが考えた解決策は呌び出し偎の接続䜜業を残したたた管理者がその䜜業を完了させたず匷制的に認識させるこずでした。 ブロック操䜜はラむブラリによっお党おのナヌザが利甚可胜になったため、管理者がやるべきこずがなくなり、 今はより倚くのリク゚ストを受け入れるこずができたす。 メッセヌゞを䞭倮で受けずる必芁がなくなったため、時間のかかるタスクは極力倖でやるようになりたした。 プロセスを増やすこずによっお負荷が予枬できるブロック操䜜やバッファリング操䜜ずいうのはいいアむデアです。 本質的に同時に行う必芁がないものをプロセスを増やしお耇雑性を生み出すたえに、本圓にそれが必芁か確認するこずも重芁です。 別のオプションずしおタスクを非同期のものに倉換するずいうこずです。もしそれが蚱されるのであれば、 長時間ゞョブを開始し、それを䞀意に識別するこずで元々の芁求を凊理したす。リク゚ストを利甚可胜になったずき、 サヌバに前のトヌクンず䞀緒になっお戻されたす。サヌバはメッセヌゞを受信し、トヌクンずマッチングをかけ、 ブロックタむムなしで芁求を返すこずができたす。 このオプションはたくさんのプロセスを利甚する点ずコヌルバック地獄になる点はすこしデメリットもありたすが、 より効率的にリ゜ヌスを掻甚するこずができたす。

期埅しおいないメッセヌゞ

OTPアプリケヌションを開発しおいるず想定しおいないメッセヌゞがくるこずはたれです。 なぜならOTPアプリケヌションはhandle_info/2でハンドリングされおいるので、予期しないメッセヌゞは あたり蓄積されないでしょう。しかしながらOTP準拠システムが党おの動䜜を実装しおいるずは限りたせん。 もしモニタリングツヌルがあるのであれば、どのようにメモリが増えおいくのか、キュヌのサむズを芋お 萜ちそうなプロセスを芋぀けるこずができたす。ここで必芁ずされるメッセヌゞをハンドリングするこずで 問題を修正するこずができたす。

入力の制限

入力制限で最も簡単なのはErlangシステム䞊のメッセヌゞキュヌの制限を行うこずです。 それは最もシンプルで最適かもしれたせんがナヌザの動きを遅くしおいるこずを意味したす。 䞀方でナヌザには悪い経隓を䞎えるこずになりたす。デヌタ入力を制限する䞀般的な方法は 同期制埡䞍胜なプロセス呌び出しを行うこずです。次のリク゚ストに移る前にレスポンスを芁求するこずにより、 察象郚分が遅延するこずを防ぎたす。このアプロヌチの難しい郚分はボトルネックずなっおいるキュヌが、 システムの䞀番先端ではないため、深く探玢しようずするずその近蟺にある党おのキュヌを最適化しおから 探す必芁がありたす。ボトルネックは䞀般的にデヌタベヌスや、ディスクIO,ネットワヌク䞊のサヌビスなどがあげられたす。 これは結局同期郚分をバック゚ンドに䞀旊凊理を䟝頌しお終わるたでナヌザに"枛速しおください"ず䌝えるこずができたす。 開発者はよくこのパタヌンをナヌザごずのAPI制限等に利甚しおいたす。これはQoSの保蚌や公平なリ゜ヌスの䟛絊が可胜になりたす。

タむムアりトはどのように蚭蚈すべきか

この方匏でトリッキヌのなのは同期呌び出しで負荷をあたえる郚分をバック゚ンドに远いやるこずですが、 その同期䜜業がどの皋床時間がかかるものなのかタむムアりトを蚭定しなければなりたせん。 この問題を衚珟する䞀番の方法はシステムの先端で最初の時間がはじたっおから、その本質の指什が朜るこずです。 これはタむマヌが蚈画した時間埅たなければ行けないずいうこずを意味したす。 䞀番簡単なのは埅ち時間を無制限にするこずです。Pat Hellandはこれに察しお面癜い回答をしおいたす。

いく぀かのアプリケヌション開発者は議論しおタむムアりトなしで問題ないずいうかもしれたせん。
私はタむムアりトを30幎にしないか提案したした。そうするず次々ず合理的なレスポンスが生成されたす。
なぜ30幎が愚かで無制限が合理的なのでしょう私は未だに無制限埅぀メッセヌゞングアプリケヌションに
出䌚ったこずがありたせん。

結局これはケヌスバむケヌスだずおもいたす。しかし倚くのケヌスではフロヌ制埡に違うメカニズムを利甚するように 蚭蚈すべきでしょう。

暩限をたずねる

バック゚ンドにたかせるシンプルな方匏ずしおブロックしおほしいリ゜ヌス、䟋えば早くできないものや ビゞネスやナヌザにクリティカルなものを特定するずいうものがありたす。それらのリ゜ヌスをロックする 際にリク゚ストやそれらを利甚する暩利があるか確認を行いたす。確認するものずしお、CPU,メモリ、負荷、 䞊列床、レスポンス時間などがあれられたす。SafetyValveアプリケヌションはこういったニヌズにそったフレヌムワヌクです。 さらにシステムや障害に関しお䜕かナヌスケヌスで䜕かしたいケヌスに察しおも、利甚可胜な回路がありたす。 䟋えば、breaky, fuse, circuirt_breakerなどがありたす。 ETSを利甚したアドホックなシステムずいった他のツヌルも利甚可胜です。重芁な郚分はシステムの先端でブロックしおしたうずきに プロセスがデヌタにアクセスする暩利があるか確認するのですが、コヌドの本質的なボトルネックは暩利があるかどうか 確認する郚分になりたす。この方匏で進んでいくこずのメリットはタむマヌや同期レむダヌの抜象化を耇雑にしなくおいい点です。 あなたはボトルネックを守り、゚ッゞや制埡点、党おにおいお読みやすいものが出来䞊がりたす。

ナヌザは䜕をみるか

バンク゚ンドのトリッキヌな郚分に぀いおここで説明したす。同期呌び出しを通しお暗黙的にバック゚ンドにたかせた凊理が 完了したずき、負荷が実際にどこにかかおいるか知る唯䞀の方法はシステムが遅くなっおいく堎面です。悲しいこずに、 匱いハヌドりェア、匱いネットワヌク、関連のない堎面での負荷、䜎速のクラむアントなど様々なこずが考えられたす。 システムがバック゚ンドに凊理を適甚する際にそれ自身の応答性を蚈枬するこずは熱を持った人を蚺断するのず同矩です。 そしお䜕か間違っおいないかを衚瀺したす。

蚺察のための暩限を確認するず、あなたに明確なレポヌトを送れるようむンタフェヌスを定矩したす。 システム党䜓ずしお負荷が高い堎合や、指什のなかでどこかの制限にひっかかっおいる堎合を知るこずができる。 システムを蚭蚈する際に行うべき遞択がありたす。ナヌザはナヌザごずに制限をもっおいたすかそれずも システム党䜓ずしお制限をもっおいたすか システム党䜓ずしお制限をも぀かノヌドごずに制限を持぀かどちらにしおも実装は簡単です。しかし、どちらかに倒すず 䞍公平になるかもしれたせん。90%のリク゚ストをしめおいるナヌザがいたずしお、そのナヌザは他の倧倚数のナヌザのせいで プラットフォヌムが利甚できないかもしれたせん。 ナヌザごずに制限を持぀ず、公平ですしプレミアムナヌザで制限をなくすずいう察応も可胜です。これは単玔にいいこずですが、 より倚くのナヌザに利甚しおもらうためにはより効率的なシステム党䜓での制埡を怜蚎する必芁がありたす。 100ナヌザではじめお䞀分間で100リク゚ストを制限ずした堎合最倧で䞀分間に10,000回リク゚ストがくるこずになりたす。 20ナヌザ同様の条件で远加するずたちたちクラッシュしおしたうかもしれたせん。 ナヌザが増えおくるずそれだけ゚ラヌが起こりえる可胜性が増えおきたす。ビゞネス的に蚱容できるトレヌドオフを考慮する ポむントは重芁ですが、ナヌザはシステム党䜓が䞀定時間萜ちおいるだけより、䞀日䞭萜ちおいるこずに䞍満を持぀ 傟向がありたす。

廃棄デヌタ

Erlangシステムの倖偎で遅くなっお、それがスケヌルアップできないずきあなたはデヌタを捚おるかクラッシュさせる必芁がありたす。 それは悲しい珟実ですがそれ以倖の察応方法は難しいです。プログラマ、゜フトりェア゚ンゞニア、コンピュヌタサむ゚ンティストは 䜿っおいないデヌタを削ぎ萜ずし、䜿いやすいように保ちたす。諊めずに最適化を続けお正垞な状態にしたす。 しかしながら、もしデヌタが出お行くより入っおくる方が早い堎合、 Erlangシステムがそれを十分に察応できるか刀断すべきポむントになりたす。それはafterずいうコンポヌネントで凊理したす。 もしあなたがデヌタが入っおくる制限を持っおいない堎合クラッシュしないようにデヌタを廃棄する必芁がありたす。

ランダムに捚おる

ランダムにデヌタを捚おるこずが最も簡単でシンプルに保぀こずができたす。 トリックずしおは䟋えば0から1の間の数字は捚おるず行ったしきい倀によるものです。

-module(drop).
-export([random/1]).
random(Rate) ->
maybe_seed(),
random:uniform() =< Rate.
maybe_seed() ->
case get(random_seed) of
undefined -> random:seed(erlang:now());
{X,X,X} -> random:seed(erlang:now());
_ -> ok
end.

もしメッセヌゞの95%を保持したいのであれば、認蚌はcase drop:random(0.95) of true -> send(); false -> drop() endを 呌びたす。もしくはdrop:random(0.95) andalso send()これは特にメッセヌゞの削陀に䜕も意味を持たせない堎合です。 maybee_seed関数はプロセスの䞭でseedが怜蚌された倀かくらだないものになっおいないか怜蚌しおいたす。 たた、耇数回now関数が呌ばれるのをさける目的でも利甚されおいたす。 このメ゜ッドから1぀わかったこずがありたす。ランダムドロップは理想的にはキュヌレベルではなくProducerレベルで 実行されるべきです。キュヌの負荷をさけるためには最初の堎所からデヌタを送らない方法です。 なぜならErlangには閉ざされたメヌルボックスはないので保蚌された受信プロセスのみ捚おるようにしたす。 このプロセスは乱暎に動䜜しおいるもので、倧量のメッセヌゞを取り陀くようにしお、正垞に動䜜させるよう぀ずめたす。

䞀方で、Producerレベルでの廃棄は党おのプロセス間で共有できる必芁がありたす。 これは面癜い堎所で最適化が行われたす。ETSのテヌブルやapplication/set_envを利甚しおしきい倀を蚭定するのです。 これによっお負荷をベヌスに廃棄すべきメッセヌゞをコントロヌルでき、蚭定デヌタに぀いおはapplication:get_env/2を利甚したす。 䞊蚘の技術を応甚するずConsumerレベルではなくメッセヌゞの優先順䜍で廃棄すべき割合を蚭定できたす。

キュヌバッファ

キュヌバッファはランダム廃棄ではない方法でメッセヌゞをコントロヌルする方法ずしおいい方法です。 特にコンンスタントにはいっおくる線圢ストリヌミングデヌタではなく入力デヌタの負荷が気になるずきに有効です。 プロセスがキュヌの圢を持ったメヌルボックスであるにも関わらず、あなたは党おのメッセヌゞを受信するこずができたす。 キュヌバッファは安党に動䜜するため、぀のプロセスを必芁ずしたす。

  • gen_serverのような䞀般プロセス
  • メッセヌゞをバッファリングするプロセス

これを動䜜させるために、バッファプロセスはメヌルボックスからデヌタを取り陀いお、キュヌにデヌタをおきたす。 い぀でもサヌバを動䜜できるようになっおおり、バッファプロセスにいく぀のメッセヌゞがきおいるか確認できたす。 バッファプロセスはキュヌから取り出しおサヌバにわたし、デヌタを蓄積したす。 キュヌにデヌタがきお新しいメッセヌゞを受け取れるようになればあなたはデヌタを取り出しお新しいずこにおくこずができたす。 必芁であれば叀いメッセヌゞは削陀もできるし残しおおくこずもできたす。 ここでリングバッファのように安定した受信数のメッセヌゞを維持し、か぀負荷に察する耐性も必芁です。 PO Boxが代衚的なラむブラリです。

スタックバッファ

スタックバッファはキュヌバッファを制埡するのに䟿利ですが、遅延があたりない芁件が重芁であるこずを意識しおください。 バッファをスタックずしお利甚する堎合、2぀のプロセスが必芁で、䞀぀はキュヌバッファずもう䞀぀は キュヌバッファをリストずしお扱うためのプロセスです。 スタックバッファが䜎レむテンシで特に喜ばれるのはバッファフロヌトに関連するものです。 もしキュヌに入ったメッセヌゞの䞭でいく぀か遅延が発生するず、キュヌにある党おのメッセヌゞが数ミリ秒遅延したす。 結局、メッセヌゞの鮮床は悪くなり廃棄の察象になりたす。䞀方でスタックにするこずで新しいものを時間通りサヌバに送りながら 制限された個数の芁玠のみ所持するこずが可胜になりたす。 あなたはスタックを確認しながらQoSに応じおスタックを管理するこずができたす。PO Boxもそれににた実装になっおいたす。 スタックバッファの欠点は必ずしも送られおきた順番に凊理する必芁がないずいうこずです。 それぞれが独立のタスクずなっおいる堎合はいいのですが、連続したものが䞀぀のタスクずしおなっおいる堎合、 無駄なタスクが残っおしたう可胜性がありたす。

時間に特化したバッファ

もしあなたが叀すぎるむベントの前に叀いむベントを察応しないずいけない堎合、そこから耇雑な構成になり、 毎回スタックを確認しないずいけなくなるので、コンスタントにスタックを削陀するのが非垞に非効率になりたす。 面癜いアプロヌチは耇数のスタックをもったバケットを甚意しおスタック間を時間で管理するずいう方法です。 もしQoSに満たないスタックがあれば時間を参照しおスタック党䜓を削陀すればいいのです。 これは䞀郚のメリットに察しお倚くのデメリットがあるように思うかもしれたせんが、ずにかくメッセヌゞを削陀するずきに 䜎レむテンシで行いたい堎合は非垞に奜たしい方法になりたす。

定期的な負荷を扱う

定期的な負荷を扱うための新しい解決策が必芁になるかもしれたせん。キュヌずバッファヌは時々起こる負荷に察しお 盞性がいいです。そしおこれらは入力に察しお远い぀けるようなずきに信頌性を持っお凊理ができたす。 こういった堎合たくさんのメッセヌゞが䞀぀のプロセスに集䞭するず問題が発生したす。 この堎合぀のいいアプロヌチがありたす。

  • スケヌルさせお耇数のプロセスでロヌドバランスする
  • ETSテヌブルを掻甚する(入力を枛らす)

ETSテヌブルはプロセスより倚数のリク゚ストを捌くこずができたすが、ETSテヌブルがサポヌトしおいる動䜜は ごく基本的なもののみです。䞀貫したカりンタから远加、削陀、単玔な読み蟌みは動䜜可胜です。 ETSテヌブルは2぀のアプロヌチを必芁ずしたす。 䞀般的にはなされるのは最初の条件は普通のプロセスずしおうたく動䜜するこずです。 N個のプロセスをあげお、それらに名前を぀けお、どれかを遞んでメッセヌゞを投げるこずができるようにしたす。 負荷を想定しおむベントを分散させおランダムにプロセスを遞択し、か぀信頌性が必芁ずされたす。 状態を持たないコミュニケヌションが必芁なのであれば、䜜業はほが同じ方法で共有され、倱敗に察しお鈍感になりたす。 経隓的に私は動的にatomを䜜成するこずを避けおいお、ETSテヌブルのread_concurrencyをtrueにセットするこずでworkerに 共有する方匏を奜んでいたす。これは動くこずももちろんですが埌でいく぀かの曎新を行う際に䟿利だからです。 䌌たような方匏ずしおlhttpcラむブラリを利甚しおドメむンを基本ずしたロヌドバランス構成を採るこずもできたす。

二぀目の条件はロックずカりンタヌを䜿いたすが、基本的な構成は残しお、実際にメッセヌゞを送る前に ETSテヌブルを曎新できるようにしおおくこずです。党おのクラむアントが共有するのに制限があるのはよく知られおいたすが プロセスを䜜成するリク゚ストはこの制限をクリアするようにしたす。 このアプロヌチはメッセヌゞキュヌを避けるためにdispcountが利甚されおいたす。そしおこれはリク゚ストが拒吊されおも 埅たなくおいいように䜎レむテンシを保蚌した䞊で実珟するこずができたす。 その埌ラむブラリ偎ですぐ諊めるのか別のワヌカヌで詊すのか実装したす。

どのようにしお廃棄するか

ここたでのほずんどの解決策はメッセヌゞの量をベヌスにしお考えおきたしたが、 メッセヌゞサむズや、耇雑性、もしくはあなtが指暙ずしたい䜕かをベヌスに考えるこずもできたす。 キュヌかスタックバッファを利甚したずきは、゚ントリの数を数えるのではなく、メッセヌゞサむズや 負荷をチェックする必芁があるかもしれたせん。 私が経隓䞊考えるのはメッセヌゞの詳现を考慮せずに捚おるのではなく、 各アプリケヌションが受け入れるかどうか刀断する独自のポむントを持っおいたす。 もちろんそこにはデヌタをあなたに送っお"fire-and-forget"に埓った方法で察応するこずも可胜です システム党䜓を非同期パむプラむンの䞀郚だず考えるが、その方法だずなぜデヌタが捚おられたのか ゚ンドナヌザ向けの説明が難しくなりたす。もしあなたが廃棄するメッセヌゞを集めお、"Y個のメッセヌゞが砎棄されたした。 理由はXです"ずするのであれば、ナヌザの理解を埗やすくなりたす。 この堎合Herokuのlogprex機胜を採甚するこずになるかずおもいたす。これはルヌティングに関するシステムで L10 errorsを吐き出すこずができたす。これを利甚するこずでシステムの䞀郚が党おのメッセヌゞを扱うこずができないこずを ナヌザに䌝えるこずができたす。

最埌に負荷があるずきに受け入れいれるかどうかに぀いおはシステムを利甚するナヌザに䟝存する傟向がありたす。 新しい技術を開発するより芁件は簡単で少しの倉曎で可胜な堎合が倚いですが、 ずきどきそれだけでは察応できないこずもありたす。

リモヌトノヌドぞの接続

実行䞭のサヌバず察話する堎合、基本的に次のいずれかの方法でおこなわれたす。 䞀぀はscreenやtmuxを利甚しおバックグラりンドで起動しおおきシェルを利甚可胜な状態にしおおくこずです。 もう䞀぀は関数や包括的な蚭定ファむルを再読み蟌みさせる方法です。 動的なセッションで察話する堎合REPL圢匏で動䜜しおいれば䞀般的にはアクセス可胜です。 プログラムや蚭定ファむルで管理しおいる堎合、慎重に怜蚎する必芁があり、䜕をしたいのか はっきりしさせおからアクセスする必芁がありたす。埌者の方法は党おのシステムで利甚可胜なはずですので 今回はこれが䜕を䞎えるのかに぀いおはスキップしたす。 Erlangの堎合REPLよりもう少し"動的"に近い方匏をずりたす。基本的にErlangVMはREPLを必芁ずしおおらず、 バむトコヌドを実行しお動䜜されるので、シェルは必芁ありたせん。 しかしErlangがどのように䞊列にマルチプロセスで動䜜するのか、そしお分散のためにどのようなサポヌトを しおいるか知るために任意のErlangプロセスに察しおREPL圢匏でアクセス可胜です。 Erlangでは䞀぀のscreenに䞀぀のshellずいう圢匏ではなく、䞀床に耇数のErlangVM䞊に存圚する 耇数の接続にアクセスするこずができたす。 基本的に同時に二぀の接続にアクセスする堎合はcookieをベヌスにアクセスしたすが、それが含たれおいない ケヌスでもアクセスするこずは可胜です。 その堎合ノヌドの名前をもずにそれらのノヌドに事前にアクセスできるこずを 確認しおおく必芁がありたす。prior measure

ゞョブ制埡モヌド

ゞョブ制埡モヌドJCLモヌドはErlangシェル䞊で^Gを抌すこずで切り替えられたす。 メニュヌからリモヌトシェルにアクセスするためのオプションが遞べたす。

(somenode@ferdmbp.local)1>
User switch command
--> h
c [nn] - connect to job
i [nn] - interrupt job
k [nn] - kill job
j - list all jobs
s [shell] - start local shell
r [node [shell]] - start remote shell
q - quit erlang
? | h - this message
--> r ’server@ferdmbp.local’
--> c
Eshell Vx.x.x (abort with ^G)
(server@ferdmbp.local)1>

䞊蚘コマンドを発行するず、行が線集されおリモヌトシェルずしお機胜するようになりたす。 党おの出力はリモヌト先の出力がロヌカル䞊に衚瀺されたす。 シェルを抜けおJCLモヌドにもどるには^Gを抌したす。ロヌカルシェルから抜けるためには ^G qをタむプしたす。

(server@ferdmbp.local)1>
User switch command
--> q

自動的にクラスタ党䜓に接続しないようにhiddenモヌドで接続した方がいいかもしれたせん。

Remsh

このメカニズムはJCLモヌドに䌌おいたすが、甚法が違いたす。 党䜓のJCLモヌドにアクセスするためには以䞋のようにアクセスしたす。

erl -name local@domain.name -remsh remote@domain.name

これは短瞮版です。

erl -sname local@domain -remsh remote@domain

その他のErlangのオプションも怜蚌されたす。 JCLモヌドで動いおいるものずメカニズムは同じですが、最初からリモヌトシェルに アクセスできおいるずころが違いたす。^Gが安党に抜けるいい方法です。

SSHデヌモン

Erlang/OTPはSSH実装が同胞されおいおそれはサヌバにもクラむアントにもなるこずができたす。 そのデモアプリケヌションはErlangのリモヌトシェルで動䜜しおいたす。 これを動䜜させるために、SSHのキヌを甚意しおおく必芁があるので、テスト甚に以䞋のように蚭定しおおきたす。

$ mkdir /tmp/ssh
$ ssh-keygen -t rsa -f /tmp/ssh/ssh_host_rsa_key
$ ssh-keygen -t rsa1 -f /tmp/ssh/ssh_host_key
$ ssh-keygen -t dsa -f /tmp/ssh/ssh_host_dsa_key
$ erl
1> application:ensure_all_started(ssh).
{ok,[crypto,asn1,public_key,ssh]}
2> ssh:daemon(8989, [{system_dir, "/tmp/ssh"},
2> {user_dir, "/home/ferd/.ssh"}]).
{ok,<0.52.0>}

私はここでいく぀かのオプションを蚭定しおいたす。system_dirはhostファむルがどこにあるのか指定しおいたす。 user_dirで蚭定ファむルの堎所を指定しおいたす。特別なパスワヌドや公開キヌの指定もオプションで蚭定可胜です。 SSHデヌモンを介しお接続する堎合以䞋のように接続したす。

$ ssh -p 8989 ferd@127.0.0.1
Eshell Vx.x.x (abort with ^G)
1>

こうするこずで珟圚のハヌドにErlangをむンストヌルするこずなくアクセスができたす。 接続を切るずきはSSHの接続を切るだけで倧䞈倫です。q()やinit:stop()関数を実行するず リモヌト先が停止しおしたうので気を぀けおください。 もし接続で困った堎合、-oLogLevel=DEBUGを぀けお実行するずSSH接続デバッグの出力が衚瀺されたす。

名前づけされたされたパむプたち

あたり知られおいない方法ずしお明瀺的に分散を指定しおないErlangノヌドに察しお名前付けされたパむプを 通しお接続するこずができたす。これはrun_erlずいう名前付けパむプをラップしたコマンドでアクセスできたす。

$ run_erl /tmp/erl_pipe /tmp/log_dir "erl"

最初の匕数が名前付けパむプを利甚するファむルです。二぀目の匕数がログをはくディレクトリです。 ノヌドに接続するためにはto_erlを利甚したす。

$ to_erl /tmp/erl_pipe
Attaching to /tmp/erl_pipe (^D to exit)
1>

これでシェルが接続されたす。`抜けるずきは^Dをタむプしたす。

実行状態の蚈枬

ErlangVMを本番環境で利甚するこずの䞀぀のセヌルスポむントは透過的に実行時の䞭身、 デバッグ、プロファむル、分析ができるこずです。 プログラムを利甚しおアクセスできるこずのメリットはそれらを利甚したツヌルが簡単に䜜れるので、 タスクや監芖ツヌルを䜜るのが簡単だずいうこずです。必芁なずきにVMの情報を閲芧するこずができるのです。

経隓的なアプロヌチでは成長しおいくシステムを健康的な状態に保぀ためには党おの芖点で監芖できるこずが重芁です。 それが普通かそう出ないかずいうアドバむスは䞀般的にはありたせん。あなたは通垞の状態を知るためにある時間垯の たくさんのデヌタが欲しいでしょう。䜕日かたっお必芁な情報を集めるこずができたら、それをOFFにしお察策をたおるこずができたす。 この章では普通のOTPアプリケヌションにどのようにアクセスできるのを芋せたいずおもいたす。 しかし、党おの特城が䞀カ所にあるわけではないですが、本番システムで自身で簡単にアクセスできたす。 それらは䟿利なツヌルずいうよりはブロックを構築するずいうほうが近いかもしれたせん。 基本的なツヌルはreconラむブラリにたずめられおいおテキストのハむラむトや䟿利なオペレヌションがたずたっおいたす。

党䜓を芋る

倧きい意味でVMの䞭身をみようずするず、コヌドが走っおいるかどうかは関係なく、統蚈情報を集めるのが圹立ちたす。 もちろんあなたの狙いが長い期間の情報取埗だずするず、䞀週間をこえる小時間のりィンドりが怜出できないずいっお 問題があがっおきたす。メモリやプロセスリヌク含めこれらの長い期間ぞのいい察応ずしおは、たずデヌタを取埗しお 日単䜍や週単䜍の掻動のなかでどういったこずが発生するか数ヶ月確信できるたで芋極めるこずです。 こういったケヌスでErlangの蚈枬ツヌルは䟿利です。以䞋にオプションがありたす。

  • folsom: VMのメモリ䞊に統蚈情報をためたす。党䜓かアプリケヌションを指定できたす。
  • vmstats and statsderl: statsdを通じおgraphiteに統蚈情報を送りたす
  • exometer: graphite, collectd, statsd, Riak, SNMPなどの統蚈情報ずfolsomを統合したす
  • ehmon: 暙準出力に吐き出したす、そのあずsplunk等ずもれんけいできたす
  • ETSテヌブルを利甚しお継続的にデヌタ出力するこずができたす。
  • 問題なければシェルの䞭でルヌプさせるこずも可胜です。

これらをあなたの目的にそっお必芁な情報を取埗するこずがいいずおもいたす。

メモリ

VM䞊のメモリを調べる堎合はerlang:memory()を利甚したす。

1> erlang:memory().
[{total,13772400},
{processes,4390232},
{processes_used,4390112},
{system,9382168},
{atom,194289},
{atom_used,173419},
{binary,979264},
{code,4026603},
{ets,305920}]

これにはいく぀かの説明が必芁です、 単䜍は党おバむトで返し、割り圓おられおいるメモリを返したす。 これは埌で芋おみたすがOSが割り圓おるものより少し小さい倀になりたす。 totalフィヌルドはプロセスずシステムが利甚しおいるメモリの合蚈になりたす。 processesはErlangのプロセスが利甚しおいるメモリを、systemはシステムで利甚しおいるメモリになりたす。 残りはETSテヌブルずVMで利甚しおいるメモリの䞀芧で他の隠れフィヌルドに぀いおは説明したせん。 もしあなたがVM党䜓のメモリ量を知りたいのであれば、本圓の最倧倀ではulimitで取埗できる倀ですが、 正確にはVMの䞭で取埗するこずは難しいです。 もししりたい堎合はtopかhtopを利甚しお取埗するこずができたす。幞運にもrecon_alloc:memory/1で取埗可胜です。 匕数は以䞋になりたす。

  • used: Erlangが割り圓おた䞭で利甚しおいるメモリを衚瀺したす
  • allocated: VMが割り圓おたメモリの衚瀺
  • unused: allocated - used
  • usage: パヌセント衚瀺する

これらの远加オプションも指定可胜です、そしお7章で利甚するメモリリヌクに぀いおも必芁になるずおもいたす。

CPU

䞍幞にもErlang開発者にずっおCPUをプロファむルするのは難しいです。理由は以䞋の通りです。

  • VMがスケゞュヌリングによっおプロセスに関連のない仕事を倚くやっおおり、数倀化が難しい
  • VM党䜓はreductionをベヌスにCPUを䜿甚しおいる。reductionの数によっおスケゞュヌルが倉曎される。
  • 仕事量が少ないずきにスリヌプしないようスレッドがルヌプしおいるため。

これらの芁玠により、実際にうごいおいるCPUを知るこずは難しいです。Erlangが共通的に利甚する CPUもありたすが実際にたくさんの実䜜業がはいるずCPUはさらに利甚するこずになりたす。 最も正確にデヌタを知れるのがスケゞュヌラのりォヌルタむムです。それはオプションで実行できる 統蚈でノヌド䞊で手で実行するずそこから定期間隔で取埗したす。 これはErlangプロセスが実際に動䜜しおいた時間を瀺したす。 NIFs, BIFs, garbage collectionなどが察象になりたす。 これはCPU利甚率ずいうよりかはスケゞュヌラ利甚率の倀ずいうほうが正しいです。 Erlang/OTPリファレンスマニュアルで基本的な䜿いかたは曞いおありたすがここではreconを䜿っお取埗しおみたす。

1> recon:scheduler_usage(1000).
[{1,0.9919596133421669},
{2,0.9369579039389054},
{3,1.9294092120138725e-5},
{4,1.2087551402238991e-5}]

recon:scheduler_usage(N)はNミリ秒間隔でデヌタを取埗し、各スケゞュヌラの利甚時間を出力したす。 このケヌスではおおきく二぀のスケゞュヌラがあっお䞀぀は93%皋床、もう䞀぀は1%以䞋しか䜿っおいたせん。 ただ、htopを利甚するず各コアの利甚率は以䞋のように芋えたす。

1 [||||||||||||||||||||||||| 70.4%]
2 [||||||| 20.6%]
3 [|||||||||||||||||||||||||||||100.0%]
4 [|||||||||||||||| 40.2%]

この結果はErlangが比范的暇なずきにずった結果ですが、OSからみるず忙しく芋えたす。 他にも面癜い振る舞いがみれおいお1点目のポむントではOSが報告しおきた内容より Erlangが報告した内容のほうが高くなっおいたす。OSリ゜ヌスを埅っおいるスケゞュヌラたちは 圌らがこれ以䞊仕事ができないものだず刀断しおいたす。もしOSがCPUを利甚しないタスクをしおいる堎合 Erlangスケゞュヌラは仕事ができないものだず刀断しお高い利甚率が衚瀺されたす。 これらの把握は容量蚈画時には重芁でCPUが指し瀺すものよりErlangのものを信甚した方がよいでしょう。

プロセス

党䜓でみるずVMの䞭でどの皋床タスクをこなしおいるかみるこずができたす。䞀般的にErlangでいいずいわれおいるのは 本圓に同時に動いおいるプロセスを䜿うこずです。䟋えばWebサヌバでいうず1リク゚ストもしくは1接続で1プロセス䜿いたすし ステヌトフルの堎合1ナヌザあたり1プロセス远加されるこずになるでしょう。そしおノヌド䞊で䜕本のプロセスを 䜿っおいるのか統蚈を取埗するこずができたす。ほずんどのツヌルは5.1でお話したしたがマニュアルで利甚するずきは 以䞋のように取埗できたす。

1> length(processes()).
56535

この倀を取埗するこずでプロセスリヌク等の情報に圹立おるこずができたす。

ポヌト

ポヌトもプロセスず同様に考慮すべきです。 ポヌトは倖の䞖界ず接続するためのデヌタタむプで、TCP゜ケット、UDP゜ケット、SCTP゜ケット、 ファむル蚘述子などがあげられたす。プロセス同様ポヌトをカりントするメ゜ッドがありたす。length(erlang:ports()) しかしこのメ゜ッドでは党おのタむプのポヌトをマヌゞしおしたうのですが、 reconを利甚しおそれらを゜ヌトしおみせるこずが可胜です。

1> recon:port_types().
[{"tcp_inet",21480},
{"efile",2},
{"udp_inet",2},
{"0/1",1},
{"2/2",1},
{"inet_gethost 4 ",1}]

このリストは各ポヌトに぀いおカりントされおおり、デヌタタむプも持っおいたす。 タむプの名前はErlang VMが持っおいるもの自身を文字列にしたものです。 "_inet"が぀くものは䞀般的にTCP,UDP,SCTPずいったプロトコルを瀺したす。 "etype"ファむルは䞀般的にファむルを瀺すもので、"0/1"ず"2/2"は暙準IOず暙準゚ラヌIOを衚珟しおいたす。 ほずんどの他のタむプはport programやport driverなどプログラム内で利甚されるドラむバ名を瀺したす。 これらを远跡するこずでシステムの負荷状況やシステム評䟡挏れ等を確認するこずができたす。

掘り䞋げおいく

あなたが問題を持っお倧きなログ等を芋るずきはい぀でも、たずは目的にそっおその呚蟺から興味を持ち始めるでしょう。 奇劙な状態のプロセスがありたすかだずしたらそれは远跡が必芁になりたす 远跡するこずは入出力の確認等で倧倉䟿利ですがそこにいき぀く前に、もっず掘り䞋げるこずが必芁です。 メモリリヌク以倖メモリリヌク自䜓は少し特別な技術が必芁でそれは7章で説明したすはほずんどが プロセスずポヌトファむルず゜ケットに関連したす。

プロセス

Erlangシステムにおいおプロセスは非垞に重芁です。なぜならプロセスは䞭心にいおそこから始たり、 そこから始たるこずの倚くを知っおいたす。幞運にもVMは倚くの情報を利甚可胜で、いく぀かは安党に䜿うこずができたすが、 いく぀かは本番環境で利甚するのにおすすめしないものもありたす。 なぜならかれらは倧量のデヌタをコピヌしおメモリヌを消費し、それを衚瀺しようずするずノヌドがこらせれおしたうかもしれたせん

党おの倀はprocess_info(Pid, key) もしくは process_info(Pid, [keys])を呌ぶこずで取埗できたす。 以䞋にいく぀かの共通で利甚するキヌを瀺したす。

Meta:
  dictionary: プロセス蟞曞にある党おを返したす。䞀般的にギガバむトレベルのデヌタを入れるべきではないので安党です。
  group_leader: IOが走っおいるプロセスの芪プロセス(format/1-3の出力)
  registered_name: もしプロセスが名前を持っおいたらそれを返したす
  status: スケゞュヌラから芋たプロセスの状態。次の倀を持っおいたす。
    exiting: プロセスは完了しおいるがクリアされおいない状態
    waiting: receive ... end で埅っおいる状態
    running: 実行䞭
    runnable: 始められる状態だがスケゞュヌルが他のプロセスを実行しおいる状態
    garbage_collecting: GC
    suspended: BIFもしくはポヌトバッファが䞀杯になっおいるこずによっお䞭断されおいる状態。
               このプロセスはポヌトが忙しい状態じゃなくなったずきに再実行されたす。
Signals:
  links: 党おのリンクしおいるプロセスず゜ケットやファむルディスクリプタの䞀芧を返したす。
         基本的には安党ですが数千の情報をもった倧きなスヌパヌバむザには泚意が必芁です。
  monitored_by: 珟圚のプロセスを監芖しおいるプロセス䞀芧を返したす(erlang:monitor/2ず同じ)
  monitors: monitored_byの反察の皮類です。あるプロセスに監芖されおいるプロセス䞀䞞を返したす。
  trap_exit: もしプロセスがトラップしおいる堎合はtrueをそれ以倖はfalseを返したす
Location:
  current_function: 実行䞭の関数情報をタプルで衚珟したす。{Mod, Fun, Arity}
  current_location: モゞュヌル内のどこにいるかタプルで衚珟したす。 {Mod, Fun, Arity, [{File, Filename}, {line, Num]}
  current_stacktrace: 珟圚のスタックずレヌスを返したす
  initial_call: プロセスがspawnされたずきの関数情報を{Mod, Fun, Arity}で返したす。どのプロセスにspawnされたか識別する際に䟿利です。
Memory used:
  binary: バむナリぞの参照ずそのデヌタサむズを返したす。もしプロセスが倧量にそれらを解攟した堎合安党に利甚できないかもしれたせん。
  garbage collection: プロセス内のGCされた情報を返したす。内容は"subject to change"ずしお文曞化されたす。フルsweep GCなどのオプションを通しお䜕回GCされたのかずヒヌプサむズに぀いお返したす。
  heap_size: Erlangプロセスにはoldずnewヒヌプがありたすが、これはGCによっお振り分けられたす。これは䞀番新しい䞖代のヒヌプをスタックサむズ含めお返华ししたす。倀はwordsの䞭に入っおいたす。
  memory: プロセスで利甚しおいるメモリコヌルスタック、ヒヌプ、VMで利甚されおいるメモリをバむトレベルで返华したす
  message_queue_len: プロセスのメヌルボックスの䞭にあるメッセヌゞ数を返したす
  message: プロセスの䞭にあるメッセヌゞ党おを返したす。これはたれにメヌルボックス内をロックしおしたうので本番環境で利甚するのはおすすめできないずきがありたす。垞にたずはmessage_queue_lenでサむズを確認しえtください。
  total_heap_size: heap_sizeず䌌おいたすがこれにはold領域も含たれおいたす。
Work:
  reductions: Erlang VMはスケゞュヌリングの実装をポヌタブルにするこずで任意のタスク単䜍でスケゞュヌリングできるようにしおいたす。時間ベヌスにするずたくさんのOSでErlang VMは効果的に動きたせん高いreductionsであればプロセスがたくさん仕事をしおいるこずになりたす。

幞運にもこれらは安党に利甚可胜でrecon:info/1を利甚するこずでヘルプを掻甚できたす。

1> recon:info("<0.12.0>").
[{meta,[{registered_name,rex},
{dictionary,[{’$ancestors’,[kernel_sup,<0.10.0>]},
{’$initial_call’,{rpc,init,1}}]},
{group_leader,<0.9.0>},
{status,waiting}]},
{signals,[{links,[<0.11.0>]},
{monitors,[]},
{monitored_by,[]},
{trap_exit,true}]},
{location,[{initial_call,{proc_lib,init_p,5}},
{current_stacktrace,[{gen_server,loop,6,
[{file,"gen_server.erl"},{line,358}]},
{proc_lib,init_p_do_apply,3,
[{file,"proc_lib.erl"},{line,239}]}]}]},
{memory_used,[{memory,2808},
{message_queue_len,0},
{heap_size,233},
{total_heap_size,233},
{garbage_collection,[{min_bin_vheap_size,46422},
{min_heap_size,233},
{fullsweep_after,65535},
{minor_gcs,0}]}]},
{work,[{reductions,35}]}]

recon:info/1はこれを拠点にしお動けるように最初の匕数にpidのようなものを指定しおそれを扱いたす。 それはpidに関する文字列("<0,12,0>")、名前をatomで指定({global, Atom})、もしくはサヌドパヌティの指定方匏(gproc: {via, gproc, Name})や タプルでの指定({0, 12, 0})も可胜です。プロセスはデバックしたいノヌドのロヌカルであるこずが必芁です。 もしあなたがカテゎリ情報だけほしいのであれば盎接以䞋のように参照できたす。

2> recon:info(self(), work).
{work,[{reductions,11035}]}

もしくは同じ答えを出すのにprocess_info/2も利甚可胜です。

3> recon:info(self(), [memory, status]).
[{memory,10600},{status,running}]

埌者の堎合は䞍確実な情報を取埗しおいるこずになりたす。 党おのデヌタを利甚しお私たちはシステムをデバックするこずができたす。理解しようずするには 曞くプロセスのデヌタを察象のプロセスのデヌタを芋るこずになるでしょう。

高いメモリ利甚率を芋た堎合、䟋ずしおプロセス䞀芧ず高いメモリ利甚率N個をみたいずしたす。 そのずきはrecon:proc_count(Attribute, N)を利甚するず以䞋の結果を埗るこずができたす。

4> recon:proc_count(memory, 3).
[{<0.26.0>,831448,
[{current_function,{group,server_loop,3}},
{initial_call,{group,server,3}}]},
{<0.25.0>,372440,
[user,
{current_function,{group,server_loop,3}},
{initial_call,{group,server,3}}]},
{<0.20.0>,372312,
[code_server,
{current_function,{code_server,loop,1}},
{initial_call,{erlang,apply,2}}]}]

いく぀かの属性が衚瀺されたすが、長い時間動䜜しおいるプロセスを把握できるため、ずおも䟿利です。 しかしながら問題もあっおほずんどのプロセスが短呜の堎合、状況を確認するのに十分な時間がありたせん。 䟋えばたった今忙しい状態にしおいるコヌドやプロセスを把握したいずきなど

こういったケヌスのために、Reconはrecon:proc_window(Attribute, Num, Milliseconds)を甚意しおいたす。 これはwindowのスナップショットを芋るのに䟿利です。䟋えば以䞋のようなタむムラむンがあったずしたす。

--w---- [Sample1] ---x-------------y----- [Sample2] ---z--->

この関数は二぀のサンプルをミリセコンド秒で定矩するものずしたす。 これらのサンプルはwずx, yずz, xずyの間のどこかで生存するでしょう。しかし䞍完党のため、あたり圹にはたちたせん。 もしあなたのプロセスがxからyの間に実行されたずするず、あなたはこれより少ない時間にサンプリングしたこずを 確信できるのでwからzの間を評䟡すればいいこずになりたす。これをしないで結果を芋るず、 デヌタを蓄積するのに10倍時間がかかる長時間プロセスが䞀぀ではなかったずきそれらは巚倧な消費者になっおしたいたす。 この関数は以䞋のような結果を埗るこずができたす。

5> recon:proc_window(reductions, 3, 500).
[{<0.46.0>,51728,
[{current_function,{queue,in,2}},
{initial_call,{erlang,apply,2}}]},
{<0.49.0>,5728,
[{current_function,{dict,new,0}},
{initial_call,{erlang,apply,2}}]},
{<0.43.0>,650,
[{current_function,{timer,sleep,1}},
{initial_call,{erlang,apply,2}}]}]

これら二぀の関数は問題のあるプロセスの特城を芋぀けるこずができたす。

OTPプロセス

もしプロセスに関する質問がOTPプロセスの堎合本番環境にあるプロセスは倧䜓OTPだずおもうそれらを 監芖するツヌルがさらにありたす。䞀般的にはsysモゞュヌルを利甚しお䞭身を芋るこずになりたす。 ドキュメントを読めばなぜ䟿利かわかりたす。それはOTPに察しお以䞋のような特城を持っおいたす。

  • 党おのメッセヌゞず状態の移行、グロヌバルバッファさえもシェルないしファむルで芋るこずができる
  • 統蚈情報(reductions, message count, timeなど)
  • プロセスのステヌタスを取埗できる
  • プロセスの状態を取埗できる
  • 状態を眮き換えるこずができる
  • コヌルバックずしお利甚可胜なデバック関数をカスタマむズできる

プロセス実行の䞭断たたは再開する機胜も提䟛したす。 これらの詳现たでは立ち入りたせんがそれらがあるこずは芚えおおくべきです。

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment