我正试图与凤凰城的GenServer
合作,将从不同客户端收到的信息累积到网络套接字中。 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
这是我凤凰频道的相关部分:
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
奇怪的是"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
我不确定如何在多个频道操作之间共享一个GenServer。如果我使用iex
所有函数的工作方式与我期望的完全相同,但在Phoenix中,初始化和使用会被传播。
我尝试在应用程序中启动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
可悲的是,我在调试系统时遇到了麻烦,我无法弄清楚如何做到这一点。很多新东西一下子吸收,我距离Elixir的启蒙还有几个远的地方。我添加了两个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
只是继续null
HelloWeb.Stack
,但是当我第一次获得debug1
和null
pid
时,第二次崩溃:
** (MatchError) no match of right hand side value: {:error, {:already_started, #PID<0.12310.0>}}
这似乎表明它成功启动,将自身绑定到一个唯一值,并且第二次这样做会被拒绝,因为该特定进程已经启动。但是,我无法使用HelloWeb.Stack
来覆盖它。在进行一些更改和测试之后,让我展示代码的其他相关部分:
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
这是在application.ex中启动worker的方式:
worker(HelloWeb.Stack, []), #how to verify it's really starting something and to what value it stores it's pid?
答案 0 :(得分:0)
听起来你没有使用应用程序启动GenServer,因此,消息是正确的 - 例如没有过程活着。
HelloWeb.Stack.add(payload)
功能仍然有效的原因是因为这是GenServer的强制转换功能的意图,例如:你投入到这个过程是因为你不关心返回的结果,以及它是成功还是失败(否则你会使用call
)
例如:
iex(1)> GenServer.cast(:non_existing_genserver, :add)
:ok
在这里你可以看到,即使GenServer不存在,cast
仍然会返回一条确认消息,但实际上它不会去任何地方。
要解决您的问题,请导航到您的application.ex
文件并添加一个工作人员以在应用程序启动时启动GenServer。
children = [
#...other workers/supervisors
{HelloWeb, []}
]
答案 1 :(得分:0)
HelloWeb.Stack.start_link/1
未定义,因此无法调用您实现功能start_link/0
。它不需要任何参数。在文档中,我通常会看到start_link/1
的实现是为了启动您的GenServer。
当我阅读GenServer docs时,在启动GenServer作为监督树的一部分时,总是会调用HelloWeb.Stack.start_link/1
。在您的情况下,HelloWeb.Stack.start_link/1
未定义,因此无法调用。
所以我建议您像这样实现start_link/1
:
# Client
def start_link(_init_args) do
#GenServer.start_link(__MODULE__, [])
GenServer.start_link(__MODULE__, [], name: HelloWeb.Stack)
end
内部发生的事情是Elixir / Erlang专家必须回答的问题(非常欢迎编辑!),但是我认为这是问题的根源。