Erlang / elixir:模块重新定义和热代码交换之间的区别

时间:2016-03-23 07:43:23

标签: elixir

代码交换: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语言中是有意义的)。

我想我的问题归结为以下几点:

  1. 在开发和生产过程中简单地重新定义模块会有多好/坏/丑?
  2. 在版本管理和回滚之外进行正确的代码交换有什么好处?是否有关于这个主题的好的教程/手册/文章?
  3. 热门代码交换如何在模块重新定义下工作?

2 个答案:

答案 0 :(得分:4)

在任何给定时间点的Erlang中,您可以最多运行两个版本的给定模块。

示例:

  • 加载模块
  • 使用代码
  • 启动gen_server
  • 更改内容并加载模块&#39; - 现在gen_server仍在运行旧代码
  • 启动第二个gen_server - 它将从模块&#39;
  • 运行新代码
  • 再次更改内容并加载模块&#39;&#39; - 因为在任何时间点只能有相同代码的两个版本,所以第一个模块被清除;运行它的所有进程都被终止,因此第一个gen_server被杀死

您可以通过从该模块调用完全限定的函数,从运行旧代码转换到在进程内运行新代码。因此,您不会调用function(Args)而是module:function(Args)。我不确定相同的机制是否适用于Elixir。

要手动进行热门代码升级,您必须在第一个版本的模块中进行此完全限定的函数调用。

如果您更改了gen_server中的状态表示,则无论如何都可能会使服务器崩溃。

所以回答你的问题:

  1. 这会非常糟糕。手动执行此操作仅意味着手动测试升级。它可能导致随机过程死亡,失去状态,在极端情况下会丢失数据。

  2. 使用appup和relups进行热代码交换是完全自动化的。你不需要&#34;钩子&#34;就像你的第一个代码版本中的完全限定功能一样,所以当你需要进行热代码升级时,你可以开始担心它。将正确调用代码更改,使您有机会将旧状态转换为新状态。如果您修改了多个进程,则可以定义在升级期间应冻结的模块组。我认为这里有一个很好的教程:http://learnyousomeerlang.com/relups

  3. 热代码交换刚刚开始使用先前模块定义的进程使用新模块定义。做适当的appup和relup处理代码加载,交换,更改状态和清除旧东西。使它更具可测性。

  4. 热门代码交换并不容易,在生产分布式系统时,使用新代码启动全新实例可能会更好。负载均衡器可以将新连接推送到新实例,我们只是等待旧连接死亡,然后再杀死旧实例。

    但是,在有一个实例且需要全天候在线的嵌入式系统中,热门代码交换可能是唯一的选择。

答案 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