erlang的熱更新

erlang做爲一個爲電信級別而出現的語言,熱更新是其最重要的特性之一函數

 熱代碼升級-Erlang容許程序代碼在運行系統中被修改。舊代碼能被逐步淘汰然後被新代碼替換。在此過渡期間,新舊代碼是共存的。測試

下面咱們以最典型的gen_server爲例子,講解一下這個BT的功能spa

 1 -module(tt13).
 2 -behaviour(gen_server).
 3 
 4 -export([test/0]).
 5 -export([start_link/0, stop/0, init/1, handle_call/3, handle_cast/2, handle_info/2, terminate/2, code_change/3]).
 6 
 7 -record(state, {cnt}).
 8 
 9 -define(SERVER, ?MODULE).
10 
11 %%--------------------------------------------------------------------
12 %% Function: start_link() -> {ok,Pid} | ignore | {error,Error}
13 %% Description: Starts the server
14 %%--------------------------------------------------------------------
15 start_link() ->
16     gen_server:start_link({local, ?MODULE}, ?MODULE, [], [{debug, [trace]}]).
17 
18 test() ->
19     gen_server:call(?SERVER, test). 
20 
21 stop() -> 
22     gen_server:cast(?SERVER, stop). 
23 
24 %%--------------------------------------------------------------------
25 %% Function: init(Args) -> {ok, State} |
26 %%                         {ok, State, Timeout} |
27 %%                         ignore               |
28 %%                         {stop, Reason}
29 %% Description: Initiates the server
30 %%--------------------------------------------------------------------
31 init(_) -> {ok, #state{cnt=1}}.
32 %%--------------------------------------------------------------------
33 %% Function: %% handle_call(Request, From, State) -> {reply, Reply, State} |
34 %%                                      {reply, Reply, State, Timeout} |
35 %%                                      {noreply, State} |
36 %%                                      {noreply, State, Timeout} |
37 %%                                      {stop, Reason, Reply, State} |
38 %%                                      {stop, Reason, State}
39 %% Description: Handling call messages
40 handle_call(test, _From, #state{cnt=Cnt} = State) ->
41     {reply, {ok, Cnt}, State#state{cnt=Cnt+1}};
42 
43 handle_call(stop, _From, State) ->
44     {stop, normal, ok, State};
45 
46 handle_call(_Unrec, _From, State) ->
47     {reply, {error, invalid_call}, State}.
48 
49 %%--------------------------------------------------------------------
50 %% Function: handle_cast(Msg, State) -> {noreply, State} |
51 %%                                      {noreply, State, Timeout} |
52 %%                                      {stop, Reason, State}
53 %% Description: Handling cast messages
54 %%--------------------------------------------------------------------
55 handle_cast(_Msg, State) ->
56     {noreply, State}.
57 
58 %%--------------------------------------------------------------------
59 %% Function: handle_info(Info, State) -> {noreply, State} |
60 %%                                       {noreply, State, Timeout} |
61 %%                                       {stop, Reason, State}
62 %% Description: Handling all non call/cast messages
63 %%--------------------------------------------------------------------
64 
65 handle_info(_Info, State) ->
66     {noreply, State}.
67 
68 %%--------------------------------------------------------------------
69 %% Function: terminate(Reason, State) -> void()
70 %% Description: This function is called by a gen_server when it is about to
71 %% terminate. It should be the opposite of Module:init/1 and do any necessary
72 %% cleaning up. When it returns, the gen_server terminates with Reason.
73 %% The return value is ignored.
74 %%--------------------------------------------------------------------
75 
76 terminate(_Reason, _State) ->
77     io:format("hello gen server: terminating~n").
78 
79 %%--------------------------------------------------------------------
80 %% Func: code_change(OldVsn, State, Extra) -> {ok, NewState}
81 %% Description: Convert process state when code is changed
82 %%--------------------------------------------------------------------
83 
84 code_change(_OldVsn, State, _Extra) ->
85     {ok, State}.
86 
87 %%====================================================================
88 %%other fun
89 %%====================================================================

 編譯運行結果debug

 1 1> c(tt13).
 2 {ok,tt13}
 3 2> tt13:start_link().
 4 {ok,<0.39.0>}
 5 3> tt13:test().
 6 *DBG* tt13 got call test from <0.32.0>
 7 *DBG* tt13 sent {ok,1} to <0.32.0>, new state {state,2}
 8 {ok,1}
 9 4> tt13:test().
10 *DBG* tt13 got call test from <0.32.0>
11 *DBG* tt13 sent {ok,2} to <0.32.0>, new state {state,3}
12 {ok,2}
13 5> tt13:test().
14 *DBG* tt13 got call test from <0.32.0>
15 *DBG* tt13 sent {ok,3} to <0.32.0>, new state {state,4}
16 {ok,3}

若是修改了函數,能夠直接運行code

 1 -module(tt13).
 2 -version("1.1").
 3 -behaviour(gen_server).
 4 
 5 %...........
 6 %...........省略若干行
 7 %................
 8 
 9 handle_call(test, _From, #state{cnt=Cnt} = State) ->
10     {reply, {ok, Cnt}, State#state{cnt=Cnt*2}};
11 
12 %...........
13 %...........省略若干行
14 %................

 能夠看到咱們修改了計數的方法,並且修改了版本號,而後咱們繼續運行orm

 1 6> c(tt13).                                    %編譯新的代碼
 2 {ok,tt13}
 3 7> tt13:test().      
 4 *DBG* tt13 got call test from <0.32.0>
 5 *DBG* tt13 sent {ok,4} to <0.32.0>, new state {state,8}
 6 {ok,4}
 7 8> tt13:test().
 8 *DBG* tt13 got call test from <0.32.0>
 9 *DBG* tt13 sent {ok,8} to <0.32.0>, new state {state,16}
10 {ok,8}
11 9> tt13:test().
12 *DBG* tt13 got call test from <0.32.0>
13 *DBG* tt13 sent {ok,16} to <0.32.0>, new state {state,32}
14 {ok,16}

 能夠看到代碼就直接替換了,注意編譯的時候會用新的代碼替換下一次運行的結果,正在運行仍是old code,因此不要編譯屢次(通常在測試環境先進行熱更新測試)。server

   若是要替換init/1裏面的代碼?這個方法確定是不行的,由於init/1代碼只運行一次,好比我要修改state結構體,那要怎麼弄呢blog

 1 -module(tt13).
 2 -version("2.0").
 3 -behaviour(gen_server).
 4 
 5 -record(state, {testcheck, cnt}).
 6 %...........
 7 %...........省略若干行
 8 %................
 9 
10 init(_) -> {ok, #state{testcheck='chk', cnt=1}}.
11 %%--------------------------------------------------------------------
12 %% Function: %% handle_call(Request, From, State) -> {reply, Reply, State} |
13 %%                                      {reply, Reply, State, Timeout} |
14 %%                                      {noreply, State} |
15 %%                                      {noreply, State, Timeout} |
16 %%                                      {stop, Reason, Reply, State} |
17 %%                                      {stop, Reason, State}
18 %% Description: Handling call messages
19 handle_call(test, _From, #state{cnt=Cnt} = State) ->
20     {reply, {ok, Cnt}, State#state{cnt=Cnt+1}};
21 
22 %...........
23 %...........省略若干行
24 %................
25 
26 code_change("1.1", {state, Cnt}, _Extra) -> 
27     {ok, {state, chk, Cnt}};
28 
29 code_change(_OldVsn, State, _Extra) ->
30     {ok, State}.
31 
32 %...........
33 %...........省略若干行
34 %................

 

  咱們修改了state結構體,修改了init/1函數,並且重寫了code_change/3,下面咱們運行以下ip

 1 10> compile:file(tt13).          /*編譯代碼
 2 {ok,tt13}
 3 11> sys:suspend(tt13).           /*暫停服務
 4 ok
 5 12> code:purge(tt13).           /*清除舊的代碼,若是有運行的化
 6 false
 7 13> code:load_file(tt13).        /*加載新的代碼
 8 {module,tt13}
 9 14> sys:change_code(tt13,tt13,"1.1",[]).    /*修改state狀態
10 ok
11 15> sys:resume(tt13).                /*恢復服務
12 ok
13 16> tt13:test().
14 *DBG* tt13 got call test from <0.32.0>
15 *DBG* tt13 sent {ok,32} to <0.32.0>, new state {state,chk,64}
16 {ok,32}
17 17> tt13:test().
18 *DBG* tt13 got call test from <0.32.0>
19 *DBG* tt13 sent {ok,64} to <0.32.0>, new state {state,chk,128}
20 {ok,64}
21 18> tt13:test().
22 *DBG* tt13 got call test from <0.32.0>
23 *DBG* tt13 sent {ok,128} to <0.32.0>, new state {state,chk,256}
24 {ok,128}

 

  整個替換過程是10-15步,注意這個過程當中的tt13服務是hang住的,若是這時候使用服務,會出現timeout,因此通常這5步都是同時執行。it

       後面能夠看到state狀態已經改變

相關文章
相關標籤/搜索