【发布时间】:2017-03-15 06:41:59
【问题描述】:
我正在拨打 Phoenix 路线,其工作往往需要 10 多秒。这太长了,Phoenix 超时。
这个问题可以通过 websockets 轻松解决,但是我想知道它是如何使用纯 REST 完成的。
Phoenix 流程模型是什么?是否所有请求都进入工作池,即使有池,长时间阻塞也是不明智的?
【问题讨论】:
我正在拨打 Phoenix 路线,其工作往往需要 10 多秒。这太长了,Phoenix 超时。
这个问题可以通过 websockets 轻松解决,但是我想知道它是如何使用纯 REST 完成的。
Phoenix 流程模型是什么?是否所有请求都进入工作池,即使有池,长时间阻塞也是不明智的?
【问题讨论】:
我想知道它是如何使用纯 REST 完成的
您要么提高代码效率,要么增加网络服务器的超时时间,或者两者兼而有之。
什么是 Phoenix 流程模型?是否所有请求都进入工作池
不,请求不会进入工作池。
对于 Web 服务器,Phoenix 当前使用的是cowboy。因此,当您点击 Phoenix 端点时,它实际上是在点击底层牛仔实现,这将为您的请求生成一个新的 Erlang 进程。然后由 Erlang 调度程序实际为您的进程提供 CPU 时间。
【讨论】:
也许您还可以尝试其他方法:启动一个进程来完成繁重的工作并立即返回给客户。指定名称或以其他方式使此任务可发现。然后每隔一段时间从客户端轮询以检查该过程是否完成。一旦它完成并且你得到结果,你就可以杀死它。
只有当你真的不能使用套接字时,轮询才是一种解决方法,否则套接字推送会更有效率
编辑: 好的,这是一个概念证明,应该为您指明正确的方向。
首先定义一个模块来封装这个行为
defmodule LongRunningTask do
def start(params) do
spawn(fn ->
me = self()
spawn(fn -> do_long_running_task(me, params) end)
wait()
end)
|> inspect()
end
def get_result("#PID" <> ref) do
pid = ref
|> :erlang.binary_to_list
|> :erlang.list_to_pid
send(pid, {:is_it_done_yet?, self()})
receive do
{:answer, result} -> result
:still_not_done -> :still_not_done
after
1000 -> :no_one_here
end
end
defp wait(state \\ nil) do
receive do
{:result, result} -> wait(result)
{:is_it_done_yet?, from} ->
case state do
nil ->
send(from, :still_not_done)
wait(state)
result ->
send(from, {:answer, result})
end
end
end
defp do_long_running_task(pid, _params) do
Process.sleep(10_000)
send(pid, {:result, "the answer"})
end
end
然后在您的控制器中定义 2 个操作,触发任务的初始请求和一个轮询端点,您可以在其中检查是否完成
defmodule MyController do
def init_request(conn, params) do
ref = LongRunningTask.start(params)
json conn, %{ref: ref}
end
def poll(conn, %{"ref" => ref}) do
case LongRunningTask.get_result(ref) do
:still_not_done -> json conn, %{result: "nope, poll again"}
result -> json conn, %{result: result}
end
end
end
PS:Elixir 很有趣 :)
【讨论】: