如何在Elixir或Phoenix框架中安排代码每隔几个小时运行一次?

NoD*_*ame 176 elixir phoenix-framework

所以,假设我想发送一堆电子邮件或重新创建站点地图或者每4小时一次,我将如何在凤凰城或只是使用Elixir?

Jos*_*lim 355

有一个简单的替代方案,不需要任何外部依赖:

defmodule MyApp.Periodically do
  use GenServer

  def start_link do
    GenServer.start_link(__MODULE__, %{})
  end

  def init(state) do
    schedule_work() # Schedule work to be performed at some point
    {:ok, state}
  end

  def handle_info(:work, state) do
    # Do the work you desire here
    schedule_work() # Reschedule once more
    {:noreply, state}
  end

  defp schedule_work() do
    Process.send_after(self(), :work, 2 * 60 * 60 * 1000) # In 2 hours
  end
end
Run Code Online (Sandbox Code Playgroud)

现在在你的监督树上:

worker(MyApp.Periodically, [])
Run Code Online (Sandbox Code Playgroud)

  • 不爱这种语言是不可能的:) (156认同)
  • @CodyPoll`:timer.send_interval`很好,但请记住,间隔将保持不变.所以想象你想要每分钟都做一些事情,将来,工作本身需要一分多钟.在这种情况下,您将一直在工作,您的消息队列将无限增长.上述解决方案将始终等待*工作完成后的给定时间段*. (22认同)
  • 在lib中,因为它是一个长时间运行的过程.您可以将测试放在任何有意义的地方,也许是"test/my_app/periodic_test.exs". (8认同)
  • @JoséValim — 如今, `{MyApp.Periodically, []}` 构造是否与 Elixir 中的 `worker(MyApp.Periodically, [])` 等效?试图找到文档来确认这一点,但今天早上遇到了麻烦。顺便说一句,我非常喜欢这个例子——非常有启发性。尝试使用它并享受使用 `:continue` 等的乐趣。 (4认同)
  • 我应该把这个文件放在哪里?在凤凰项目的lib /目录下?测试去哪里,测试/定期/*? (3认同)
  • 任何特殊原因都不能将`Process.send_after`移动到它自己的函数中,以便可以从`init`和`handle_info`调用该函数? (2认同)
  • 仅使用GenServer的缺点是,如果应用程序崩溃,则所有计划的作业都会丢失。这就是为什么有这么多库处理此问题的原因-大多数时候,您需要弹性来避免丢失计划的任务。 (2认同)
  • 如果有人像我一样卡住了,请不要在你的间隔数学中使用浮点数.我用的是`0.25`.这会从`:erlang.send_after`产生一个参数错误.错误消息以不同的顺序显示`:erlang.send_after`的参数,而不是给'Process.send_after`,这让我暂时离开了. (2认同)
  • 它也高度依赖于任务。如果大多数任务最终连续运行两次,则不会导致问题。 (2认同)

Svi*_*len 29

Quantum允许您在运行时创建,查找和删除作业.

此外,您可以在创建cronjob时将参数传递给任务函数,如果您对UTC不满意,甚至可以修改时区:

https://github.com/c-rack/quantum-elixir

如果您的应用程序作为多个独立实例(例如Heroku)运行,则有Redis支持的作业处理器,它还支持任务调度:

例如:https://github.com/akira/exq

Toniq:https://github.com/joakimk/toniq

Verk:https://github.com/edgurgel/verk


Gja*_*don 23

你可以使用erlcron.你喜欢它

job = {{:weekly, :thu, {2, :am}},
  {:io, :fwrite, ["It's 2 Thursday morning~n"]}}

:erlcron.cron(job)
Run Code Online (Sandbox Code Playgroud)

A job是一个2元素元组.第一个元素是表示作业计划的元组,第二个元素是函数或MFA(模块,函数,Arity).在上面的例子中,我们:io.fwrite("It's 2 Thursday morning")每周四凌晨2点运行.

希望有所帮助!

  • 别客气!还有https://github.com/c-rack/quantum-elixir这是一个灵丹妙药,如果你愿意的话 (4认同)

小智 6

我使用了Quantum库Quantum- Elixir.
请遵循以下说明.

#your_app/mix.exs
defp deps do
  [{:quantum, ">= 1.9.1"},  
  #rest code
end



#your_app/mix.exs
def application do
  [mod: {AppName, []},
   applications: [:quantum,
   #rest code         
 ]]
end

#your_app/config/dev.exs
config :quantum, :your_app, cron: [
  # Every minute
  "* * * * *": fn -> IO.puts("Hello QUANTUM!") end
]
Run Code Online (Sandbox Code Playgroud)

搞定.通过运行以下命令启动服务器.

iex -S mix phoenix.server 
Run Code Online (Sandbox Code Playgroud)


s3c*_*ur3 6

我发现:timer.send_interval/2使用GenServerProcess.send_after/4(在接受的答案中使用)更符合人体工程学。

\n\n

您不必每次处理通知时都重新安排通知,:timer.send_interval/2而是设置一个不断收到消息的时间间隔\xe2\x80\x94,无需schedule_work()像接受的答案那样不断打电话。

\n\n
defmodule CountingServer do\n  use GenServer\n\n  def init(_) do\n    :timer.send_interval(1000, :update)\n    {:ok, 1}\n  end\n\n  def handle_info(:update, count) do\n    IO.puts(count)\n    {:noreply, count + 1}\n  end\nend\n
Run Code Online (Sandbox Code Playgroud)\n\n

每 1000 毫秒(即每秒一次),IntervalServer.handle_info/2将被调用,打印当前的count,并更新 GenServer 的状态 ( count + 1),给出如下输出:

\n\n
1\n2\n3\n4\n[etc.]\n
Run Code Online (Sandbox Code Playgroud)\n

  • 请记住,如果您的时间间隔小于完成任务所需的时间,则此解决方案可能会导致队列溢出。 (4认同)