生成一个受监督的进程以定期报告心跳

Spawning a supervised process to report a heartbeat periodically

我正在尝试生成一个进程,该进程将每五秒 post 一个 HTTP 请求以向服务器报告它的心跳。我的代码是:

defmodule MyModule.Heartbeat do
  def start_link do
    spawn_link(fn ->
      :timer.apply_interval(:timer.seconds(5), __MODULE__, :beat, [])
    end)
  end

  defp beat do
    HTTPoison.post "https://myserver/heartbeat
  end
end

defmodule MyModule.Supervisor do
  use Supervisor

  def start_link do
    Supervisor.start_link(__MODULE__, :ok)
  end

  def init(:ok) do
     children = [
       worker(MyModule.Heartbeat, [])
     ]

    supervise(children, strategy: :one_for_one)
  end
end

但是,当我尝试启动该应用程序时,它退出并出现以下错误:

[info] Application my_module exited: MyModule.start(:normal, []) returned an error: shutdown: failed to start child: MyModule.Heartbeat
    ** (EXIT) #PID<0.535.0>

我只需要一些进程,它将 运行 作为监督树的一部分,并在指定的时间间隔内发送请求。它本身不需要能够接收任何消息,我也不太在意具体的实现。

任何人都可以提出我在这里做错了什么,阻止了这个过程的启动,是否有更好的方法来实现这个?

您的代码中有 3 个错误:

  1. :timer.apply_interval/4 returns 立即,因此匿名 在 MyModule.Heartbeat.start_link/0 中传递给 spawn_link 的函数终止 执行该行后不久,导致 :timer 从中删除间隔 它的队列,并且从未调用 MyModule.Heartbeat.beat/0

    你可以通过在最后添加一个 :timer.sleep(:infinity) 来解决这个问题 表达式,它使您的进程永远休眠。

  2. MyModule.Heartbeat.beat/0 需要是一个 public 函数,这样 传递给 spawn_link 的匿名函数可以调用它。

  3. MyModule.Heartbeat.start_link/0 需要 return {:ok, pid} 才能成功。 spawn_link return只是一个 pid。

经过这 3 次更改后的最终代码:

defmodule MyModule.Heartbeat do
  def start_link do
    pid = spawn_link(fn ->
      :timer.apply_interval(:timer.seconds(5), __MODULE__, :beat, [])
      :timer.sleep(:infinity)
    end)
    {:ok, pid}
  end

  def beat do
    IO.puts "beat"
  end
end

defmodule MyModule.Supervisor do
  use Supervisor

  def start_link do
    Supervisor.start_link(__MODULE__, :ok)
  end

  def init(:ok) do
     children = [
       worker(MyModule.Heartbeat, [])
     ]

    supervise(children, strategy: :one_for_one)
  end
end

输出:

iex(1)> MyModule.Supervisor.start_link
{:ok, #PID<0.84.0>}
iex(2)> beat
beat
beat
beat

我不确定你是否需要所有这些设置作为函数引发的异常 传递给 :timer.apply_interval 不影响调用过程。这 以下脚本不断尝试 运行 该函数,即使它总是引发一个 异常:

defmodule Beat do
  def beat do
    raise "hey"
  end
end

:timer.apply_interval(:timer.seconds(1), Beat, :beat, [])
:timer.sleep(:infinity)

输出:

$ elixir a.exs

16:00:43.207 [error] Process #PID<0.53.0> raised an exception
** (RuntimeError) hey
    a.exs:3: Beat.beat/0

16:00:44.193 [error] Process #PID<0.54.0> raised an exception
** (RuntimeError) hey
    a.exs:3: Beat.beat/0

16:00:45.193 [error] Process #PID<0.55.0> raised an exception
** (RuntimeError) hey
    a.exs:3: Beat.beat/0