Достижение обмена кодами в gen_server Эрланга
Я хочу использовать функцию горячей замены кода Эрланга на сервере gen_server, чтобы мне не пришлось его перезапускать. Как мне это сделать? Когда я искал, все, что я мог найти, была одна статья, в которой упоминалось, что мне нужно использовать gen_server:code_change
Перезвоните.
Однако я не смог найти документацию / примеры того, как это использовать. Любая помощь или ссылки на ресурсы приветствуются!
5 ответов
Как я уже упоминал, нормальным способом обновления является создание правильных файлов.appup и.relup, и пусть release_handler делает то, что нужно сделать. Однако вы можете вручную выполнить соответствующие шаги, как описано здесь. Извините за длинный ответ.
Следующий фиктивный 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" с [], переданным как "Extra" в 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
Вам не нужно использовать этот обратный вызов в gen_server
поведение. Он есть, если вы меняете внутреннее представление состояния через обновление кода.
Вам нужно только загрузить новый модуль и gen_server
запуск старой версии приведет к обновлению, поскольку он вызывает новый модуль. Просто у вас нет возможности изменить представление, если это необходимо.
Самый простой способ сделать это - заменить .beam
файл и запустить l(my_server_module).
в оболочке. Это обходит code_change
функция, и, следовательно, требует, чтобы представление состояния не изменилась.
Как уже упоминалось, правильный способ сделать это - создать новый выпуск со скриптами appup и relup. Этот новый выпуск затем устанавливается вместе с release_handler.
Если вы хотите сделать это правильно, что настоятельно рекомендуется, вам необходимо ознакомиться с использованием OTP Supervisor и приложений.
Вы могли бы сделать хуже, чем читать Руководство пользователя Принципов разработки OTP здесь:
http://www.erlang.org/doc/design_principles/users_guide.html
Если вы находитесь на rebar3, часть этой ручной обработки была автоматизирована (например, генерация appup и relup), вы можете найти больше информации здесь: http://lrascao.github.io/automatic-release-upgrades-in-erlang/