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?

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, [])

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


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!


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 

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


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.

Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.