热代码替换在Erlang中的应用

21

我正在进行我的第一个erlang真实项目,不过这段代码为了简洁起见已经被精简。我想在我的项目远程运行时加载一个更新的文件。我已经阅读了有关使用像gen_servergen_fsm这样的行为来免费实现该功能的内容。虽然这可能可以实现结果,但我想利用此机会学习如何实现它,而不仅仅是完成它。我已经阅读了有关代码替换和LYSE有关热代码爱好者等其他内容的文档,但我还没有找到适合我所做的工作的任何东西,所以这是基本的想法。

-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!而不是This is a test。我知道我可以简单地调用c(reloading),让它按预期工作,但我想发送实际项目一个消息,而不是手动更新代码。那么我的问题出在哪里?我做错了什么,应该怎样热加载这个代码呢?如前所述,为了教学目的,我正在寻找一个非OTP(Open Telecom Platform,已停止开发)的解决方案。


1
在shell中:c(重新加载),l(重新加载)。 - Muzaaya Joshua
@MuzaayaJoshua 正如我在上一段中提到的,我知道我可以使用 c(reloading),但在实际项目中我不想使用 erl - joneshf
4个回答

25
为了得到一个明确的答案,我发布了这个帖子。
使用@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.

首先code:purge旧的?MODULE,然后compile:file新文件,最后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(“我已经改变了这个!〜n”),
3> Loop ! upgrade.                   
upgrade
4> Loop ! hello.  
I have changed this!
hello

6
通常,实际服务器不会明确处理新模块的编译、清除和加载等操作。您需要管理代码的环境,并在加载新版本后向服务器发送“升级”消息。如果您有多个使用相同代码的服务器,则这也是唯一的方法。OTP 就是这样工作的。 - rvirding

19

虽然Erlang可以处理模块的两个版本,并且使用mod:func(...)调用函数将始终调用模块的最新版本(如果该函数被导出),但您仍然需要将新版本的模块加载到Erlang系统中。不能期望它自动检测到您刚好有一个新版本的模块,找到它,编译它并加载它。

N.B. 编译和加载是两个不同的事情。因此,c(mod). 同时编译加载模块,而l(mod).仅加载已经编译的模块的目标代码(.beam文件)。Erlang编译器从模块compile调用,并且只是编译和生成一个.beam文件,而代码加载由模块code处理。


好的,很好,这很有道理。那么我如何在不使用 shell 的情况下加载新版本的模块呢? - joneshf
2
@joneshf 这一切都是通过模块 codecode:load_file(Mod) 实现的。这也是 shell 中 c/1l/1 使用的方法。更多文档请参见 http://www.erlang.org/doc/man/code.html。 - rvirding

1
除此之外,我想提醒您一些自动重新加载代码的工具存在。
您应该查看syncactive项目。

谢谢。我之前不知道这些项目。不想听起来傲慢或者不感激,但是这并没有真正回答问题。这个回答可能更适合作为评论。 - joneshf
1
@joneshf,我知道,但我担心它作为评论不会得到足够的关注,而实际上搜索(自动)热代码替换的人会更满意那些项目,而不是手工解决方案。 - Lol4t0
1
那么问题可能是问题标题不够具体。可以将其更改为“Erlang中的手动热代码替换”或类似内容。 - joneshf

1

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接