gpt4 book ai didi

erlang - Erlang 代码热交换在事件过程中是如何工作的?

转载 作者:行者123 更新时间:2023-12-04 13:58:29 25 4
gpt4 key购买 nike

我目前正在开发一个实时媒体服务器,它将允许普通消费者向我们发送实时视频。在我们当前的环境中,我们已经看到发送给我们的广播持续了几天,因此能够在不断开用户连接的情况下修复错误(或添加功能)的想法非常引人注目。

然而,当我编写代码时,我意识到热代码交换没有任何意义,除非我编写每个进程以便所有状态始终在 gen_server 内完成,并且 gen_server 调用的所有外部模块必须尽可能简单。

让我们看下面的例子:

-module(server_template).
-behaviour(gen_server).

-export([start/1, stop/0]).
-export([init/1, handle_call/3, handle_cast/2, handle_info/2, terminate/2, code_change/3]).

start() -> gen_server:start_link({local, ?MODULE}, ?MODULE, [], []).

init([]) -> {ok, {module1:new(), module2:new()}}.

handle_call(Message, From, State) -> {reply, ok, State}.

handle_cast(any_message, {state1, state2}) ->
new_state1 = module1:do_something(state1),
new_state2 = module2:do_something(state2),
{noreply, {new_state1, new_state2}}.

handle_info(_Message, _Server) -> {noreply, _Server}.

terminate(_Reason, _Server) -> ok.

code_change(_OldVersion, {state1, state2}, _Extra) ->
new_state1 = module1:code_change(state1),
new_state2 = module2:code_change(state2)
{ok, {new_state1, new_state2}}

据我所知,当一个新版本的代码被加载到当前运行的运行时而不使用OTP系统时,你可以通过调用你的模块作为外部函数调用来升级到当前的代码版本,所以 my_module:loop(state) .

我还看到,当执行热插拔时, code_change/3函数被调用并升级状态,所以我可以使用它来确保我的每个依赖模块将他们给我的最后一个状态迁移到当前代码版本的状态。这样做是因为主管知道正在运行的进程,这允许进程暂停,以便它可以调用代码更改函数。都好。

但是,如果调用外部模块总是调用该模块的当前版本,那么如果在功能中间完成热交换,这似乎会中断。例如,同样我的 gen_server 当前正在处理 any_message投,比如说在运行 module1:do_something()之间和 module2:do_something() .

如果我理解正确, module2:do_something()现在将调用 do_something 的最新版本函数,这可能意味着我将未迁移的数据传递到 module2:do_something() 的新版本中.如果它是已更改的记录、具有意外数量的元素的数组,或者即使 map 缺少代码期望的值,这很容易导致问题。

我是否误解了这种情况是如何运作的?如果这是正确的,这似乎表明我必须跟踪可能转换模块边界的任何数据结构的某种类型的版本详细信息,并且每个公共(public)函数都必须检查该版本号并在必要时执行按需迁移。

这似乎是一个非常高的要求,似乎很容易出错,所以我想知道我是否遗漏了一些东西。

最佳答案

是的,你完全正确。没有人说热代码交换很容易。我在一家电信公司工作,该公司的所有代码升级都是在实时系统上执行的(这样用户就不会在通话过程中断开连接)。正确地做意味着仔细考虑你提到的所有这些场景,并为每个失败准备代码,然后测试,然后修复问题,测试等等。要正确测试它,您需要一个在负载下(例如在测试环境中)运行旧版本的系统,然后部署新代码并检查是否有任何崩溃。

在您的问题中提到的这个特定示例中,处理此问题的最简单方法是编写两个版本的 module2:do_something/1 ,一个接受旧状态,一个接受新状态。然后相应地处理旧状态,例如将其转换为新状态。

为此,您还需要确保 module2 的新版本在任何模块有机会以新状态调用它之前部署:

  • 如果应用程序包含 module2是其他应用程序的依赖项release_handler将首先升级该模块。
  • 否则,您可能需要将部署分成两部分,首先升级常用功能以便它们可以处理新状态,然后部署新版本 gen_servers和其他调用 module2 的模块.
  • 如果您不使用发布处理程序,您可以手动指定加载模块的顺序。

  • 这也是为什么在 Erlang 中它是 advised to avoid circular dependencies 的原因。在模块之间的函数调用中,例如当 modA调用 modB 中的函数它调用 modA 中的另一个函数.

    对于在发布处理程序的帮助下执行的升级,您可以验证 release_handler 的顺序。将在 relup 中升级旧系统上的模块 release_handler 的文件根据 old and new release 生成.这是一个包含所有升级说明的文本文件,例如: remove (删除模块), load_object_code (加载新模块), load , purge , ETC。

    请注意,没有严格要求所有应用程序必须遵循 OTP 原则才能使热代码交换工作,但是使用 gen_server的和正确的 supervisor堆栈使开发人员和发布处理程序都更容易处理此任务。

    如果您不使用 OTP 版本,则无法使用发布处理程序进行升级,但您仍然可以在系统上强制重新加载模块并将它们升级到新版本。只要您不需要添加/删除 Erlang 应用程序就可以正常工作,因为为此需要更改发布定义,并且在没有发布处理程序的支持的情况下无法在实时系统上完成。

    关于erlang - Erlang 代码热交换在事件过程中是如何工作的?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/37368376/

    25 4 0
    Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
    广告合作:1813099741@qq.com 6ren.com