我与在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
答案 0 :(得分:1)
问题在处理程序中。你搞砸了所有论点。 state
是GenServer
的内部状态,您没有传递它,而是在回调中收到它。如果您的州是具有键project_id
和pid
的地图,并且您将其命名为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
回调也具有错误的签名。