DynamicSupervisor-与工人的沟通问题

时间:2018-11-23 11:05:57

标签: erlang elixir otp supervisor elixir-framework

我与在dynamicSupervisor中创建的工作人员进行交流时遇到问题。开始我所有的工作人员后,我尝试通过pid呼叫一个,并产生一个错误(总是)。 在孩子刚开始的时候,我把pid与ets保持在一起。然后,当我想给这个孩子打电话时,我通过标识符从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 个答案:

答案 0 :(得分: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回调也具有错误的签名。