Замена горячего кода в erlang
Я работаю над своим первым настоящим проектом в erlang, однако этот код упрощен для краткости. Я хочу иметь возможность загружать более новую версию файла в мой проект удаленно, пока он работает. Я читал об использовании такого поведения, как gen_server
или gen_fsm
, у которого это бесплатно. Хотя это может привести к результату, я хочу использовать это, чтобы узнать, как это сделать, а не просто сделать это. Я прочитал документы о замена кода, а бит LYSE о Hot Code Любить, между прочим, но я не смог найти ничего, что работает на то, что я делаю, поэтому вот основная идея.
-module(reloading).
-export([loop/0]).
loop() ->
receive
upgrade ->
?MODULE:loop();
hello ->
io:format("This is a test~n"),
loop();
_ ->
loop()
end.
Я просто зацикливаюсь на том, что могу отправить сообщение upgrade
, и он загрузит более новую версию кода.
$ erl
Erlang R15B01 (erts-5.9.1) [source] [64-bit] [async-threads:0] [hipe] [kernel-poll:false]
Eshell V5.9.1 (abort with ^G)
1> c(reloading).
{ok,reloading}
2> Loop = spawn(reloading, loop, []).
<0.39.0>
3> Loop ! hello.
This is a test
hello
В этот момент я меняю 10 строк на io:format("I have changed this!~n"),
4> Loop ! upgrade.
upgrade
5> Loop ! hello.
This is a test
hello
Я ожидаю, что этот вызов hello
напечатать I have changed this!
not This is a test
. Я знаю, что могу просто позвонить c(reloading).
и выполнить эту работу так, как ожидалось, но я хочу отправить фактическое сообщение о проекте, а не вручную обновлять код. Итак, где мой разрыв? Что я делаю неправильно, что я должен делать, чтобы нагрузить этот код? Как упоминалось ранее, я ищу решение без OTP для образования.
Ответы
Ответ 1
Чтобы получить явный ответ, я публикую это.
Используя @rvirding
предложение об использовании модуля code, я изменил его так:
-module(reloading).
-export([loop/0]).
loop() ->
receive
upgrade ->
code:purge(?MODULE),
compile:file(?MODULE),
code:load_file(?MODULE),
?MODULE:loop();
hello ->
io:format("This is a test~n"),
loop();
_ ->
loop()
end.
Первый код: очистить старый ?MODULE
, затем скомпилировать: файл новый файл и наконец, code: load_file новый ?MODULE
.
Это работает, как я и предполагал.
$ erl
Erlang R15B01 (erts-5.9.1) [source] [64-bit] [async-threads:0] [hipe] [kernel-poll:false]
Eshell V5.9.1 (abort with ^G)
1> Loop = spawn(reloading, loop, []).
<0.34.0>
2> Loop ! hello.
This is a test
hello
Измените строку на io:format("I have changed this!~n"),
3> Loop ! upgrade.
upgrade
4> Loop ! hello.
I have changed this!
hello
Ответ 2
В то время как erlang может обрабатывать две версии модуля, а вызов функции с помощью mod:func(...)
будет всегда вызывать последнюю версию модуля (если функция экспортируется), вам все равно придется загружать новую версию модуля в систему Erlang, Вы не можете ожидать, что он автоматически обнаружит, что у вас где-то есть новая версия модуля, найдите его, скомпилируйте и загрузите.
N.B. компиляция и загрузка - это две разные вещи. Таким образом, c(mod).
компилирует и модуль, а l(mod).
просто загружает объектный код (.beam файл) уже скомпилированного модуля. Компилятор Erlang вызывается из модуля compile
, и он просто компилирует и генерирует файл .beam, а загрузка кода обрабатывается модулем code
.
Ответ 3
В дополнение к вышеизложенному я хотел бы заметить, что некоторые инструменты, которые автоматически перезагружают код для вас, существуют.
Вы должны взглянуть на sync или active.
Ответ 4
Скомпилируйте *.beam локально, затем отправьте его на свой сервер и перезагрузите его, как указано в man-страницах:
http://erlang.org/documentation/doc-1/reference_manual/code_loading.html#id86381
-module(m).
-export([loop/0]).
loop() ->
receive
code_switch ->
m:loop();
Msg ->
...
loop()
end.