Elixir:如果服务器重新启动,如何保持 OTP 队列?

Posted

技术标签:

【中文标题】Elixir:如果服务器重新启动,如何保持 OTP 队列?【英文标题】:Elixir: How to persist OTP queue if server restarts? 【发布时间】:2017-09-16 14:50:05 【问题描述】:

我正在使用 OTP 来管理事件队列:

defmodule ParrotApi.MatchingSupervisor do
  use Supervisor

  ## Callbacks

  def start_link() do
    Supervisor.start_link(__MODULE__, [])
  end

  def init(_) do
    children = [
      worker(ParrotApi.MatchingServer, []), # TODO: State is gone if this crashes

      # Supervise connections
      supervisor(Registry, [:unique, :connection_registry]),
      supervisor(ParrotApi.ConnectionSupervisor, []),
    ]

    supervise(children, strategy: :one_for_one)
  end
end

我的问题是,如果服务器重新启动,我的队列会发生什么?我需要它在重新启动后持续存在。据我所知,它存储在内存中,因此如果服务器重新启动,它就会被擦除。

【问题讨论】:

【参考方案1】:

如果服务器重新启动,如果服务器是指整个机器而不仅仅是 genserver,那么您是正确的。我建议将队列存储在数据库或 redis 等持久存储中。

【讨论】:

【参考方案2】:

我创建了一个单独的代理来跟踪 GenServer 中的状态。 Agent 非常简单,有一个 API 来更新状态,并根据请求获取它。

我在 GenServer 中创建 reply(state, result)noreply(state) 函数,并在 handle_callhandle_casthandle_info 函数的末尾调用它们。

replyno_reply 函数将 state 放入代理中,然后返回 :no_reply, state:reply, result, state 元组。我还在终止时更新代理。

当服务器重新启动时,我检查它是否重新启动,如果是,则在 genservers init 回调中从代理中提取状态。我只是检查代理。如果它没有数据,我知道是原始启动。

这里的想法是代理是一个非常简单的价值存储。它非常简单,不太可能失败。

不过,您应该小心使用这种方法。重新启动策略背后的想法是 GenServer 以其初始状态重新启动。如果您的 GenServer 由于其状态问题而重新启动,它可能会进入连续重新启动状态。

【讨论】:

hm 所以代理将数据存储在磁盘上,因此在重新启动时恢复/持久化? 没有。我只是在解决您的 GenServer 重新启动的问题。如果需要处理应用程序启动,可以使用数据库或 erlang 的 DETS 模块。在这种情况下,您可能不需要代理,您可以将上面示例中对 Agent.put 的调用替换为将数据保存在磁盘上的调用。或者,如果您有一个多节点设计,我想您会使用另一个节点来缓存数据。 我决定将数据库中的记录存储与同步主管结合使用。这样,如果服务器宕机,我的队列可以恢复,我仍然可以保证我的记录按顺序处理 @Edmund 听起来像是满足您要求的好方法。顺便说一句,我使用上面给出的方法是针对不需要在服务器重置后幸存下来的状态。我的大多数解决方案都通过 tcp 套接字与外部服务器通信。如果我的应用程序重新启动,所有的 tcp 链接都会被删除,并且随着 tcp 连接的重新建立,状态会被重建。此外,我的状态更改可能每秒数千次,因此我无法承受如此频繁的数据库访问。 有道理。我的用例是匹配两个在线用户(如聊天轮盘赌)。所以我只需要按顺序将它们排队,然后将前两个出列以“匹配”它们。

以上是关于Elixir:如果服务器重新启动,如何保持 OTP 队列?的主要内容,如果未能解决你的问题,请参考以下文章

如何重新编译 Elixir 项目并从 iex 中重新加载它?

[Elixir002]节点启动后自动连接其它节点

使用纯 erlang OTP 代替带有 React.js 和 phoenix 框架的 elixir 后端

如何在 Elixir 主管中引用之前启动的进程

检查 Erlang OTP Release 以在 Elixir 上执行不同的功能

Elixir:通过某些操作重新启动 GenServer