问题
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.
where GenServer will make a request each second.
Also don't wait for the request to complete, as the request can take more than a second.
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 DynamicSupervisor
s, 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