DynamicSupervisor - 与工人沟通的问题

DynamicSupervisor - problem with communication with the workers

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

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

returns 以下错误:

[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

问题出在处理程序中。你把所有的论点都搞砸了。 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 回调也有错误的签名。