Подтвердить что ты не робот

Достижение обмена кодами в Erlang gen_server

Я ищу, чтобы использовать функцию обмена горячим кодом Erlang на gen_server, так что мне не нужно ее перезапускать. Как мне это сделать? Когда я искал, я мог найти только одну статью, в которой упоминалось, что мне нужно использовать обратный вызов gen_server:code_change.

Однако я не мог найти документацию/примеры того, как это использовать. Любая помощь или ссылки на ресурсы очень ценятся!

4b9b3361

Ответ 1

Как я уже упоминал, обычным способом обновления является создание правильных файлов .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

Ответ 2

Вам не нужно использовать этот обратный вызов в поведении gen_server. Он существует, если вы измените внутреннее представление состояния при обновлении кода.

Вам нужно только загрузить новый модуль, а gen_server запустить старую версию будет обновляться, так как он вызывает новый модуль. Просто вы не можете изменить представление, если это необходимо.

Ответ 3

Самый простой способ сделать это - заменить файл .beam и запустить l(my_server_module). в оболочке. Это обходит функцию code_change и поэтому требует, чтобы представление состояния не изменилось.

Как уже упоминалось, правильный способ сделать это - создать новый выпуск с помощью скриптов appup и relup. Затем этот новый выпуск устанавливается с release_handler.

Ответ 4

Если вы хотите сделать это правильно, что очень рекомендуется, вам необходимо прочитать об использовании Supervisors и приложений OTP.

Вы можете сделать хуже, чем прочитать Руководство пользователя принципов дизайна OTP здесь:

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

Ответ 5

Если вы на панели rebar3, часть этой ручной обработки была автоматизирована (т.е. создание приложения и повторного использования), вы можете найти более подробную информацию здесь: http://lrascao.github.io/automatic-release-upgrades-in-erlang/