Pergunta

Eu estou olhando para fazer uso do recurso de troca de código quente do Erlang em um gen_server, para que eu não tenho que reiniciá-lo. Como devo fazer isso? Quando eu procurei, tudo o que eu poderia encontrar era um artigo que mencionou que eu preciso fazer uso de callback gen_server:code_change.

No entanto, eu não poderia realmente encontrar qualquer documentação / exemplos sobre como usar isso. Qualquer ajuda ou links para recursos muito apreciada!

Foi útil?

Solução

Como já mencionei a maneira normal de atualização está criando a .appup adequada e arquivos .relup, e deixe release_handler fazer o que precisa ser feito. No entanto, você pode executar manualmente as etapas envolvidas, como descrito aqui. Desculpem a resposta longa.

Os seguintes implementos gen_server fictícios um contador. A versão antiga ( "0") simplesmente armazena um inteiro como Estado, enquanto a nova versão ( "1") armazena {tschak, Int} como o estado. Como eu disse, este é um exemplo fictício.

z.erl (idade):

-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 (novo):

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

Inicie o shell, e compilar o código antigo. Observe a gen_server é iniciado com rastreamento de depuração.

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

funciona como esperado:. Retornos do Int, e novo estado é Int + 1

Agora substitua z.erl com o novo, e execute os seguintes passos.

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

O que você fez: 5: compilou o novo código. 6: suspendeu o servidor. 7: purgado código antigo (apenas no caso). 8: carregado o novo código. 9: alteração de código invocada no processo de 'z' para o módulo 'z' a partir da versão "0" com [] passado como "extra" para code_change. 10:. Retomou o servidor

Agora, se você executar mais alguns testes, você pode ver, que o servidor trabalha com o novo formato de estado:

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

Outras dicas

Você não precisa usar esse retorno de chamada no comportamento gen_server. É lá, se você mudar a representação interna do estado através de uma atualização de código.

Você só precisa carregar o novo módulo eo gen_server executando a versão antiga irá atualizar, pois ele chama o novo módulo. É justo que você não tem a chance de mudar a representação se isso for necessário.

A maneira mais simples de fazer isso é substituir o arquivo .beam e l(my_server_module). executados no Shell. Isso ignora a função code_change, e, portanto, requer que a representação do estado não foi alterado.

Como já mencionado, a maneira correta de fazer isso é criar uma nova versão com scripts AppUp e relup. Esta nova versão é então instalado com release_handler .

Se você quer fazer isso da maneira certa, que é altamente recomendado, então você precisa ler sobre o uso de OTP Supervisores e Aplicativos.

Você poderia fazer pior do que ler o Guia do Usuário do OTP Princípios de Design aqui:

http://www.erlang.org/doc/design_principles/users_guide.html

Se você estiver em rebar3, algumas dessas processamento manual foi automatizado de distância, você pode encontrar mais informações aqui (ie AppUp e geração de relup.): http://lrascao.github.io/automatic-release-upgrades-in-erlang/

Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top