Comment planifier le code pour qu'il s'exécute toutes les quelques heures dans le cadre Elixir ou Phoenix?

194

Alors disons que je veux envoyer un tas d'e-mails ou recréer un plan du site ou quoi que ce soit toutes les 4 heures, comment ferais-je à Phoenix ou tout simplement avec Elixir?

NoDisplayName
la source

Réponses:

387

Il existe une alternative simple qui ne nécessite aucune dépendance externe:

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

Maintenant dans votre arbre de supervision:

worker(MyApp.Periodically, [])
José Valim
la source
168
Il est impossible de ne pas aimer cette langue :)
NoDisplayName
3
Où dois-je mettre ce fichier? Sous le répertoire lib / du projet Phoenix? Où va le test, pour tester / périodiquement / *?
EugZol
9
En lib car c'est un long processus. Vous pouvez mettre le test comme bon vous semble, peut-être "test / my_app / periodically_test.exs".
José Valim
2
Une raison particulière de ne pas passer Process.send_afterà sa propre fonction pour que la fonction puisse être appelée à partir des deux initet handle_info?
Ryan Bigg
24
@CodyPoll :timer.send_intervalva bien, mais gardez à l'esprit que les intervalles seront constants. Imaginez donc que vous voulez faire quelque chose à chaque minute et, à l'avenir, le travail lui-même prendra plus d'une minute. Dans de tels cas, vous travailleriez tout le temps et votre file d'attente de messages augmenterait sans limite. La solution ci-dessus attendra toujours la période donnée une fois le travail terminé.
José Valim
32

Quantum vous permet de créer, rechercher et supprimer des travaux lors de l'exécution.

De plus, vous pouvez passer des arguments à la fonction de tâche lors de la création d'un cronjob et même modifier le fuseau horaire si vous n'êtes pas satisfait de l'UTC.

Si votre application s'exécute en tant que plusieurs instances isolées (par exemple Heroku), il existe des processeurs de travaux soutenus par PostgreSQL ou Redis, qui prennent également en charge la planification des tâches:

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
la source
1
Je pense que ce sera une surpuissance pour beaucoup de tâches simples qui ne l'exigent pas mais merci pour la réponse quand même.
NoDisplayName
Avoir une liste de bibliothèques disponibles m'a été utile.
sheldonkreger
24

Vous pouvez utiliser erlcron pour cela. Vous l'utilisez comme

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

:erlcron.cron(job)

A jobest un tuple à 2 éléments. Le premier élément est un tuple qui représente la planification du travail et le deuxième élément est la fonction ou un MFA (Module, Function, Arity). Dans l'exemple ci-dessus, nous courons :io.fwrite("It's 2 Thursday morning")tous les 2h du jeudi.

J'espère que cela pourra aider!

Gjaldon
la source
Ouais c'est mieux que rien, merci. Je laisserai la question sans réponse pendant un certain temps, peut-être y aura-t-il d'autres suggestions
NoDisplayName
4
Vous êtes les bienvenus! Il y a aussi github.com/c-rack/quantum-elixir qui est une bibliothèque d'élixir, si vous préférez
Gjaldon
6

J'ai utilisé la bibliothèque Quantum Quantum- Elixir .
Suivez les instructions ci-dessous.

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

Tout est prêt. Démarrez le serveur en exécutant la commande ci-dessous.

iex -S mix phoenix.server 
Shashidhar Mayannavar
la source
C'est comme des cronjobs
DarckBlezzer
1

Je trouve un :timer.send_interval/2peu plus ergonomique à utiliser avec un GenServerque Process.send_after/4(utilisé dans la réponse acceptée ).

Au lieu d'avoir à replanifier votre notification chaque fois que vous la manipulez, :timer.send_interval/2configurez un intervalle pendant lequel vous recevez un message à l'infini - pas besoin de continuer à appeler schedule_work()comme la réponse acceptée l'utilise.

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

Toutes les 1000 ms (c.-à-d., Une fois par seconde), IntervalServer.handle_info/2sera appelé, imprimera le courant countet mettra à jour l'état ( count + 1) du GenServer , vous donnant une sortie comme:

1
2
3
4
[etc.]
s3cur3
la source
0

Outre l'utilisation Process.send_after, vous pouvez également utiliser : timer.apply_interval .

YongHao Hu
la source
0

Quantum est génial, nous l'utilisons au travail en remplacement de cron avec un frontal Phoenix et nous ajoutons également des tâches en temps réel, ce qui est très soigné.

merci
la source