代码交换:Achieving code swapping in Erlang's gen_server
模块重新定义:
iex(node2@127.0.0.1)6> Code.load_file("mesngr.ex", "./lib")
[{Mesngr,
<<70, 79, 82, 49, 0, 0, 12, 72, 66, 69, 65, 77, 69, 120, 68, 99, 0, 0, 0, 255, 131, 104, 2, 100, 0, 14, 101, 108, 105, 120, 105, 114, 95, 100, 111, 99, 115, 95, 118, 49, 108, 0, 0, 0, 4, 104, 2, 100, 0, ...>>}]
iex(node2@127.0.0.1)8> Code.load_file("mesngr.ex", "./lib")
lib/mesngr.ex:1: warning: redefining module Mesngr
[{Mesngr,
<<70, 79, 82, 49, 0, 0, 12, 72, 66, 69, 65, 77, 69, 120, 68, 99, 0, 0, 0, 255, 131, 104, 2, 100, 0, 14, 101, 108, 105, 120, 105, 114, 95, 100, 111, 99, 115, 95, 118, 49, 108, 0, 0, 0, 4, 104, 2, 100, 0, ...>>}]
iex(node2@127.0.0.1)9>
我已经注意到某些差异,例如在模块重新定义的情况下不会调用GenServer的code_change回调(因为我假设只是覆盖,而不是从new转换 - &gt;当前和当前 - &gt ;老)。但我也注意到重新定义这样的模块会改变底层代码(这在FP语言中是有意义的)。
我想我的问题归结为以下几点:
答案 0 :(得分:4)
在任何给定时间点的Erlang中,您可以最多运行两个版本的给定模块。
示例:
您可以通过从该模块调用完全限定的函数,从运行旧代码转换到在进程内运行新代码。因此,您不会调用function(Args)
而是module:function(Args)
。我不确定相同的机制是否适用于Elixir。
要手动进行热门代码升级,您必须在第一个版本的模块中进行此完全限定的函数调用。
如果您更改了gen_server
中的状态表示,则无论如何都可能会使服务器崩溃。
所以回答你的问题:
这会非常糟糕。手动执行此操作仅意味着手动测试升级。它可能导致随机过程死亡,失去状态,在极端情况下会丢失数据。
使用appup和relups进行热代码交换是完全自动化的。你不需要&#34;钩子&#34;就像你的第一个代码版本中的完全限定功能一样,所以当你需要进行热代码升级时,你可以开始担心它。将正确调用代码更改,使您有机会将旧状态转换为新状态。如果您修改了多个进程,则可以定义在升级期间应冻结的模块组。我认为这里有一个很好的教程:http://learnyousomeerlang.com/relups
热代码交换刚刚开始使用先前模块定义的进程使用新模块定义。做适当的appup和relup处理代码加载,交换,更改状态和清除旧东西。使它更具可测性。
热门代码交换并不容易,在生产分布式系统时,使用新代码启动全新实例可能会更好。负载均衡器可以将新连接推送到新实例,我们只是等待旧连接死亡,然后再杀死旧实例。
但是,在有一个实例且需要全天候在线的嵌入式系统中,热门代码交换可能是唯一的选择。
答案 1 :(得分:1)
警告“所以不要调用函数(Args),你会做Module.function(Args)。” @tkowal提到的是,在Elixir中import
仍然是完全限定的调用,即使它们在Elixir中编写的文字源代码中看起来不像它。
defmodule Foo do
import Bar, only: [baz: 0]
def foo do
# reloads Bar
baz
end
end