zoukankan      html  css  js  c++  java
  • erlang的热更新

    erlang作为一个为电信级别而出现的语言,热更新是其最重要的特性之一

     热代码升级-Erlang允许程序代码在运行系统中被修改。旧代码能被逐步淘汰而后被新代码替换。在此过渡期间,新旧代码是共存的。

    下面我们以最典型的gen_server为例子,讲解一下这个BT的功能

     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 %%====================================================================

     编译运行结果

     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}

    如果修改了函数,可以直接运行

     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 %................

     可以看到我们修改了计数的方法,而且修改了版本号,然后我们继续运行

     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,所以不要编译多次(一般在测试环境先进行热更新测试)。

       如果要替换init/1里面的代码?这个方法肯定是不行的,因为init/1代码只运行一次,比如我要修改state结构体,那要怎么弄呢

     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,下面我们运行如下

     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步都是同时执行。

           后面可以看到state状态已经改变

  • 相关阅读:
    UVa-1218
    Uva-1220
    UVa-10003
    UVa-1625
    UVa-11584
    UVa-12563
    UVa-12166 Equilibrium Mobile
    加油
    UVa-10129
    不再刷“水题”!
  • 原文地址:https://www.cnblogs.com/tudou008/p/9473639.html
Copyright © 2011-2022 走看看