在Erlang的gen_server中实现代码交换

ibrsph3r  于 2022-12-08  发布在  Erlang
关注(0)|答案(5)|浏览(167)

我想在gen_server上使用Erlang的热代码交换功能,这样我就不用重新启动它了。我该怎么做呢?当我搜索时,我只能找到一篇文章,其中提到我需要使用gen_server:code_change回调。
然而,我真的找不到任何关于如何使用这个的文档/示例。任何帮助或资源链接都非常感谢!

jgovgodb

jgovgodb1#

正如我已经提到的,升级的正常方式是创建适当的.appup和. reup文件,并让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}}.

启动shell,编译旧代码。注意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
pcrecxhr

pcrecxhr2#

你不需要在gen_server行为中使用这个回调函数,只要你在代码升级中改变状态的内部表示就可以了。
你只需要加载新的模块,运行旧版本的gen_server就会升级,因为它调用了新的模块。只是如果有必要的话,你没有机会改变它的表示。

wqnecbli

wqnecbli3#

最简单的方法是替换.beam文件并在shell中运行l(my_server_module).,这样就绕过了code_change函数,因此要求状态的表示没有改变。
如前所述,正确的方法是用appup和reup脚本创建一个新的发行版,然后用release_handler安装这个新的发行版。

sqserrrh

sqserrrh4#

If you want to do it the right way, which is highly recommended, then you need to read up on the use of OTP Supervisors and Applications.
You could do worse than to read the OTP Design Principles User's Guide here :
http://www.erlang.org/doc/design_principles/users_guide.html

jaxagkaj

jaxagkaj5#

如果你在rebar3上,一些手动处理已经自动化了(例如appup和reup生成),你可以在这里找到更多信息:http://lrascao.github.io/automatic-release-upgrades-in-erlang/

相关问题