EVM 如何在进程中保存代码版本以及 OTP 在热重载方面做了什么?
how does EVM hold code version in a process and what's the OTP do in term of hot reload?
我正在探索 Elixir/Erlang 热重载并尝试了解 Erlang 热重载的工作原理。
一些post give a glance of hot swap and here, from elixir,给出了热插拔的关键步骤。
此外,我尝试使用著名的 tcp 池库 Erlang ranch 来了解热交换如何在开发和部署环境中保持 tcp 连接。代码是at github([=34=里面有一些中文,随便用mix run
或者iex -S mix
和telnet localhost 8000
测试一下)。
热重载的一个非常有影响的事情是当持有代码的进程被删除时,进程将被杀死。在这个阶段,我应该给它一个恢复策略或者确保代码在做热时不能被删除 swap.I 认为一个好的做法是将逻辑代码删除到另一个文件,该文件与套接字连接循环代码不同。
我比较困惑的是,EVM是如何识别进程中的代码版本,并在更新新代码时去除旧版本的?
我经常听说 OTP 有助于热重载?
这个 document 描述了升级的步骤,它如何处理 运行 环境中的热重载?
谢谢。
下一个模块是超级基础服务器,用于说明没有 OTP 机制的代码更改。答案仅针对 erlang 代码。
-module (modtest).
-export ([init/0,loop/1]).
init() ->
spawn(?MODULE, loop, [version()]).
loop(Version) ->
receive
reload ->
io:format("external call state is ~p, current version is ~p~n",[Version,version()]),
?MODULE:loop(version());
stop ->
io:format("stopped~n");
Message ->
io:format("receive message ~p~n---> local call state is ~p, current version is ~p~n",[Message,Version,version()]),
loop(version())
after 10000 ->
io:format("timeout, state is ~p, current version is ~p~n",[Version,version()]),
loop(version())
end.
version() -> version1.
首先试用版本1中的模块
1> c(modtest).
{ok,modtest}
2> P = modtest:init().
<0.66.0>
timeout, state is version1, current version is version1
3> P! message1.
receive message message1
---> local call state is version1, current version is version1
message1
timeout, state is version1, current version is version1
4> P ! reload.
external call state is version1, current version is version1
reload
timeout, state is version1, current version is version1
接下来,进行巨大的进化
version() -> version2.
在 VM 外部编译模块并返回到 运行 应用程序
5> % compile outside version 2
timeout, state is version1, current version is version1
5> P! message1.
receive message message1
---> local call state is version1, current version is version1
message1
6> P ! reload.
external call state is version1, current version is version1
reload
7> P! message1.
receive message message1
---> local call state is version1, current version is version1
message1
timeout, state is version1, current version is version1
没有任何反应,模块没有自动加载,让我们在VM中加载模块
8> % load new version
timeout, state is version1, current version is version1
8> l(modtest).
{module,modtest}
9> P! message1.
receive message message1
---> local call state is version1, current version is version1
message1
10> P ! reload.
external call state is version1, current version is version1
reload
11> P! message1.
receive message message1
---> local call state is version1, current version is version2
message1
12> P! message1.
receive message message1
---> local call state is version2, current version is version2
message1
timeout, state is version2, current version is version2
13> P! stop.
stopped
stop
14>
很好,新代码已在模块中首次 "fully qualified" 调用后更新,
不幸的是,您无法控制何时考虑新代码。在示例中,即使
有一个重新加载功能,新代码在下一个循环中使用,如果需要对状态数据进行任何修改,则为时已晚。
下一个代码使用中间完全限定调用以允许修改状态数据。
-module (modtest).
-export ([init/0,loop/1,code_change/1]).
init() ->
spawn(?MODULE, loop, [version()]).
loop(Version) ->
receive
reload ->
NewVersion = ?MODULE:code_change(Version),
io:format("external call state is ~p, current version is ~p~n",[Version,NewVersion]),
?MODULE:loop(NewVersion);
stop ->
io:format("stopped~n");
Message ->
io:format("receive message ~p~n---> local call state is ~p, current version is ~p~n",[Message,Version,version()]),
loop(version())
after 10000 ->
io:format("timeout, state is ~p, current version is ~p~n",[Version,version()]),
loop(version())
end.
version() -> version3.
code_change(Version) ->
io:format("it is possible here to do any action on the state: ~p before the code change is completed~n",[Version]),
% It is possible to have different adaptation depending on the current version
version().
在 VM 中检查这个新版本
1> c(modtest).
{ok,modtest}
2> P = modtest:init().
<0.66.0>
3> P ! message.
receive message message
---> local call state is version3, current version is version3
message
4> P ! message.
receive message message
---> local call state is version3, current version is version3
message
5> P ! reload.
it is possible here to do any action on the state: version3 before the code change is completed
reload
external call state is version3, current version is version3
6> P ! reload.
it is possible here to do any action on the state: version3 before the code change is completed
reload
external call state is version3, current version is version3
timeout, state is version3, current version is version3
7> % new version
做一个新版本
...
version() -> version4.
...
然后返回虚拟机
7> c(modtest).
{ok,modtest}
timeout, state is version3, current version is version3
8> P ! message.
receive message message
---> local call state is version3, current version is version3
message
9> P ! message.
receive message message
---> local call state is version3, current version is version3
message
10> P ! reload.
it is possible here to do any action on the state: version3 before the code change is completed
reload
external call state is version3, current version is version4
11> P ! message.
receive message message
---> local call state is version4, current version is version4
message
12> P ! stop.
stopped
stop
13>
很好,按预期工作。但是仍然有一个很大的限制,"server" 不能使用任何其他完全限定的调用,否则,没有任何保证
加载新代码后将立即调用函数 code_change。
这是 OTP 在版本升级或降级中带来的行为(参见 release_handling)。
我正在探索 Elixir/Erlang 热重载并尝试了解 Erlang 热重载的工作原理。
一些post
此外,我尝试使用著名的 tcp 池库 Erlang ranch 来了解热交换如何在开发和部署环境中保持 tcp 连接。代码是at github([=34=里面有一些中文,随便用mix run
或者iex -S mix
和telnet localhost 8000
测试一下)。
热重载的一个非常有影响的事情是当持有代码的进程被删除时,进程将被杀死。在这个阶段,我应该给它一个恢复策略或者确保代码在做热时不能被删除 swap.I 认为一个好的做法是将逻辑代码删除到另一个文件,该文件与套接字连接循环代码不同。
我比较困惑的是,EVM是如何识别进程中的代码版本,并在更新新代码时去除旧版本的?
我经常听说 OTP 有助于热重载?
这个 document 描述了升级的步骤,它如何处理 运行 环境中的热重载?
谢谢。
下一个模块是超级基础服务器,用于说明没有 OTP 机制的代码更改。答案仅针对 erlang 代码。
-module (modtest).
-export ([init/0,loop/1]).
init() ->
spawn(?MODULE, loop, [version()]).
loop(Version) ->
receive
reload ->
io:format("external call state is ~p, current version is ~p~n",[Version,version()]),
?MODULE:loop(version());
stop ->
io:format("stopped~n");
Message ->
io:format("receive message ~p~n---> local call state is ~p, current version is ~p~n",[Message,Version,version()]),
loop(version())
after 10000 ->
io:format("timeout, state is ~p, current version is ~p~n",[Version,version()]),
loop(version())
end.
version() -> version1.
首先试用版本1中的模块
1> c(modtest).
{ok,modtest}
2> P = modtest:init().
<0.66.0>
timeout, state is version1, current version is version1
3> P! message1.
receive message message1
---> local call state is version1, current version is version1
message1
timeout, state is version1, current version is version1
4> P ! reload.
external call state is version1, current version is version1
reload
timeout, state is version1, current version is version1
接下来,进行巨大的进化
version() -> version2.
在 VM 外部编译模块并返回到 运行 应用程序
5> % compile outside version 2
timeout, state is version1, current version is version1
5> P! message1.
receive message message1
---> local call state is version1, current version is version1
message1
6> P ! reload.
external call state is version1, current version is version1
reload
7> P! message1.
receive message message1
---> local call state is version1, current version is version1
message1
timeout, state is version1, current version is version1
没有任何反应,模块没有自动加载,让我们在VM中加载模块
8> % load new version
timeout, state is version1, current version is version1
8> l(modtest).
{module,modtest}
9> P! message1.
receive message message1
---> local call state is version1, current version is version1
message1
10> P ! reload.
external call state is version1, current version is version1
reload
11> P! message1.
receive message message1
---> local call state is version1, current version is version2
message1
12> P! message1.
receive message message1
---> local call state is version2, current version is version2
message1
timeout, state is version2, current version is version2
13> P! stop.
stopped
stop
14>
很好,新代码已在模块中首次 "fully qualified" 调用后更新, 不幸的是,您无法控制何时考虑新代码。在示例中,即使 有一个重新加载功能,新代码在下一个循环中使用,如果需要对状态数据进行任何修改,则为时已晚。 下一个代码使用中间完全限定调用以允许修改状态数据。
-module (modtest).
-export ([init/0,loop/1,code_change/1]).
init() ->
spawn(?MODULE, loop, [version()]).
loop(Version) ->
receive
reload ->
NewVersion = ?MODULE:code_change(Version),
io:format("external call state is ~p, current version is ~p~n",[Version,NewVersion]),
?MODULE:loop(NewVersion);
stop ->
io:format("stopped~n");
Message ->
io:format("receive message ~p~n---> local call state is ~p, current version is ~p~n",[Message,Version,version()]),
loop(version())
after 10000 ->
io:format("timeout, state is ~p, current version is ~p~n",[Version,version()]),
loop(version())
end.
version() -> version3.
code_change(Version) ->
io:format("it is possible here to do any action on the state: ~p before the code change is completed~n",[Version]),
% It is possible to have different adaptation depending on the current version
version().
在 VM 中检查这个新版本
1> c(modtest).
{ok,modtest}
2> P = modtest:init().
<0.66.0>
3> P ! message.
receive message message
---> local call state is version3, current version is version3
message
4> P ! message.
receive message message
---> local call state is version3, current version is version3
message
5> P ! reload.
it is possible here to do any action on the state: version3 before the code change is completed
reload
external call state is version3, current version is version3
6> P ! reload.
it is possible here to do any action on the state: version3 before the code change is completed
reload
external call state is version3, current version is version3
timeout, state is version3, current version is version3
7> % new version
做一个新版本
...
version() -> version4.
...
然后返回虚拟机
7> c(modtest).
{ok,modtest}
timeout, state is version3, current version is version3
8> P ! message.
receive message message
---> local call state is version3, current version is version3
message
9> P ! message.
receive message message
---> local call state is version3, current version is version3
message
10> P ! reload.
it is possible here to do any action on the state: version3 before the code change is completed
reload
external call state is version3, current version is version4
11> P ! message.
receive message message
---> local call state is version4, current version is version4
message
12> P ! stop.
stopped
stop
13>
很好,按预期工作。但是仍然有一个很大的限制,"server" 不能使用任何其他完全限定的调用,否则,没有任何保证 加载新代码后将立即调用函数 code_change。
这是 OTP 在版本升级或降级中带来的行为(参见 release_handling)。