我是Elixir的新手,之前我从未遇到过这个问题。我很好奇处理这类问题的最佳或可接受的方式是什么?
我spawn
一个进程,它从websocket接收数据,然后send
将此数据传回父级。父运行一个递归process()
函数,receive
来自生成进程的数据。
process
函数与数据匹配,并且在大多数情况下运行回调函数。由于处理数据,此回调函数可能位于较重的一侧。
通过删除大量Logger.info
语句,我能够发现生成的进程从websocket接收数据并将send
这些数据传递给父进程但是父进程实际上并没有处理邮箱。
我使用alive?/1
函数确定父进程处于活动状态,之后我使用Process.info/1
函数检查了邮箱的状态 - 它显示邮箱消息的数量正在增长(数百个)消息)。
我的工作解决方案是,不是在process
函数中调用回调,而是spawn
,我相信它允许父进程更快地处理其邮箱。
有什么其他更好的方法来处理这种情况?
def run(url, callback) do
{domain, path} = parse_url(url)
socket = Socket.Web.connect!(domain, path: path, secure: true)
spawn_link(Project.WebsocketClient, :listen, [url, socket, self])
process(callback)
end
def process(callback) do
receive do
{:ok, data} ->
callback.(data)
# spawn(fn -> callback.(data) end) - my fix.
{:ping} ->
Logger.info("Pong")
{:error, _, url} ->
run(url, callback)
end
process(callback)
end
defp recv(socket) do
try do
Socket.Web.recv!(socket)
rescue
e in RuntimeError -> {:error, e}
end
end
def listen(url, socket, pid) do
case recv(socket) do
{:text, data} ->
send(pid, {:ok, data})
{:ping, _} ->
Logger.info("Ping!")
Socket.Web.send!(socket, {:pong, ""})
send(pid, {:ping})
{:error, e} ->
Logger.warn("Websocket died because: #{inspect(e)}. Attempting to restart")
send(pid, {:error, e, url})
exit(:died)
end
listen(url, socket, pid)
end
答案 0 :(得分:7)
这是一个开放式的问题,所以我将分享我对这个问题的看法。
receive
是有选择性的。如果其他人正在向进程发送消息并且它们与这些模式中的任何一个都不匹配,则消息可能会累积。记录这些消息甚至使进程崩溃总是一个好主意:
receive do
(...)
other -> log_somewhere_or_crash(other)
end
spawn
帮助您,可能邮箱不会被错误的邮件弄得乱七八糟。 spawn
并不是一个坏主意,但您无法控制已创建的进程数。当您创建数百万个内存时,可能会耗尽内存。使用可能是个好主意。您可以定义所需的工人数量。
在使用池中的工作人员之前,您必须将其检出。完成工作后,您必须将其检入池中。如果所有工作人员都忙,并且您使用了阻止API,那么调用进程将等待。它会将队列从“处理过程”移动到“监听过程”。
GenServer
。您已使用GenServer
功能重新实现process
行为。 GenServer
完全符合您的要求,但通常更容易进行测试和调试。您只需指定回调。它也可以很容易地监督。
如果处理繁重且您的消息不断出现,它们将在某处缓冲。现在它在父进程中。如果使用spawn
,它们将在调度程序队列中等待的许多进程中进行缓冲。如果减慢从WebSocket接收消息的速度,它们将存储在TCP缓冲区中,这也可能会溢出。这样,发件人将等待TCP窗口。如果它在一个单独的线程中生成消息,它们将在那里累积。
最好有一种流量控制机制,通知生产者减慢或丢弃无法处理的消息。
有一个非常好的poolboy。