programacion - Lograr el intercambio de código en el gen_server de Erlang
erlang telecomunicaciones (5)
Como ya mencioné, la forma normal de actualización es crear los archivos apropiados .appup y .relup, y dejar que release_handler haga lo que se necesita hacer. Sin embargo, puede ejecutar manualmente los pasos involucrados, como se describe aquí. Lo siento por la respuesta larga.
El siguiente dummy gen_server implementa un contador. La versión anterior ("0") simplemente almacena un número entero como estado, mientras que la nueva versión ("1") almacena {tschak, Int} como estado. Como dije, este es un ejemplo ficticio.
z.erl (antiguo):
-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 (nuevo):
-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}}.
Inicia el shell y compila el código antiguo. Observe que gen_server se inicia con la traza de depuración.
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 se esperaba: devuelve el Int, y el nuevo estado es Int + 1.
Ahora reemplace z.erl con el nuevo y ejecute los siguientes pasos.
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
Lo que acabas de hacer: 5: compiló el nuevo código. 6: suspendió el servidor. 7: código antiguo purgado (por si acaso). 8: cargado el nuevo código. 9: cambio de código invocado en el proceso ''z'' para el módulo ''z'' de la versión "0" con [] pasado como "Extra" a code_change. 10: reanudó el servidor.
Ahora, si ejecuta algunas pruebas más, puede ver que el servidor funciona con el nuevo 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
Estoy buscando hacer uso de la función de intercambio de código activo de Erlang en un servidor gen, para que no tenga que reiniciarlo. ¿Cómo debo hacer eso? Cuando busqué, todo lo que pude encontrar fue un artículo que mencionó que necesito hacer uso de gen_server:code_change
callback.
Sin embargo, realmente no pude encontrar ninguna documentación / ejemplos sobre cómo usar esto. Cualquier ayuda o enlaces a recursos muy apreciados!
La forma más sencilla de hacerlo es reemplazar el archivo .beam
y ejecutar l(my_server_module).
en la concha Esto evita la función de cambio de code_change
y, por lo tanto, requiere que la representación del estado no haya cambiado.
Como ya se mencionó, la forma correcta de hacerlo es crear una nueva versión con scripts appup y relup. Esta nueva versión se instala con release_handler .
No es necesario utilizar esa devolución de llamada en el comportamiento gen_server
. Está ahí si cambia la representación interna del estado a través de una actualización de código.
Solo necesita cargar el nuevo módulo y el gen_server
ejecuta la versión anterior se actualizará, ya que llama al nuevo módulo. Es solo que no tienes la oportunidad de cambiar la representación si es necesario.
Si desea hacerlo de la manera correcta, lo cual es altamente recomendable, entonces debe leer sobre el uso de Supervisores y Aplicaciones OTP.
Podría hacerlo peor que leer la Guía del usuario de los Principios de diseño de OTP aquí:
http://www.erlang.org/doc/design_principles/users_guide.html
Si está en rebar3, parte del procesamiento manual se ha automatizado (es decir, generación de aplicaciones y relup), puede encontrar más información aquí: http://lrascao.github.io/automatic-release-upgrades-in-erlang/