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

Posted

技术标签:

【中文标题】如何安排代码在 Elixir 或 Phoenix 框架中每隔几个小时运行一次?【英文标题】:How can I schedule code to run every few hours in Elixir or Phoenix framework? 【发布时间】:2015-11-12 03:33:44 【问题描述】:

假设我想每 4 小时发送一堆电子邮件或重新创建站点地图或其他任何内容,我将如何在 Phoenix 或仅使用 Elixir 来执行此操作?

【问题讨论】:

【参考方案1】:

您可以为此使用erlcron。你使用它就像

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

:erlcron.cron(job)

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

希望有帮助!

【讨论】:

是的,总比没有好,谢谢。我会暂时不回答这个问题,也许会有其他建议 不客气!还有github.com/c-rack/quantum-elixir 这是一个长生不老药库,如果你愿意的话【参考方案2】:

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

defmodule MyApp.Periodically do
  use GenServer

  def start_link(_opts) 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

现在在您的监督树中:

children = [
  MyApp.Periodically
]

Supervisor.start_link(children, strategy: :one_for_one)

【讨论】:

不可能不喜欢这种语言:) 我应该把这个文件放在哪里?在 Phoenix 项目的 lib/ 目录下?测试去哪里,测试/定期/*? 在 lib 中,因为它是一个长时间运行的进程。您可以进行任何有意义的测试,可能是“test/my_app/periodically_test.exs”。 @CodyPoll :timer.send_interval 很好,但请记住,间隔将保持不变。所以想象一下,你想每分钟都做某事,而在未来,这项工作本身需要一分钟多的时间。在这种情况下,您将一直在工作,并且您的消息队列将无限增长。上述解决方案将始终等待工作完成后的给定时间。 @JoséValim — 这些天,MyApp.Periodically, [] 构造是否与 Elixir 中的 worker(MyApp.Periodically, []) 等效?试图找到文档来确认这一点,但今天早上遇到了麻烦。顺便说一句,绝对喜欢这个例子——非常有启发性。玩弄它并享受使用:continue 等的乐趣。【参考方案3】:

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

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

如果您的应用作为多个隔离实例(例如 Heroku)运行,则有由 PostgreSQL 或 Redis 支持的作业处理器,它们也支持任务调度:

欧班:https://github.com/sorentwo/oban

Exq:https://github.com/akira/exq

托尼克:https://github.com/joakimk/toniq

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

【讨论】:

我认为对于许多不需要它的简单任务来说这将是一种矫枉过正,但无论如何感谢您的回答。 拥有可用库列表对我很有帮助。【参考方案4】:

我使用了 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
]

一切就绪。通过运行以下命令启动服务器。

iex -S mix phoenix.server 

【讨论】:

这就像 cronjobs【参考方案5】:

除了使用Process.send_after,还可以使用:timer.apply_interval。

【讨论】:

【参考方案6】:

Quantum 很棒,我们在工作中用它作为 cron 的替代品和一个 phoenix 前端,我们还在real-time 中添加了工作,非常简洁。

【讨论】:

【参考方案7】:

我发现:timer.send_interval/2GenServer 一起使用比Process.send_after/4(在the accepted answer 中使用)更符合人体工程学。

:timer.send_interval/2 无需在每次处理通知时都重新安排通知时间,而是设置了一个让您无休止地接收消息的时间间隔 - 无需像接受的答案那样继续调用 schedule_work()

defmodule CountingServer do
  use GenServer

  def init(_) do
    :timer.send_interval(1000, :update)
    :ok, 1
  end

  def handle_info(:update, count) do
    IO.puts(count)
    :noreply, count + 1
  end
end

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

1
2
3
4
[etc.]

【讨论】:

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

以上是关于如何安排代码在 Elixir 或 Phoenix 框架中每隔几个小时运行一次?的主要内容,如果未能解决你的问题,请参考以下文章

从 Phoenix / Elixir GET 函数中调用 Typescript 函数

Phoenix/Elixir - 协议 Enumerable 未实现

在 Phoenix / Elixir 中启用跨域资源共享 CORS

sh Elixir Phoenix Cheatsheet

Elixir/Phoenix 日期从工作日 + 周数

GraphQL Elixir/Phoenix API:Socket 挂起,响应很大