Como posso agendar código para execução a cada poucas horas no framework Elixir ou Phoenix?

194

Então, digamos que eu quero enviar um monte de e-mails ou recriar o mapa do site ou o que quer que seja a cada 4 horas, como eu faria isso em Phoenix ou apenas com o Elixir?

NoDisplayName
fonte

Respostas:

386

Existe uma alternativa simples que não requer nenhuma dependência externa:

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

Agora na sua árvore de supervisão:

worker(MyApp.Periodically, [])
José Valim
fonte
167
É impossível não amar essa linguagem :)
NoDisplayName
3
Onde devo colocar esse arquivo? No diretório lib / do projeto Phoenix? Para onde vai o teste, para testar / periodicamente / *?
21915 EugZol
9
Na lib porque é um processo demorado. Você pode fazer o teste o que fizer sentido, talvez "test / my_app / periodically_test.exs".
José Valim
2
Alguma razão específica para não passar Process.send_afterpara sua própria função, de modo que a função possa ser chamada de ambos inite handle_info?
Ryan Bigg
24
O @CodyPoll :timer.send_intervalestá bom, mas tenha em mente que os intervalos serão constantes. Imagine que você quer fazer algo a cada minuto e, no futuro, o trabalho em si leva mais de um minuto. Nesses casos, você estaria trabalhando o tempo todo e sua fila de mensagens aumentaria sem limites. A solução acima sempre aguardará o período especificado após o término do trabalho.
José Valim
32

O Quantum permite criar, localizar e excluir trabalhos em tempo de execução.

Além disso, você pode passar argumentos para a função task ao criar um cronjob e até modificar o fuso horário se não estiver satisfeito com o UTC.

Se seu aplicativo estiver sendo executado como várias instâncias isoladas (por exemplo, Heroku), existem processadores de tarefas suportados pelo PostgreSQL ou Redis, que também suportam o agendamento de tarefas:

Oban: https://github.com/sorentwo/oban

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

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

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

Svilen
fonte
1
Eu acho que será um exagero para muitas tarefas simples que não exigem isso, mas obrigado pela resposta de qualquer maneira.
NoDisplayName
Ter uma lista de bibliotecas disponíveis foi útil para mim.
sheldonkreger 3/09/16
24

Você pode usar erlcron para isso. Você usa como

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

:erlcron.cron(job)

A jobé uma tupla de 2 elementos. O primeiro elemento é uma tupla que representa a programação do trabalho e o segundo elemento é a função ou um MFA (Módulo, Função, Arity). No exemplo acima, executamos a :io.fwrite("It's 2 Thursday morning")cada 2 da manhã de quinta-feira.

Espero que ajude!

Gjaldon
fonte
Sim, é melhor que nada, obrigado. Deixarei a pergunta sem resposta por um tempo, talvez haja outras sugestões
NoDisplayName
4
De nada! Há também github.com/c-rack/quantum-elixir que é um lib elixir, se você preferir
Gjaldon
6

Eu usei a biblioteca Quantum Quantum- Elixir .
Siga as instruções abaixo.

#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
]

Tudo pronto. Inicie o servidor executando o comando abaixo.

iex -S mix phoenix.server 
Shashidhar Mayannavar
fonte
Isto é como cronjobs
DarckBlezzer
1

Acho :timer.send_interval/2um pouco mais ergonômico para usar com a GenServerque Process.send_after/4(usado na resposta aceita ).

Em vez de ter que reagendar sua notificação cada vez que você a manipula, :timer.send_interval/2configure um intervalo no qual você recebe uma mensagem sem parar - não é necessário continuar ligando schedule_work()como a resposta aceita.

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

A cada 1000 ms (ou seja, uma vez por segundo), IntervalServer.handle_info/2será chamado, imprima a corrente counte atualize o estado do GenServer ( count + 1), fornecendo uma saída como:

1
2
3
4
[etc.]
s3cur3
fonte
0

Além de usar Process.send_after, você também pode usar : timer.apply_interval .

YongHao Hu
fonte
0

O Quantum é ótimo, usamos no trabalho como uma substituição do cron com um front-end da Phoenix e também adicionamos trabalhos em tempo real, o que é muito interessante.

thanos
fonte