鉄筋の使用
Rebarは以前のバージョンとの互換性を維持せずに非常に積極的に開発しているため、このチュートリアルには古い情報が含まれている場合があります。Erlangで開発する場合、多くの場合、異なるソースから依存関係を収集し、必要なバージョンを追跡し、プロジェクトを配布するためのOTPリリースを作成する必要があります。 物事はかなり日常的で不快です。 開発が不快な瞬間をより少なくするために、Basは非常に便利なツール-Rebarを作成しました。 この記事では、サードパーティの依存関係を使用し、構成可能なOTPリリースを作成する実際の例で使用することの利点を明らかにします。
Rebarはhg.basho.com/rebar/downloads/rebarからダウンロードできます。 これは、複数のビームモジュールを含む1つの単一ファイルです。 PATH実行可能ファイルの検索パスを入力する便利な場所、たとえば
~/bin/
または
/usr/local/bin/
ます。
プロジェクトに取りかかりましょう。
まず、プロジェクトを配置するディレクトリ(
gpt
)を作成し、そこに移動します。
$ mkdir gpt && cd gpt
その中に、アプリケーションのソースファイルを直接保存するサブディレクトリを作成します。
$ mkdir -p apps / gpt && cd apps / gpt
アプリケーションのスケルトンの作成:
$ rebar create-app appid = gpt
appidパラメーターは、アプリケーションの名前を定義し、それに応じてソースファイルのプレフィックスを定義します。
$ ls -1 src
gpt_app.erl
gpt.app.src
gpt_sup.erl
アプリケーションの説明とgprocの依存関係を.appファイルの空白(
src/gpt.app.src
)に追加します。
{説明、「GProcチュートリアル」}、
...
{アプリケーション、
[
カーネル、
stdlib
gproc%<---アプリケーションはgprocに依存
]}、
...
プロジェクトが保存されている最上位ディレクトリに戻り、その中に
rel
サブディレクトリを作成してそこに移動します。
$ cd ../../
$ mkdir rel && cd rel
Relには、リリースを作成するために必要なファイルが含まれます-プロジェクトを実行するために必要なすべて、そのランタイムの依存関係すべて。
rebar
を使用して、
nodeid
パラメータに名前を渡すことにより、ノードの空白を作成します。
$ rebar create-node nodeid = gptnode
reltool.config
ファイルの編集:
...
{lib_dirs、["../deps"、 "../apps"]},% <---これらのディレクトリでは、reltoolはアプリケーションの依存関係も検索します。
{rel、 "gptnode"、 "1"、
[
カーネル、
stdlib
サスル
gproc、%<--- gprocアプリケーション
gpt%<---アプリケーション
]}、
...
次に、
files/vm.args
編集して、ノードの名前を変更
files/vm.args
ます。
-name gptnode@127.0.0.1
に
-sname gptnode @ localhost
トップレベルのディレクトリに戻りましょう。
$ cd ../
そして、次の内容の
rebar.config
ファイルを作成します。
%%依存関係はここにあります
{deps_dir、["deps"]}。
鉄筋が見える%%サブディレクトリ
{sub_dirs、["rel"、 "apps / gpt"]}。
%%コンパイラオプション
{erl_opts、[debug_info、fail_on_warning]}。
%%依存関係リスト
%%対応するgitリポジトリのmasterブランチは、gprocディレクトリに複製されます。
{deps、
[
{gproc、 "。*"、{git、 "http://github.com/esl/gproc.git"、 "master"}}
]}。
これで、リリースを作成する準備ができました。 いくつかの
rebar
コマンドを実行しましょう(コマンド出力は省略されます):
$ rebar get-deps
$鉄筋のコンパイル
$ rebar generate
get-deps
コマンドは依存関係をダウンロードします。 私たちの場合、これはgprocアプリケーションです。
compile
コマンドは明らかにすべてのソースファイルをコンパイルし、
generate
はリリースを作成します。
rel/gptnode
は、他のホストに簡単に移動できます(もちろん、リリースにはErlang仮想マシンが含まれているため、バイナリ互換性が必要です)。 リリースを作成した後、何が起こったかを実行します。
(cd rel / gptnode && sh bin / gptnode console)
必要なすべてのアプリケーションが実行されていることを確認します。
(gptnode @ localhost)1> application:which_applications()。
[{sasl、 "SASL CXC 138 11"、 "2.1.9.2"}、
{gpt、「GProcチュートリアル」、「1」}、
{gproc、「GPROC」、「0.01」}、
{stdlib、「ERTS CXC 138 10」、「1.17.2」}、
{カーネル、「ERTS CXC 138 10」、「2.14.2」}
gptとgprocに興味があります。 ご覧のとおり、これらはこのリストにあります。
gprocを使用する
そのため、
rebar
でそれを把握し、簡単なプロジェクトを作成して操作する方法を学びました。 gprocに取りかかりましょう。
ご存じのとおり、Erlangのアプリケーションは、原則として、メッセージを交換する多くのプロセスで構成されています。
プロセスがメッセージの送信先を知るためには、レジストラにいくつかの座標をプロセス識別子に変換させる必要があります。 デフォルトでは、Erlang / OTPはアトム名でプロセス登録を提供します。 これは無駄です。アトムはガベージコレクターによって収集されず、一度作成されると、ノード全体の操作が完了するまで存続するため、必然的にすべてのメモリが枯渇し、必要に応じて一意の名前でプロセスを登録します。 さらに、異なる用語をアトムに変換し、このためのいくつかのルールを構成する必要があるため、このようなアプローチは不便です。さらに、プロセスは1つの名前でのみ登録できます。
erlang:register/2
関数を使用してアトム名の下にプロセスを登録することは、名前を変更してはならない少数の長期間有効なプロセスに対してのみ有効です;アナログは命令型プログラミング言語のグローバル変数です。
これらの制限を回避するために、次のスキームがよく使用されます。
- レジストラプロセスが起動し、etsテーブルを作成してその所有者になります。
- 登録が必要なプロセスを開始すると、登録の座標(アーラン用語)とその識別子を含むメッセージをレジストラに送信します。
- レジストラはこのマッピングをetsテーブルに書き込み、
erlang:monitor/2
によるプロセス監視を有効にします。 - 登録されたプロセスは、完了時に登録解除に関するメッセージを明示的に送信するか、このプロセスがクラッシュしたときにレジストラが
'DOWN'
メッセージを受信し、その後etsテーブルからレコードを削除します。
このスキームは非常に頻繁に使用され、ほとんどすべてのアプリケーションには独自の実装があり、独自の機能とバグがあります。 もちろん、このレジストラを何かユニークなものに置き換えたいという自然な欲求があります。 そして、この問題の解決策は、開発者のUlf Wigerと彼のgprocアプリケーション(https://github.com/esl/gproc)の形でもたらされました。
アプリケーションAPIは
github.com/esl/gproc/blob/master/doc/gproc.mdにあります。
ローカル登録
最も単純なケースを考えてみましょう-任意の用語でのプロセスのローカル(現在のノードでの)登録。
例のソースコードは、
github.com /
Zert /
gproc-tutorial.gitで入手できます。
gprocを介して登録するプロセスのコードは、
gpt_proc.erl
ファイルにあります。
gpt_sup.erl
は、このプロセスグループのスーパーバイザーコードが含まれています。
gpt_sup:start_worker/1
関数が呼び出されると、プロセスが開始され、関数に唯一の引数として渡される名前で登録されます。 この場合、これは数字です。
上記のコマンドを使用してノードを起動し、異なる識別子を使用して一連のプロセス起動を実行しました。
(gptnode @ localhost)1> [gpt_sup:start_worker(Id)|| Id <-リスト:seq(1,3)]。
(gpt_proc:29)プロセスの開始:1
(gpt_proc:29)プロセスの開始:2
(gpt_proc:29)プロセスの開始:3
[{ok、<0.61.0>}、{ok、<0.62.0>}、{ok、<0.63.0>}]
gproc:add_local_name(Name)
関数の呼び出し
gproc:add_local_name(Name)
は、名前
Name
で呼び出しているプロセスを登録します(この関数は、
gproc:reg({n,l,Name})
単なるラッパー
gproc:reg({n,l,Name})
。ここで、
n
は
name
、
l
は
local
です。 その後、
gproc:lookup_local_name(Name)
関数はプロセス識別子を返します。
次に、プロセスの開始を待機して、名前4で登録するようにプロセスの1つに指示しましょう。これを担当するコードは次のとおりです。
handle_info({await、Id}、
#state {id = MyId} = State)->
gproc:await({n、l、Id})、
?DBG( "MyId:〜p。〜NNewId:〜p。"、[MyId、Id])、
{noreply、State};
ここで、
gproc:await/1
関数は、次の形式の引数で呼び出されます:
{n, l, Id}
。 何らかの理由で、ラッパーはありませんが、まあです。
(gptnode @ localhost)2> gproc:lookup_local_name(1)! {待って、4}。
{待って、4}
識別子4でプロセスを開始すると、最初にメッセージが表示され、次に最初の待機プロセスからメッセージが表示されます。
(gptnode @ localhost)3> gpt_sup:start_worker(4)。
(gpt_proc:29)プロセスの開始:4
(gpt_proc:45)MyId:1。
NewId:4。
{OK、<0.66.0>}
stop
メッセージを受信するプロセスを停止しましょう。
handle_info(停止、状態)->
{停止、通常、状態};
そしてそれを止めます:
(gptnode @ localhost)4> gproc:lookup_local_name(1)! やめて
止まる
その後、プロセスはレジストラーデータベースから自動的に削除されます。
(gptnode @ localhost)5> gproc:lookup_local_name(1)。
未定義
グローバル登録
Erlangが広く分布していることはよく知られています。 これは、ノード間で透過的にメッセージを交換することを意味します。つまり、プロセス識別子を持っているため、どのノードにいるかを知らなくてもメッセージを送信できます。 gprocによるプロセスのローカル登録により、任意の用語を1つのアーランノード内のプロセス識別子にマッピングできますが、この用語を使用して他のノードで識別子値を取得することはできません。
クラスタ内のノードが他のノードからアクセスできるようにプロセスを登録できるようにするには、グローバル登録があります。 GProcは、gprocの呼び出し
gproc:add_global_name/1
を実装します。これにより、このアクションを実行できます。 例を考えてみましょう。
最初に、クラスターに結合された2つのノードを構築します。特定のテンプレートに従って構成ファイルを作成する機能があるため、
rebar
はこれに役立ちます。 クラスターを作成するときは、次の詳細を考慮する必要があります。
- ノードに同じCookie値を設定します
- それらに異なる名前を付けます。
- 各ノードで必要な
kernel
アプリケーションに適切なパラメーターを渡します - GProcを使用する場合、目的のノードロールをGProcに渡します。
最初の2つの項目は
files/vm.args
設定され
files/vm.args
:
##ノードの名前
-sname {{node}}
##分散アーランのCookie
-setcookie gptnode
ここで、
{{node}}
は、リリースの作成時に入力されるプレースホルダーです。
-setcookie
仮想マシン
-setcookie
は、このノードのCookie値を設定します;クラスターでは、すべてのノードが同じ値を持つ必要があります。
2番目の2つのポイントは、
files/app.config
設定され
files/app.config
。 プレースホルダーもここで使用されます。
%% GProc
{gproc、{{gproc_params}}}、
%%カーネル
{kernel、{{kernel_params}}}、
プレースホルダーを埋めるために、
reltool.config
ファイルで、前の2つのファイルをテンプレートとして処理する必要があることを示します。
{template、 "files / app.config"、 "etc / app.config"}、
{template、 "files / vm.args"、 "etc / vm.args"}
各ノードに1つずつ、2つの構成ファイルを作成します:
vars/dev1_vars.config
および
vars/dev2_vars.config
。 dev1_vars.configファイルには、次のプレースホルダー値が含まれます。
%% etc / app.config
{gproc_params、
「[
{gproc_dist、{['gpt1 @ localhost']、
[{workers、['gpt2 @ localhost']}]}}
] "}。
{kernel_params、
「[
{sync_nodes_mandatory、['gpt2 @ localhost']}、
{sync_nodes_timeout、15000}
] "}。
%% etc / vm.args
{node、 "gpt1 @ localhost"}。
dev2_vars.config
ファイルの
dev2_vars.config
、
sync_nodes_mandatory
および
node
パラメーターが交換されます。 それらをより詳細に分析しましょう。
gproc_dist
パラメーターはgprocアプリケーションを参照します;これは2つのリストのタプルです。 最初のリストはリーダー(マスター)になることができるノードであり、2番目のリストにはキーと値のタプルが含まれています。これまでのところ、クラスターの単純なメンバー(スレーブ)であるノードのリストを設定する1つのキー-
workers
のみが必要です。
カーネルアプリケーションには2つのパラメーターがあります。 最初の
sync_nodes_mandatory
は、クラスターに存在する必要があるノードのリストです。 2番目の
sync_nodes_timeout
は、前のリストのノードが表示されるまで各ノードが待機する時間(ミリ秒)です。 この間にノードが表示されなかった場合、ノードは停止します。 両手を開始する時間を確保するために、15秒の値にします。
node
の値は、仮想マシンの起動パラメーターに書き込まれます。これはその名前です。
ここで、Makefileから次のルールを使用して2つのリリースを作成します。
dev1 dev2:
mkdir -p dev
(cd rel && rebar generate target_dir = .. / dev / $ @ overlay_vars=vars/$@_vars.config)
dev/dev1
に移動し、2番目のターミナルウィンドウを起動(または画面に新しいウィンドウを作成
),
dev / dev2ディレクトリに移動し
.
.
./bin/gptnode console`。 最初のErlangシェルで利用可能なノードのリストを見てみましょう:
(gpt1 @ localhost)1>ノード()。
[gpt2 @ localhost]
2番目のノードが正常に起動し、クラスターに接続されていることがわかります。 長期間にわたって賢くならないようにするために、現在のシェルのプロセスを何らかの条件でグローバルに登録します。
(gpt1 @ localhost)2> gproc:add_global_name({shell、1})。
本当
別のウィンドウで、この用語のプロセスIDを要求してください。
(gpt2 @ localhost)2> gproc:lookup_global_name({shell、1})。
<3358.70.0>
ご覧のとおり、正常に。 このプロセスにメッセージを送信することにより、最初のノードでメッセージを受信できます。
(gpt2 @ localhost)3> gproc:lookup_global_name({shell、1})! {the、message}。
伝言
flush()
コマンドで最初のノードで読み取ります:
(gpt1 @ localhost)3> flush()。
シェルは{the、message}を得ました
わかった
おわりに
それだけです 鉄筋に関するドキュメントは非常に少なく、次の使用後に常に忘れられているため、私は自分のために記事を書き始めました。 その過程で、gprocの使用を開始しました。2回起きないように、すべてを1つの記事にまとめました。