【问题标题】:Simple Elixir / Plug Processes Issue - PID not sticking around简单的 Elixir / Plug 进程问题 - PID 不存在
【发布时间】:2019-12-28 20:52:16
【问题描述】:

我刚开始使用 Elixir,想用 Plug 构建一个非常简单的 API。我使用this guide 启动并运行了一个非常简单的 API。


基本上,我面临的问题是,每当我在queue_service.ex 中使用send/2 函数时,似乎都找不到我注册为:qs 的进程(并且出现错误)。我想要实现的是一个持续存在的过程,以便我可以跨请求维护状态。


在我的router.ex 文件中,我有:

defmodule SecondQueue.Router do
  use Plug.Router

  alias SecondQueue.Plug.VerifyParams
  alias SecondQueue.QueueService

  plug Plug.Parsers, parsers: [:urlencoded, :multipart]
  plug :match
  plug :dispatch


  get "/receive-message" do
    # gather query parameters from connection
    queue = Map.get(conn.params, "queue")
    message = Map.get(conn.params, "message")

    # handle the params
    QueueService.handle_incoming(queue, message)

    send_resp(conn, 201, "Created")
  end
end


然后在queue_service.ex 内部,我启动队列进程,将其注册到:qs 的原子,然后希望稍后能够通过请求调用的函数访问该进程。我有:

defmodule SecondQueue.QueueService do
  alias SecondQueue.QueueStore
  use Agent

  {:ok, agent_pid} = QueueStore.start_queues_link()
  Process.register(agent_pid, :qs)

  def handle_incoming(queue, message) do
    queue_atom = String.to_atom(queue)
    send(:qs, {:put, queue_atom, "success"})
  end
end

最后,在queue_store.ex 中,我实际上定义了我想要存储状态的进程,并运行一个循环以使其保持活动状态并准备好接收消息。我有:

defmodule SecondQueue.QueueStore do
  def start_queues_link() do
    Task.start_link(fn -> queues_loop(%{}) end)
  end

  defp queues_loop(map) do
    receive do
      {:get, key, caller} ->
        send caller, Map.get(map, key)
        queues_loop(map)
      {:put, key, value} ->
        IO.puts("i am here")
        queues_loop(Map.put(map, key, value))
    end
  end
end

更新:
Github 仓库:https://github.com/qarthandgi/test-second-queue

【问题讨论】:

  • 就像 Aleksei 所说,您在编译时开始命名进程,因此在运行时它不存在。另一个问题是,当您应该考虑使用GenServer 或Agent 时,您当前正在使用任务。任务并不是真的要以这种能力使用。 Agents 和 GenServers 还具有不需要为事物滚动自己的循环的好处,因为有些极端情况是您不会想到的。
  • @JustinWood 我很欣赏额外的见解。肯定会调查那些。到目前为止,我喜欢 Elixir,只是一种不同的思维方式。
  • 旁注: 如果queue 来自外部,String.to_atom(queue) 具有潜在危险。它容易受到原子 DoS 攻击。
  • @AlekseiMatiushkin 它确实来自外部。我刚看到this article。由此,我推断您应该首先尝试将其转换为现有原子,如果抛出异常,则创建一个新原子。这是保护这种攻击需要做的吗?
  • 不,这不能保护原子 DoS。当它来自外部时,您应该将其保留为字符串。映射键完全可以是字符串。

标签: erlang elixir cowboy plug


【解决方案1】:

Elixir 是一种编译语言。下面的代码在编译阶段执行;运行时未启动任何进程。

defmodule SecondQueue.QueueService do
  ...
  {:ok, agent_pid} = QueueStore.start_queues_link()
  Process.register(agent_pid, :qs)
  ...
end

相反,您需要将此代码放入一个函数并显式调用此函数以启动QueueStore(直接或通过将其插入应用的监督树。)

【讨论】:

  • 哇,谢谢。是否应该在 router.ex 文件中直接调用 start_queue_store() 函数?在application.ex 里面?我承认这可能是偏好,但我只是想知道是否有最佳实践方式来做到这一点。我还在更新问题以包含我的 github 存储库。
  • 如果这是要在您的应用程序的生命周期内运行的东西,您需要将其添加到您的监督树中。
  • 如果您有一个应用程序,常见的模式是将进程放入监督树中,以便在崩溃/失败时由 OTP 优雅地重新启动。
  • @AlekseiMatiushkin 感谢您的见解。谢谢!
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2014-09-27
  • 1970-01-01
  • 1970-01-01
  • 2016-07-03
  • 1970-01-01
  • 2011-09-26
相关资源
最近更新 更多