每秒 1 个 HTTP 请求的 GenServer

GenServer with 1 HTTP request per second

我做了这个genserver

defmodule Recorder do
  use GenServer

  def start_link(args) do
    id = Map.get(args, :id)
    GenServer.start_link(__MODULE__, args, name: id)
  end

  def init(state) do
    schedule_fetch_call()
    {:ok, state}
  end

  def handle_info(:jpeg_fetch, state) do
    spawn(fn ->
      IO.inspect("I am being called")
      IO.inspect(DateTime.utc_now())
      Fincher.request(:get, state.url) |> IO.inspect()
    end)
    schedule_fetch_call()
    {:noreply, Map.put(state, :run, true)}
  end

  defp schedule_fetch_call do
    Process.send_after(self(), :jpeg_fetch, 1000)
  end
end

我运行在这种状态下每秒 1 个请求。

  defp get_children do
    Enum.map([
      %{
        id: :hdipc,
        url: "http://77.66.206.55/jpgmulreq/1/image.jpg?key=1516975535684&lq=1&COUNTER"
      }
    ], fn(camera) ->
      Supervisor.child_spec({Recorder, camera}, id: camera.id)
    end)
  end

application.ex.

我在这里使用spawn但我不想使用spawn,请问解决这个问题最合乎逻辑和理想的方法是什么。

  1. 其中 GenServer 每秒都会发出一个请求。

  2. 也不要等待请求完成,因为请求可能需要一秒以上。

  3. 在 HTTP 请求-响应的情况下,我还想执行其他一些特定操作。

我不想让 genserver 耗尽并崩溃。但要处理每秒都会发生的请求的背压(不是 genstage,因为需求不确定)。

是否可以使用 GenServer 的方式 运行 而无需生成和处理请求?任何指导或帮助都会很棒。

它看起来像是 DynamicSupervisor 的完美用例。

将工人定义为 GenServer 来执行工作。

defmodule Recorder.Worker do
  use GenServer

  def start_link(opts) do
    {id, opts} = Map.pop!(opts, :id)
    GenServer.start_link(__MODULE__, opts, name: id)
  end

  def init(state) do
    schedule_fetch_call()
    {:ok, state}
  end

  def handle_info(:jpeg_fetch, state) do
    result = Fincher.request(:get, state.url)
    schedule_fetch_call()
    {:noreply, Map.put(state, :run, {DateTime.utc_now(), result})}
  end

  defp schedule_fetch_call,
    do: Process.send_after(self(), :jpeg_fetch, 1000)
end

然后定义一个DynamicSupervisor

defmodule Recorder.Supervisor do
  use DynamicSupervisor

  def start_link(opts),
    do: DynamicSupervisor.start_link(__MODULE__, opts, name: __MODULE__)

  def start_child(opts),
    do: DynamicSupervisor.start_child(__MODULE__, {Recorder.Worker, opts})

  @impl DynamicSupervisor
  def init(opts),
    do: DynamicSupervisor.init(strategy: :one_for_one, extra_arguments: [opts])
end

现在根据需要开始尽可能多的 DynamicSupervisors,也受到监督。


我相信我已经推荐了我的 Tarearbol 库,它(除其他外)将上面的 Dynamic Workers Management 简化为有点像

defmodule Recorder.Worker do
  use Tarearbol.DynamicManager

  def children_specs do
    Enum.into([%{id: :hdipc, url: "..."}], %{}, &{&1.id, &1})
  end

  def perform(_id, state) do
    result = Fincher.request(:get, state.url)
    {:noreply, Map.put(state, :run, {DateTime.utc_now(), result})}
  end
end

有了它,perform/2 将在 timeout 选项(默认为 1 秒)之后执行,并且还可以使用 handle_timeout/1.

处理响应超时

示例 from tests 可能会受到启发。