我可以在不停止 运行 应用程序的情况下重新加载单个更改的 Erlang 模块吗?

Can I reload single changed Erlang module without stopping running application?

我的 Erlang 应用程序需要很长时间才能启动,甚至更长时间才能完全重新编译。我做了一些我想尽快测试的小改动。我想在不停止应用程序的情况下编译和加载我的更改。如何从启动应用程序后进入的 Erlang 控制台执行此操作?我的源代码位于 ./src 目录中,光束文件被编译到 ./ebin 目录中,我想对更改后的文件执行相同的操作。 我的应用程序以

开始
erl -pa ebin deps/*/ebin

像往常一样编译模块,然后将其输入 erlang shell:

l(my_module).

这将从新的 my_module.beam 文件加载代码并替换 my_module 模块。

旧版本可能不会完全消失 - 有关详细信息,请参阅 Erlang 参考手册中的 the section on Code Reloading

您可以通过以下方式完成:

  1. 正在加载新版本的模块,例如c(module_name)

  2. 对新模块中的函数进行完全限定调用。 “完全合格”是指:

     module_name:function_name()
    

    模块仅在进行完全限定函数调用的过程中更新。

这是一个例子:

a.erl:

-module(a).
-compile(export_all).

start() -> 
    %% Long startup time:
    timer:sleep(1000), 

    spawn(fun() -> loop() end).

loop() ->
    receive
        {a, Msg} -> 
            show1(Msg),
            loop();
        {b, Msg} -> 
            show2(Msg),
            loop();
        update_code -> a:loop()
    end.

show1(Arg) ->
    %%io:format("Hello ~w!~n", [Arg]).  %original code
    io:format("XXX ~w!~n", [Arg]).  %new code

show2(Arg) ->
    %%io:format("Goodbye ~w!~n", [Arg]).  %original code      
    io:format("YYY ~w!~n", [Arg]).  %new code

在shell:

1> c(a).           
a.erl:2: Warning: export_all flag enabled - all functions will be exported
{ok,a}

2> Pid = a:start().
<0.72.0>

3> a:show1(jane).  %%Executed by shell process.
Hello jane!
ok

4> Pid ! {a, jane}. %% show1/1 executed in spawned process.
Hello jane!
{a,jane}

5> c(a).           
a.erl:2: Warning: export_all flag enabled - all functions will be exported
{ok,a}

6> a:show1(jane).  %%Module updated in shell process.
XXX jane!
ok

7> Pid ! {a, jane}.
Hello jane!     <---- STILL EXECUTING OLD MODULE
{a,jane}

8> Pid ! update_code.  %% Make fully qualified function call in spawned process.
update_code

9> Pid ! {a, jane}.  %% The whole module has been replaced by new code.
XXX jane!
{a,jane}

10> Pid ! {b, jane}.   
YYY jane!
{b,jane}

11> 

只是为了补充其他答案:

当您进行发布时,您可能有两个版本的应用程序并且您可能想要升级。比如说,您首先加载了 lib/foo-1.0.0/ebin/foo_app.beam。然后你通过你喜欢的方法安装 foo-2.0.0,我使用 target_system:install("foo", "/tmp/erl-target")。现在我有 /tmp/erl-target/lib/foo-2.0.0/.

我用code:which(foo_app)看加载了哪一个。要加载它,在这种情况下,我将首先启动 Eshell:

$ /tmp/erl-target/bin/erl -boot /tmp/erl-target/releases/1/start

之后我输入:

1> application:ensure_all_started(foo).
{ok,[...,foo]}

要查看当前加载的是哪一个,您键入(在同一个 Eshell 中):

2> code:which(foo_app).                                  
"/tmp/erl-target/lib/foo-1.0.0/ebin/foo_app.beam"

您将需要 /tmp/erl-target/lib/foo-2.0.0/ebin/ 中的 foo.appup 文件,其中包含以下内容:

{"2.0.0",
 [{"1.0.0",[{load_module,foo_app}]}],
 [{"1.0.0",[{load_module,foo_app}]}]}.

要升级,您输入:

3> release_handler:upgrade_app(foo, 'lib/foo-2.0.0').
{ok,[]}

如果一切顺利,您将看到以下内容:

4> code:which(foo_app).                                  
"/tmp/erl-target/lib/foo-2.0.0/ebin/foo_app.beam"

如果我在导出的 foo-2.0.0 中添加了一个新函数,它将可用,例如。

如果您想降级回以前的版本,您必须在 /tmp/erl-target/lib/foo-1.0.0/ebin/ 中有一个类似的 foo.appup 文件,但交换了版本号。


如有错误请指正,我是Erlang新手。我自己测试过,它对我有用。这只是在一个版本中升级您的应用程序,而不是升级版本本身。

如果有什么不清楚的地方,请告诉我。我仍在尝试自己解决这个问题。 :)