如何安排代码在 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/2
与GenServer
一起使用比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 未实现