我想利用Erlang的热代码切换功能在gen_server上使用,这样我就不需要重新启动它。我应该怎么做?当我搜索时,我只找到一篇文章提到我需要使用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 (new):
-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是带有调试跟踪(debug trace)启动的。
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
文件并在shell中运行l(my_server_module).
,这将绕过code_change
函数,因此需要确保状态的表示未发生更改。http://www.erlang.org/doc/design_principles/users_guide.html
如果你正在使用rebar3,一些手动处理已经被自动化了(例如appup和relup生成),你可以在这里找到更多信息:http://lrascao.github.io/automatic-release-upgrades-in-erlang/
code:purge
呢? - spockwang