RebarとGProcを使用する

鉄筋の使用



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関数を使用してアトム名の下にプロセスを登録することは、名前を変更してはならない少数の長期間有効なプロセスに対してのみ有効です;アナログは命令型プログラミング言語のグローバル変数です。
これらの制限を回避するために、次のスキームがよく使用されます。
  1. レジストラプロセスが起動し、etsテーブルを作成してその所有者になります。
  2. 登録が必要なプロセスを開始すると、登録の座標(アーラン用語)とその識別子を含むメッセージをレジストラに送信します。
  3. レジストラはこのマッピングをetsテーブルに書き込み、 erlang:monitor/2によるプロセス監視を有効にします。
  4. 登録されたプロセスは、完了時に登録解除に関するメッセージを明示的に送信するか、このプロセスがクラッシュしたときにレジストラが'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}) 。ここで、 nnamellocalです。 その後、 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はこれに役立ちます。 クラスターを作成するときは、次の詳細を考慮する必要があります。

最初の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.configsync_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つの記事にまとめました。

Source: https://habr.com/ru/post/J112681/


All Articles