我正在尋找在gen_server上使用Erlang的熱代碼交換功能,以便我不必重新啓動它。我應該怎麼做?當我搜索時,我能找到的只有一篇文章提到我需要使用gen_server:code_change
回調。在Erlang的gen_server中實現代碼交換
但是,我無法真正找到任何關於如何使用它的文檔/示例。任何幫助或資源的鏈接非常感謝!
我正在尋找在gen_server上使用Erlang的熱代碼交換功能,以便我不必重新啓動它。我應該怎麼做?當我搜索時,我能找到的只有一篇文章提到我需要使用gen_server:code_change
回調。在Erlang的gen_server中實現代碼交換
但是,我無法真正找到任何關於如何使用它的文檔/示例。任何幫助或資源的鏈接非常感謝!
正如我已經提到的,正常的升級方式是創建正確的.appup和.relup文件,並讓release_handler完成需要完成的任務。但是,您可以手動執行涉及的步驟,如此處所述。很抱歉,很長的答案。
下面的dummy 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」以[]作爲「額外」傳遞給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
就會升級,因爲它會調用新模塊。只是如果有必要,你沒有機會改變表示。
當你的意思是「加載新模塊」,你的意思是,我只是重新編譯使用genserver的模塊,並且正在運行的服務器自動升級? – jeffreyveon 2009-12-03 16:02:09
這就是如果你從Erlang shell(比如用c())編譯它會發生什麼。否則使用代碼:load_file/2或代碼:load_binary/2來獲得類似的效果。 – 2009-12-03 16:58:12
如果沒有使用正確的appup和relup腳本,我不認爲這有效...... – Zed 2009-12-03 17:55:08
如果您想以正確的方式進行操作,強烈建議,那麼您需要閱讀使用OTP主管和應用程序。
你可以做,而不是在這裏閱讀OTP設計原則用戶指南雪上加霜:
http://www.erlang.org/doc/design_principles/users_guide.html
謝謝,我讀了不同的OTP行爲,我只是找不到任何與此相關的信息。 – jeffreyveon 2009-12-03 16:03:44
做到這一點最簡單的方法是更換.beam
文件,並在shell中運行l(my_server_module).
。這繞過了code_change
函數,因此要求狀態的表示沒有改變。
正如已經提到的,正確的方法是使用appup和relup腳本創建新版本。這個新版本隨後與release_handler一起安裝。
如果你在rebar3,一些本手冊的處理已經自動化掉(即appup和relup代),你可以在這裏找到更多的信息:http://lrascao.github.io/automatic-release-upgrades-in-erlang/
在z.erl的版本1中,init應該返回{ok,{tschak,0}}作爲初始狀態。 – jmah 2010-01-24 03:28:02
哇,謝謝!固定。 – Zed 2010-01-24 07:42:16
爲什麼要麻煩調用'code:purge'如果虛擬機在加載後使用新版本? – spockwang 2013-11-17 08:01:21