1

我正在尝试生成一个进程,该进程将每五秒发布一个 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>

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

谁能建议我在这里做错了什么阻止了这个过程的开始,是否有更好的方法来实现这一点?

4

1 回答 1

1

您的代码中有 3 个错误:

  1. :timer.apply_interval/4立即返回,因此传递给spawn_linkin的匿名函数MyModule.Heartbeat.start_link/0在执行该行后很快终止,导致:timer从其队列中删除间隔,并且MyModule.Heartbeat.beat/0永远不会调用。

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

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

  3. MyModule.Heartbeat.start_link/0需要返回{:ok, pid}成功。 spawn_link只返回一个 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
于 2016-07-22T10:30:05.867 回答