如何在Elixir GenServer中实现每日凌晨12点定时任务
解决Phoenix LiveView中每日凌晨定时执行任务的GenServer实现问题
我有一个LiveView Web应用,希望每日凌晨12点(非工作时间)执行一段代码。已经配置了随Phoenix应用启动的GenServer,但服务器启动时间不固定,无法确定调度的时间范围。
我设想的解决方案是:Phoenix应用启动时捕获当前日期并存储在状态中;GenServer每小时运行一次,检查日期是否变化,若变化则执行目标代码并更新状态,重复此流程。但我对GenServer了解不足,不知道如何按描述存储和比较日期值,甚至猜测可能需要用Agent传递数据。
我已经基于相关示例写了初步代码,但不知道怎么完善:
defmodule App.Periodically do use GenServer def start_link(_opts) do GenServer.start_link(__MODULE__, DateTime) end def init(state) do schedule_work(state) {:ok, state} end def handle_info(:work, state) do IO.inspect state.utc_now IO.inspect "_____________NEW" IO.inspect DateTime.utc_now # When phoenix app starts - store current date # When Genserver runs..... # run conditional on stored date with current date # If they are different THEN RUN CODE # If they are the same - do nothing IO.inspect state schedule_work(state) {:noreply, state} end defp schedule_work(state) do IO.inspect "test" Process.send_after(self(), :work, 10000 ) # 1 sec for testing will change to 1 hour end end
完善后的GenServer实现方案
你不需要额外使用Agent,GenServer本身就可以维护状态。下面是调整后的代码,完全实现你的需求:
defmodule App.Periodically do use GenServer # 启动GenServer,初始化状态为当天UTC日期 def start_link(_opts) do today = DateTime.utc_now() |> Date.truncate(:day) GenServer.start_link(__MODULE__, today, name: __MODULE__) end @impl true def init(today) do # 首次调度:设置1小时后执行(测试时可改短间隔) schedule_next_work() {:ok, today} end @impl true def handle_info(:execute_daily_task, last_executed_date) do now = DateTime.utc_now() today = Date.truncate(now, :day) # 检查日期是否更新,更新则执行任务并刷新状态 {new_state, _} = if Date.compare(today, last_executed_date) == :gt do # 替换为你要执行的实际代码 IO.puts("执行每日凌晨任务:#{now}") {today, :executed} else {last_executed_date, :skipped} end # 调度下一次检查(每小时执行一次) schedule_next_work() {:noreply, new_state} end # 设置下一次任务触发间隔:1小时(3600000毫秒) defp schedule_next_work do Process.send_after(self(), :execute_daily_task, 3600 * 1000) end end
关键细节说明
- 状态管理:GenServer的状态直接存储上一次执行任务的日期,无需额外依赖。启动时自动初始化状态为当天UTC日期。
- 日期对比逻辑:用
Date.truncate/2去除时间部分,只保留纯日期结构,避免时分秒干扰;通过Date.compare/2判断日期是否跨天。 - 调度可靠性:每小时触发一次检查,确保跨天后最多1小时内执行任务,完全覆盖服务器启动时间不固定的场景。
- 测试调整:若要快速验证逻辑,可将
schedule_next_work中的间隔改为10秒,观察日期变化时的执行情况。
集成到Phoenix应用
确保在lib/app/application.ex的children列表中加入该GenServer,让它随应用启动:
children = [ # ... 其他已有服务 App.Periodically ]
内容的提问来源于stack exchange,提问作者William
相关产品推荐
相关产品推荐

