[英]Using a GenServer in Phoenix - the process is not alive
I'm trying to get my head around working with a GenServer
in Phoenix to accumulate information received from different clients to a web socket. 我正在努力与Phoenix的
GenServer
合作,将从不同客户端收到的信息累积到Web套接字。 The GenServer
looks like this: GenServer
看起来像这样:
defmodule HelloWeb.Stack do
use GenServer
@name {:global, __MODULE__} # this seems to help me to prevent having to drag the pid everywhere in the application
# Client
def start_link do
#GenServer.start_link(__MODULE__, [])
GenServer.start_link(__MODULE__, [], name: __MODULE__)
end
def add(item) do
GenServer.cast(@name, item)
end
def view do
GenServer.call(@name, :view)
end
def remove(item) do
GenServer.cast(@name, {:remove, item})
end
def wipe do
GenServer.cast(@name, :wipe)
end
#Server
def init(list) do
{:ok, list}
end
def handle_cast(:wipe, _list) do
updated_list = []
{:noreply, updated_list}
end
def handle_cast({:remove, item}, list) do
updated_list = Enum.reject(list, fn(i) -> i == item end)
{:noreply, updated_list}
end
def handle_cast(item, list) do
updated_list = [item|list]
{:noreply, updated_list}
end
def handle_call(:view, _from, list) do
{:reply, list, list}
end
end
This is the relevant part of my Phoenix channel: 这是我的Phoenix频道的相关部分:
def handle_in("answer", payload, socket) do
HelloWeb.Stack.add(payload)
{:noreply, socket}
end
def handle_in("answers", _payload, socket) do
{:reply, {:ok, HelloWeb.Stack.view}, socket}
end
The weird thing is that "answer"
seems to work every time I call it but "answers"
always crashes the GenServer: 奇怪的是,每次我调用它时,
"answer"
似乎都起作用,但是"answers"
总是使GenServer崩溃:
[error] GenServer #PID<0.10032.0> terminating ** (stop) exited in: GenServer.call({:global, HelloWeb.Stack}, :view, 5000) ** (EXIT) no process: the process is not alive or there's no process currently associated with the given name, possibly because its application isn't started
I'm not sure how to share one GenServer between multiple channel actions. 我不确定如何在多个通道操作之间共享一个GenServer。 If I use
iex
all of the functions work exactly as I expect them to work, but in Phoenix the initialization and usage are spread. 如果我使用
iex
所有功能都可以按我预期的方式正常工作,但是在Phoenix中,初始化和用法已得到广泛推广。
I try to start the GenServer in Application as follows: 我尝试按以下方式在Application中启动GenServer:
defmodule Hello.Application do
use Application
# See https://hexdocs.pm/elixir/Application.html
# for more information on OTP Applications
def start(_type, _args) do
import Supervisor.Spec
# Define workers and child supervisors to be supervised
children = [
# Start the endpoint when the application starts
supervisor(HelloWeb.Endpoint, []),
# Start your own worker by calling: Hello.Worker.start_link(arg1, arg2, arg3)
worker(HelloWeb.Stack, []),
]
# See https://hexdocs.pm/elixir/Supervisor.html
# for other strategies and supported options
opts = [strategy: :one_for_one, name: Hello.Supervisor]
Supervisor.start_link(children, opts)
end
Sadly I have trouble debugging into the system and I can't figure out yet how to do that right. 可悲的是,我在调试系统时遇到了麻烦,但我仍然无法弄清楚该如何做。 Lots of new things to absorb at once and I'm still a few a-ha's away from Elixir enlightenment.
大量的新事物可以立即吸收,而Elixir的启蒙运动距离我还有几分之遥。 I have added two
debug
endpoints to debug things in very crude way: 我添加了两个
debug
端点以非常粗略的方式调试事物:
def handle_in("debug2", _payload, socket) do
{:reply, {:ok, %{ genserver: GenServer.whereis(HelloWeb.Stack) }}, socket}
end
def handle_in("debug1", _payload, socket) do
{:ok, pid} = HelloWeb.Stack.start_link
{:reply, {:ok, %{ genserver: GenServer.whereis(HelloWeb.Stack), pid: :erlang.pid_to_list(pid) }}, socket}
end
debug2
just keeps returning null
for HelloWeb.Stack
, but when I run debug1
the first time I get null
and a valid pid
, the second time it crashes with: debug2
始终为HelloWeb.Stack
返回null
,但是当我第一次运行debug1
时,我第一次获得null
和一个有效的pid
,第二次崩溃时,它的出现是:
** (MatchError) no match of right hand side value: {:error, {:already_started, #PID<0.12310.0>}}
This seems to indicate that it starts successfully, binds itself to a unique value and doing that for a second time is rejected because that particular process is already started. 这似乎表明它已成功启动,将其自身绑定到唯一值,并且第二次执行该操作被拒绝,因为该特定进程已经启动。 However I cannot use
HelloWeb.Stack
to reach it. 但是,我无法使用
HelloWeb.Stack
来到达它。 Let me show the other relevant parts of the code after doing some changes and tests: 在进行一些更改和测试之后,让我展示代码的其他相关部分:
defmodule HelloWeb.Stack do
use GenServer
@name {:global, __MODULE__}
# Client
def start_link do
#GenServer.start_link(__MODULE__, [])
GenServer.start_link(__MODULE__, [], name: @name)
end
This is how the worker is started in application.ex: 这是在application.ex中启动工作程序的方式:
worker(HelloWeb.Stack, []), #how to verify it's really starting something and to what value it stores it's pid?
Sounds like you aren't starting the GenServer with the application and therefore, the message is correct - eg there is no process alive. 听起来好像您没有使用该应用程序启动GenServer,因此,该消息是正确的-例如,没有活动的进程。
The reason the HelloWeb.Stack.add(payload)
function still works is because that's the intention of the cast functionality of GenServer's, eg you cast to the process because you don't care about the returned result at and whether it succeeds or fails (otherwise you would use call
) HelloWeb.Stack.add(payload)
函数仍然起作用的原因是因为这是GenServer的强制转换功能的目的,例如,您强制转换为该进程是因为您不关心返回的结果是否成功以及是否成功(否则,您将使用call
)
For example: 例如:
iex(1)> GenServer.cast(:non_existing_genserver, :add)
:ok
Here you can see that even though the GenServer doesn't exist, cast
still returns an ack message, but in reality it's not going to go anywhere. 在这里,您可以看到,即使GenServer不存在,
cast
仍会返回一条确认消息,但实际上它不会走到任何地方。
To solve your problem, navigate to your application.ex
file and add in a worker to start the GenServer when the application starts up. 要解决您的问题,请导航至
application.ex
文件,并添加一个工作程序以在应用程序启动时启动GenServer。
children = [
#...other workers/supervisors
{HelloWeb, []}
]
HelloWeb.Stack.start_link/1
is not defined and thus cannot be called HelloWeb.Stack.start_link/1
,因此无法调用 You implement the function start_link/0
. 您实现了功能
start_link/0
。 It does not take any arguments. 它不需要任何参数。 In the docs I usually see
start_link/1
being implemented in order to start your GenServer. 在文档中,我通常会看到为启动您的GenServer而执行了
start_link/1
。
As I read the GenServer docs , HelloWeb.Stack.start_link/1
is always invoked when you start your GenServer as part of the supervision tree. 当我阅读GenServer文档时 ,在启动GenServer作为监管树的一部分时,始终会调用
HelloWeb.Stack.start_link/1
。 In your case HelloWeb.Stack.start_link/1
is not defined and thus cannot be called. 在您的情况下,未定义
HelloWeb.Stack.start_link/1
,因此无法调用它。
So I suggest you implement start_link/1
like: 所以我建议您像这样实现
start_link/1
:
# Client
def start_link(_init_args) do
#GenServer.start_link(__MODULE__, [])
GenServer.start_link(__MODULE__, [], name: HelloWeb.Stack)
end
What happens internally is something the Elixir/Erlang experts have to answer (Edits are very welcome!), but I think this is the root of your problem. 内部发生的事情是Elixir / Erlang专家必须回答的问题(非常欢迎编辑!),但是我认为这是问题的根源。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.