Как я могу запланировать запуск кода каждые несколько часов в среде Elixir или Phoenix?

194

Допустим, я хочу отправлять кучу электронных писем или воссоздавать карту сайта или что-то еще каждые 4 часа, как бы я делал это в Фениксе или только с Elixir?

NoDisplayName
источник

Ответы:

390

Существует простая альтернатива, которая не требует каких-либо внешних зависимостей:

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

Теперь в вашем дереве наблюдения:

worker(MyApp.Periodically, [])
Хосе Валим
источник
170
Невозможно не любить этот язык :)
NoDisplayName
3
Где я должен положить этот файл? В каталоге lib / проекта Phoenix? Куда идут тесты, чтобы тестировать / периодически / *?
EugZol
9
В lib, потому что это длительный процесс. Вы можете поставить тест на то, что имеет смысл, например, «test / my_app / periodically_test.exs».
Хосе Валим
2
Любая конкретная причина не переходить Process.send_afterв свою собственную функцию, чтобы функция могла быть вызвана как из, так initи из handle_info?
Райан Бигг
24
@CodyPoll :timer.send_intervalэто хорошо, но имейте в виду, что интервалы будут постоянными. Итак, представьте, что вы хотите что-то делать каждую минуту, и в будущем сама работа займет больше минуты. В таких случаях вы будете работать все время, и ваша очередь сообщений станет неограниченной. Решение выше всегда будет ждать заданный период после выполнения работы.
Хосе Валим
33

Quantum позволяет создавать, находить и удалять задания во время выполнения.

Кроме того, вы можете передавать аргументы в функцию задачи при создании cronjob и даже изменять часовой пояс, если вас не устраивает UTC.

Если ваше приложение работает как несколько изолированных экземпляров (например, Heroku), существуют процессоры заданий, поддерживаемые PostgreSQL или Redis, которые также поддерживают планирование задач:

Обан: https://github.com/sorentwo/oban

Пример: https://github.com/akira/exq

Тоник: https://github.com/joakimk/toniq

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

Svilen
источник
1
Я думаю, что это будет излишним для многих простых задач, которые не требуют этого, но все равно спасибо за ответ.
NoDisplayName
Наличие списка доступных библиотек было для меня полезным.
Шелдонкрегер
25

Вы можете использовать erlcron для этого. Вы используете это как

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

:erlcron.cron(job)

A job- это двухэлементный кортеж. Первый элемент - это кортеж, который представляет расписание для задания, а второй элемент - это функция или MFA (Module, Function, Arity). В приведенном выше примере мы запускаем :io.fwrite("It's 2 Thursday morning")каждые 2 часа ночи четверга.

Надеюсь, это поможет!

Gjaldon
источник
Да, это лучше, чем ничего, спасибо. Я оставлю вопрос без ответа на некоторое время, возможно, будут другие предложения
NoDisplayName
4
Пожалуйста! Есть также github.com/c-rack/quantum-elixir, который является
библиотекой
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
]

Все готово. Запустите сервер, выполнив следующую команду.

iex -S mix phoenix.server 
Шашидхар Майаннавар
источник
Это как cronjobs
DarckBlezzer
1

Я нахожу :timer.send_interval/2немного более эргономичным для использования с GenServerчем Process.send_after/4(используется в принятом ответе ).

Вместо того, чтобы перепланировать уведомление каждый раз, когда вы его обрабатываете, :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.]
s3cur3
источник
0

Quantum - это здорово, мы используем его на работе как замену cron на front-end феникса, а также добавляем задания в реальном времени, что очень удобно.

Танос
источник