Bagaimana saya bisa menjadwalkan kode untuk dijalankan setiap beberapa jam dalam kerangka Elixir atau Phoenix?

194

Jadi katakanlah saya ingin mengirim banyak email atau membuat ulang sitemap atau apa pun setiap 4 jam, bagaimana saya melakukannya di Phoenix atau hanya dengan Elixir?

NoDisplayName
sumber

Jawaban:

387

Ada alternatif sederhana yang tidak memerlukan ketergantungan eksternal:

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

Sekarang di pohon pengawasan Anda:

worker(MyApp.Periodically, [])
José Valim
sumber
168
Mustahil untuk tidak menyukai bahasa ini :)
NoDisplayName
3
Di mana saya harus meletakkan file ini? Di bawah lib / direktori proyek Phoenix? Kemana perginya tes, untuk menguji / secara berkala / *?
EugZol
9
Di lib karena itu adalah proses yang berjalan lama. Anda dapat menguji apa pun yang masuk akal, mungkin "test / my_app / periodically_test.exs".
José Valim
2
Adakah alasan khusus untuk tidak pindah Process.send_afterke fungsinya sendiri sehingga fungsinya dapat dipanggil dari keduanya initdan handle_info?
Ryan Bigg
24
@CodyPoll :timer.send_intervalbaik-baik saja tetapi perlu diingat bahwa interval akan konstan. Jadi bayangkan Anda ingin melakukan sesuatu setiap menit dan, di masa depan, pekerjaan itu sendiri membutuhkan lebih dari satu menit. Dalam kasus seperti itu, Anda akan bekerja sepanjang waktu dan antrian pesan Anda akan menjadi tidak terbatas. Solusi di atas akan selalu menunggu periode yang diberikan setelah pekerjaan selesai.
José Valim
32

Quantum memungkinkan Anda membuat, menemukan, dan menghapus pekerjaan saat runtime.

Selain itu, Anda dapat meneruskan argumen ke fungsi tugas saat membuat tugas cronjob, dan bahkan memodifikasi zona waktu jika Anda tidak puas dengan UTC.

Jika aplikasi Anda berjalan sebagai beberapa instance yang terisolasi (mis. Heroku), ada pemroses pekerjaan yang didukung oleh PostgreSQL atau Redis, yang juga mendukung penjadwalan tugas:

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
sumber
1
Saya pikir itu akan menjadi kerja keras untuk banyak tugas sederhana yang tidak memerlukannya tetapi terima kasih atas jawabannya.
NoDisplayName
Memiliki daftar perpustakaan yang tersedia sangat membantu saya.
sheldonkreger
24

Anda dapat menggunakan erlcron untuk itu. Anda menggunakannya seperti

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

:erlcron.cron(job)

A jobadalah tuple 2 elemen. Elemen pertama adalah tupel yang mewakili jadwal untuk pekerjaan dan elemen kedua adalah fungsi atau MFA (Modul, Fungsi, Arity). Dalam contoh di atas, kita jalankan:io.fwrite("It's 2 Thursday morning") setiap 2 Kamis.

Semoga itu bisa membantu!

Gjaldon
sumber
Ya itu lebih baik daripada tidak sama sekali, terima kasih. Saya akan meninggalkan pertanyaan untuk sementara waktu, mungkin akan ada saran lain
NoDisplayName
4
Sama-sama! Ada juga github.com/c-rack/quantum-elixir yang merupakan lib elixir, jika Anda suka
Gjaldon
6

Saya menggunakan perpustakaan Quantum Quantum- Elixir .
Ikuti instruksi di bawah ini.

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

Siap. Mulai server dengan menjalankan perintah di bawah ini.

iex -S mix phoenix.server 
Shashidhar Mayannavar
sumber
Ini seperti cronjobs
DarckBlezzer
1

Saya menemukan :timer.send_interval/2sedikit lebih ergonomis untuk digunakan dengan GenServerdari Process.send_after/4(digunakan dalam jawaban yang diterima ).

Alih-alih harus menjadwal ulang notifikasi Anda setiap kali Anda menanganinya, :timer.send_interval/2tentukan interval di mana Anda menerima pesan tanpa henti — tidak perlu terus menelepon schedule_work()seperti yang digunakan jawaban yang diterima.

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

Setiap 1000 ms (yaitu, satu detik), IntervalServer.handle_info/2akan dipanggil, cetak saat ini count, dan perbarui status GenServer ( count + 1), memberikan Anda output seperti:

1
2
3
4
[etc.]
s3cur3
sumber
0

Selain untuk digunakan Process.send_after, Anda juga dapat menggunakan : timer.apply_interval .

YongHao Hu
sumber
0

Quantum hebat, kami menggunakannya di tempat kerja sebagai pengganti cron dengan front-end phoenix dan kami juga menambahkan pekerjaan secara real-time yang sangat rapi.

thanos
sumber