我知道我可以像这样打电话给GenServer
GenServer.call(pid, request)
# using a pid
或者像这样
GenServer.call(registered_name, request)
# if I registered the process with a name
但是有没有办法在不知道pid /注册名称的情况下在模块内部执行GenServer.call
?(即有类似GenServer.call(__CURRENT_PROCESS__, request)
的内容吗?)
答案 0 :(得分:5)
这根本行不通。 GenServer.call
只是向给定进程发送消息,然后等待另一条消息(回复),阻止当前进程。如果您以这种方式向self
发送消息,则该过程将无法自由处理该消息,因为它将被阻止等待回复。因此call
将永远超时。
我怀疑你需要的只是将你想要的功能提取到一个函数中并直接调用它。或者,您可以向当前流程发送cast
,有效地告诉它“稍后”执行某些操作。
答案 1 :(得分:4)
我不确定这是最好的方式,但你要找的是Kernel.self/0
编辑:
Per Sasha的评论代码看起来像这样:
GenServer.call(self, request)
编辑:
根据Pawel Obrok的优点,你不应该打电话给当前的流程。如果您需要向当前流程发送消息,您应该在这个方面进行:
GenServer.cast(self, request)
注意它是投射与呼叫。
答案 2 :(得分:3)
这取决于。如果您每个节点只启动一个GenServer
进程,则可以将其称为:
@doc """
If you want to call the server only from the current module.
"""
def local_call(message) do
GenServer.call(__MODULE__, message)
end
或
@doc """
If you want to call the server from another node on the network.
"""
def remote_call(message, server \\ nil) do
server = if server == nil, do: node(), else: server
GenServer.call({__MODULE__, server}, message)
end
如果您有来自同一模块的多个流程,则需要一个额外的标识符(例如,如果您有一个主管策略:simple_one_for_one
来按需生成GenServer
。
对于类似的东西我建议使用:
:gproc
来命名流程。:ets
如果您需要额外信息来识别您的流程。 :gproc
很棒,适用于GenServer
。您通常使用atom作为注册名称来命名您的进程。 :gproc
允许您使用任意术语(即元组)命名您的流程。
假设我的函数调用中有一个复杂的服务器标识符,例如{:id, id :: term}
,其中id
可以是一个字符串。我可以像GenServer
那样开始:
defmodule MyServer do
use GenServer
def start_link(id) do
name = {:n, :l, {:id, id}}
GenServer.start_link(__MODULE__, %{}, name: {:via, :gproc, name})
end
(...)
end
通过不同于原子的东西查找我的过程,就像我之前说过的那样,例如一个字符串。所以,如果我启动我的服务器,如:
MyServer.start_link("My Random Hash")
我的功能如下:
def f(id, message) do
improved_call(id, message)
end
defp improved_call(id, message, timeout \\ 5000) do
name = {:n, :l, {:id, id}}
case :gproc.where(name) do
undefined -> :error
pid -> GenServer.call(pid, message, timeout)
end
您可以使用它来调用以下流程:
MyServer.f("My Random Hash", {:message, "Hello"})
如果您的命名过程更复杂,您可以使用:ets
来存储更复杂的状态。
如果您想使用:gproc
,可以将其添加到mix.exs
文件中,例如:
(...)
defp deps do
[{:gproc, github: "uwiger/gproc"}]
end
(...)
另一方面,永远不要在GenServer.call/3
内拨打handle_call/3
。它将超时并在其他GenServer.call
上执行DOS。 handle_call/3
当时处理一条消息。
答案 3 :(得分:1)
根据我的评论,您正在尝试为GenServer
编写公共API函数,而不是在call
进程中创建GenServer
。如果不了解PID,AFAIK就没有办法做到这一点。但是,对于GenServers
,您只创建一个实例,存在这样一种情况的惯用语:您将GenServer
的唯一实例命名为实现它的模块的名称。这可以使用__MODULE__
宏轻松完成:
def start_link do
GenServer.start_link(__MODULE__, nil, name: __MODULE__)
end
然后在您的API函数中,您只需使用__MODULE__
代替PID:
def api_function do
GenServer.call(__MODULE__, :api_function)
end
这具有很好的属性,__MODULE__
将始终反映封闭模块的正确名称,即使重命名它也是如此。