DynamicSupervisor - 与工人沟通的问题

Posted

技术标签:

【中文标题】DynamicSupervisor - 与工人沟通的问题【英文标题】:DynamicSupervisor - problem with communication with the workers 【发布时间】:2019-04-25 23:56:00 【问题描述】:

我与在我的 dynamicSupervisor 中创建的工作人员的沟通有问题。在启动我所有的工作人员后,我尝试通过 pid 调用一个并生成一个错误(总是)。 在孩子开始时,我用 ets 保存 pid。然后,当我想打电话给这个孩子时,我通过标识符从 ets 获取 pid。到目前为止,一切都很好。 问题是当我执行以下操作时:

GenServer.call(
  pid,
  :action_project, %project_id: project_id, pid: :erlang.pid_to_list(pid)
)

返回以下错误:

[error] GenServer #PID<0.606.0> terminating
** (RuntimeError) attempted to call GenServer #PID<0.606.0> but no handle_call/3 clause was provided
    (backercamp) lib/gen_server.ex:693: MyApplication.ProjectWorker.handle_call/3
    (stdlib) gen_server.erl:661: :gen_server.try_handle_call/4
    (stdlib) gen_server.erl:690: :gen_server.handle_msg/6
    (stdlib) proc_lib.erl:249: :proc_lib.init_p_do_apply/3
Last message (from #PID<0.680.0>): :action_project, %pid: '<0.606.0>', project_id: "23"
State: %pid: '<0.606.0>', project_id: "23"
Client #PID<0.680.0> is alive

    (stdlib) gen.erl:169: :gen.do_call/4
    (elixir) lib/gen_server.ex:921: GenServer.call/3

这个电话有什么问题?

DynamicSupervisor 代码:

defmodule MyApplication.Supervisor do
  use DynamicSupervisor

  alias MyApplication.Management

  def start_link(arg) do
    DynamicSupervisor.start_link(__MODULE__, arg, name: __MODULE__)
  end

  def init(arg) do
    DynamicSupervisor.init(arg)
  end

  def start_project_worker(project_id) do
    spec = MyApplication.ProjectWorker, %project_id: project_id
    DynamicSupervisor.start_child(__MODULE__, spec)
  end

  def start_all_project_workers() do
    Enum.each(Management.list_all_projects(), fn %id: project_id ->
      IO.puts("Started for project Id [#project_id]")
      # 1s between workers
      :timer.sleep(1000)
      start_project_worker("#project_id")
    end)
  end

  def action_project(pid, project_id) do
    GenServer.call(
      pid,
      :action_project, %project_id: project_id, pid: :erlang.pid_to_list(pid)
    )
  end
end

工人代码:

defmodule MyApplication.ProjectWorker do

  use GenServer, restart: :transient

  alias MyApplication.Settings
  alias MyApplication.Management

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

  def init(state) do
    schedule_action_project(Settings.get_frequency_ms())
    state = Map.put(state, :pid, :erlang.pid_to_list(self()))
    persist_state(state)
    :ok, state
  end

  def handle_info(:schedule_action_project, %project_id: _project_id, pid: _pid = state) do
    action_by_state(state)
    :noreply, state
  end

  def handle_call(:action_project, %project_id: _project_id, pid: _pid = state) do
    case action_by_state(state) do
      true ->
        terminate(state)

      false ->
        :reply, state, state
    end
  end

  defp persist_state(state) do
    IO.puts(" :: Add project_id [#state.project_id] and pid #state.pid")
    :ets.insert_new(:project_backup, state.project_id, state.pid)
  end

  defp delete_persist_state(project_id) do
    IO.puts(" :: Delete project_id [#project_id]")
    :ets.delete(:project_backup, project_id)
  end

  defp schedule_action_project(time) do
    IO.puts(" :: Schedule_action_project [#time]")
    Process.send_after(self(), :schedule_action_project, time)
  end

  defp terminate(%project_id: project_id, pid: _pid = state) do
    IO.puts(
      " :: Stop processed, everything is done! project_id [#state.project_id] and pid #
        state.pid
      "
    )

    delete_persist_state(project_id)
    :stop, :normal, state
  end

  defp action_by_state(%project_id: _project_id, pid: _pid = state) do
    action_by_project(Management.get_project!(state.project_id))
  end

  defp action_by_project(%Project = project) do
    #do something in project
  end
end

【问题讨论】:

你得到什么错误?在此处发布堆栈跟踪。 【参考方案1】:

问题出在处理程序中。你把所有的论点都搞砸了。 stateGenServer 的内部状态,你不通过它,你在回调中接收它。如果您的状态是具有键 project_idpid 的映射并且您将其称为 GenServer.call(pid, :action_project),则以下签名将匹配。

def handle_call(:action_project, %project_id: _project_id, pid: _pid = state)

您应该更改此处理程序以匹配您的调用者:

def handle_call(
  :action_project, %project_id: _project_id, pid: _pid,
  _from,
  state
)

还要注意handle_call回调接收三个参数(不像handle_cast,第二个是消息的来源。


旁注: handle_info 回调也有错误的签名。

【讨论】:

哦,你是一台机器 :D 你对 handle_call 的关系是对的……handle_info 有什么问题? 我猜也一样。我不确定状态是什么。 另一个疑问。是否有可能在孩子开始时就知道它是否来自重新启动?或者有不同的孩子的初始化功能? 我不确定我是否遵循。如果您需要不同的init,最简单的方法是创建另一个模块。 不。作为一种解决方法,您可能会在第一次启动时向 DETS 设置一个标志,从 init 检查此标志并采取相应的行为。

以上是关于DynamicSupervisor - 与工人沟通的问题的主要内容,如果未能解决你的问题,请参考以下文章

ROS 的工作过程及通信原理

如何在查询中与工人及其旁边的经理一起编写查询

markdown 与prisma的码头工人

如何与工人一起创建弹性项目?

gunicorn 不能与一个以上的工人一起运行

工人与资本家互相养活,主动权在资本家手里