GenServer with 1 HTTP request per second

核能气质少年 提交于 2021-01-29 17:12:40

问题


I have made this 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

I am running it 1 request per second with such a state.

  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

in the application.ex.

I am using spawn here but I don't want to use spawn, what is the most logical and ideal way to solve this problem.

  1. where GenServer will make a request each second.

  2. Also don't wait for the request to complete, as the request can take more than a second.

  3. There are a few other certain operations I want to do in case of HTTP request-response.

I don't want to make genserver exhausted and crash. but to handle the back pressure of requests, which will be going each second (Not genstage as demand is not certain).

Is it possible to use GenServer in such a way that it can run without spawn and handle requests? any guidance or help would be wonderful.


回答1:


It looks like a perfect use-case for DynamicSupervisor.

Define the worker as a GenServer to perform a work.

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

Then define a DynamicSupervisor as

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

Now start as many DynamicSupervisors, also supervised, as you need.


I believe I have already recommended my Tarearbol library that (besides everything else,) simplifies the above with Dynamic Workers Management to somewhat like

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

With it, perform/2 will be executed after timeout option (defaulted to 1 second,) and one might also handle response timeouts with handle_timeout/1.

Examples from tests might be examined for inspiration.



来源:https://stackoverflow.com/questions/65446116/genserver-with-1-http-request-per-second

标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!