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

Posted

技术标签:

【中文标题】Elixir:通过某些操作重新启动 GenServer【英文标题】:Elixir: restart GenServer with certain action 【发布时间】:2017-08-12 23:16:06 【问题描述】:

假设我有GenServer 实例:

defmodule MyModule do
  use GenServer

  def init(_) do
   :ok, %
  end

#...
end

我希望 MyModule 受到监督,但是当它崩溃时,在它以崩溃前的状态重新启动之前做一些事情:

defmodule MyModule do
  use GenServer

  def init(_) do
   :ok, %
  end

 def init(:restart, previous_state) do
   some_func(previous_state)
   :ok, previous_state
 end

#...
end

但我不确定如何实现这一点

【问题讨论】:

您可以使用terminate/2 回调并将状态存储在其他地方(代理、数据库、ETS)并将其加载到init/1 但我认为您不能让主管通过旧状态自动重启。 你想用那个状态做什么?除了日志记录之外,我无法想象同一个 genserver 会重用它的旧状态而导致它崩溃的用例。如果您收到类似的消息,它不会立即再次崩溃吗?崩溃向主管发送错误消息。你可能会抓住它并用它做点什么。否则使用 Dogbert 建议的方法将状态存储在某处,并在 genserver 重新启动时从那里加载它。 @Johannes 我会说,在我的情况下,不是状态本身会导致崩溃,但是通过对该状态应用无效调用会引发异常 如果这些是有效异常,为什么不直接从调用中的异常中恢复? 【参考方案1】:

我记得这在戴夫·托马斯的书中有所描述。这个想法是有另一个过程来保存状态的副本。这个过程有一个工作:跟踪状态的变化。这应该可以防止它崩溃(如果原始进程通知观察者状态更改,这意味着它在应用更改时没有崩溃)。

然后,当原始进程崩溃并重新启动时,它可以从备份进程中获取先前的状态(它应该从主管那里获取该进程的 PID)。

def start_link(backup_pid) do
   GenServer.start_link(__MODULE__, backup_pid)
end  

def init(backup_pid) do
  state = Backup.get_state(backup_pid)
  :ok, state
end

def terminate(_reason, state) do
  Backup.save_state(state)
end

【讨论】:

以上是关于Elixir:通过某些操作重新启动 GenServer的主要内容,如果未能解决你的问题,请参考以下文章

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

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

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

Docker 上的 Erlang/Elixir 和热代码交换

在 Elixir ExUnit 中,我如何保证 Supervisor 将创建一个新的 GeNserver?

“pin”操作符是干啥用的,Elixir 变量是可变的吗?