再起動する必要がないように、gen_server で Erlang のホット コード スワップ機能を利用したいと考えています。どうすればいいですか?検索したところ、コールバックを利用する必要があると述べた記事が 1 つしか見つかりませんでしたgen_server:code_change
。
ただし、これを使用する方法に関するドキュメント/例は実際には見つかりませんでした。ヘルプやリソースへのリンクは大歓迎です!
再起動する必要がないように、gen_server で Erlang のホット コード スワップ機能を利用したいと考えています。どうすればいいですか?検索したところ、コールバックを利用する必要があると述べた記事が 1 つしか見つかりませんでしたgen_server:code_change
。
ただし、これを使用する方法に関するドキュメント/例は実際には見つかりませんでした。ヘルプやリソースへのリンクは大歓迎です!
すでに述べたように、アップグレードの通常の方法は、適切な .appup および .relup ファイルを作成し、必要なことを release_handler に任せることです。ただし、ここで説明するように、関連する手順を手動で実行できます。長い回答で申し訳ありません。
次のダミーの gen_server はカウンターを実装します。古いバージョン ("0") は単に整数を状態として保存しますが、新しいバージョン ("1") は {tschak, Int} を状態として保存します。私が言ったように、これはダミーの例です。
z.erl (旧):
-module(z).
-version("0").
-export([start_link/0, boing/0]).
-behavior(gen_server).
-export([init/1, handle_call/3, handle_cast/2, handle_info/2, terminate/2, code_change/3]).
start_link() -> gen_server:start_link({local, ?MODULE}, ?MODULE, [], [{debug, [trace]}]).
boing() -> gen_server:call(?MODULE, boom).
init([]) -> {ok, 0}.
handle_call(boom, _From, Num) -> {reply, Num, Num+1};
handle_call(_Call, _From, State) -> {noreply, State}.
handle_cast(_Cast, State) -> {noreply, State}.
handle_info(_Info, State) -> {noreply, State}.
terminate(_Reason, _State) -> ok.
code_change(_OldVsn, State, _Extra) -> {ok, State}.
z.erl (新規):
-module(z).
-version("1").
-export([start_link/0, boing/0]).
-behavior(gen_server).
-export([init/1, handle_call/3, handle_cast/2, handle_info/2, terminate/2, code_change/3]).
start_link() -> gen_server:start_link({local, ?MODULE}, ?MODULE, [], [{debug, [trace]}]).
boing() -> gen_server:call(?MODULE, boom).
init([]) -> {ok, {tschak, 0}}.
handle_call(boom, _From, {tschak, Num}) -> {reply, Num, {tschak, Num+1}};
handle_call(_Call, _From, State) -> {noreply, State}.
handle_cast(_Cast, State) -> {noreply, State}.
handle_info(_Info, State) -> {noreply, State}.
terminate(_Reason, _State) -> ok.
code_change("0", Num, _Extra) -> {ok, {tschak, Num}}.
シェルを起動し、古いコードをコンパイルします。gen_server がデバッグ トレースで開始されていることに注意してください。
1> c(z).
{ok,z}
2> z:start_link().
{ok,<0.38.0>}
3> z:boing().
*DBG* z got call boom from <0.31.0>
*DBG* z sent 0 to <0.31.0>, new state 1
0
4> z:boing().
*DBG* z got call boom from <0.31.0>
*DBG* z sent 1 to <0.31.0>, new state 2
1
期待どおりに動作します: Int を返し、新しい状態は Int+1 です。
z.erl を新しいものに置き換えて、次の手順を実行します。
5> compile:file(z).
{ok,z}
6> sys:suspend(z).
ok
7> code:purge(z).
false
8> code:load_file(z).
{module,z}
9> sys:change_code(z,z,"0",[]).
ok
10> sys:resume(z).
ok
5: 新しいコードをコンパイルしました。6: サーバーを一時停止しました。7: 古いコードを削除しました (念のため)。8: 新しいコードをロードしました。9: モジュール 'z' のプロセス 'z' のコード変更をバージョン "0" から呼び出し、[] を "Extra" として code_change に渡しました。10: サーバーを再開しました。
さらにいくつかのテストを実行すると、サーバーが新しい状態形式で動作することがわかります。
11> z:boing().
*DBG* z got call boom from <0.31.0>
*DBG* z sent 2 to <0.31.0>, new state {tschak,3}
2
12> z:boing().
*DBG* z got call boom from <0.31.0>
*DBG* z sent 3 to <0.31.0>, new state {tschak,4}
3
動作でそのコールバックを使用する必要はありませんgen_server
。コードのアップグレード全体で状態の内部表現を変更すると、そこにあります。
新しいモジュールをロードするだけでgen_server
、古いバージョンを実行すると新しいモジュールが呼び出されるため、アップグレードされます。必要に応じて表現を変更する機会がないというだけです。
これを行う最も簡単な方法は、.beam
ファイルを置き換えl(my_server_module).
てシェルで実行することです。code_change
これは関数をバイパスするため、状態の表現が変更されていないことが必要です。
既に述べたように、これを行う適切な方法は、appup および relup スクリプトを使用して新しいリリースを作成することです。この新しいリリースは、release_handlerでインストールされます。
強く推奨される正しい方法でそれを行いたい場合は、OTP スーパーバイザーとアプリケーションの使用について読む必要があります。
OTP Design Principles User's Guide をここで読むよりも悪いことをすることができます:
http://www.erlang.org/doc/design_principles/users_guide.html
rebar3 を使用している場合、この手動処理の一部が自動化されています (つまり、appup と relup の生成)。詳細については、http://lrascao.github.io/automatic-release-upgrades-in- を参照してください。アーラン/