Ketika saya baru mengenal Django, salah satu hal yang paling membuat frustrasi yang saya alami adalah kebutuhan untuk menjalankan sedikit kode secara berkala. Saya menulis fungsi bagus yang melakukan tindakan yang perlu dijalankan setiap hari pada jam 12 pagi. Mudah, bukan? Salah. Ini ternyata menjadi masalah besar bagi saya karena pada saat itu saya terbiasa dengan hosting web "tipe-Cpanel" di mana ada GUI praktis yang bagus untuk menyiapkan pekerjaan cron untuk tujuan ini.
Setelah banyak penelitian, saya menemukan solusi yang bagus—Celery, antrian pekerjaan asinkron yang kuat yang digunakan untuk menjalankan tugas di latar belakang. Tetapi ini menyebabkan masalah tambahan, karena saya tidak dapat menemukan satu set instruksi yang mudah untuk mengintegrasikan Celery ke dalam Proyek Django.
Tentu saja saya akhirnya berhasil menemukannya—yang akan dibahas dalam artikel ini:Bagaimana mengintegrasikan Seledri ke dalam Proyek Django dan membuat Tugas Berkala.
Bonus Gratis: Klik di sini untuk mendapatkan akses ke Panduan Sumber Daya Pembelajaran Django (PDF) gratis yang menunjukkan kepada Anda tip dan trik serta perangkap umum yang harus dihindari saat membangun aplikasi web Python + Django.
Proyek ini menggunakan Python 3.4, Django 1.8.2, Celery 3.1.18, dan Redis 3.0.2.
Ringkasan
Demi kenyamanan Anda, karena ini adalah pos yang sangat besar, silakan lihat kembali tabel ini untuk info singkat tentang setiap langkah dan untuk mengambil kode terkait.
Langkah | Ringkasan | Git Tag |
---|---|---|
Boilerplate | Unduh boilerplate | v1 |
Penyiapan | Mengintegrasikan Seledri dengan Django | v2 |
Tugas Seledri | Tambahkan Tugas Dasar Seledri | v3 |
Tugas Berkala | Tambahkan Tugas Berkala | v4 |
Berjalan Secara Lokal | Jalankan aplikasi kami secara lokal | v5 |
Berjalan dari Jarak Jauh | Jalankan aplikasi kami dari jarak jauh | v6 |
Apa itu Seledri?
“Celery adalah antrian tugas/antrian pekerjaan asinkron berdasarkan pengiriman pesan terdistribusi. Ini berfokus pada operasi waktu nyata, tetapi juga mendukung penjadwalan.” Untuk postingan kali ini, kami akan fokus pada fitur penjadwalan untuk menjalankan pekerjaan/tugas secara berkala.
Mengapa ini berguna?
- Pikirkan semua waktu yang Anda miliki untuk menjalankan tugas tertentu di masa mendatang. Mungkin Anda perlu mengakses API setiap jam. Atau mungkin Anda perlu mengirim banyak email di penghujung hari. Besar atau kecil, Seledri membuat penjadwalan tugas berkala seperti itu menjadi mudah.
- Anda tidak ingin pengguna akhir harus menunggu halaman yang tidak perlu dimuat atau tindakan selesai. Jika proses yang panjang merupakan bagian dari alur kerja aplikasi Anda, Anda dapat menggunakan Celery untuk menjalankan proses tersebut di latar belakang, saat sumber daya tersedia, sehingga aplikasi Anda dapat terus merespons permintaan klien. Ini menjauhkan tugas dari konteks aplikasi.
Penyiapan
Sebelum menyelam ke Seledri, ambil proyek awal dari repo Github. Pastikan untuk mengaktifkan virtualenv, menginstal persyaratan, dan menjalankan migrasi. Kemudian jalankan server dan navigasikan ke http://localhost:8000/ di browser Anda. Anda seharusnya melihat teks "Selamat atas halaman bertenaga Django pertama Anda" yang sudah dikenal. Setelah selesai, matikan server.
Selanjutnya, mari kita instal Seledri menggunakan pip:
$ pip install celery==3.1.18
$ pip freeze > requirements.txt
Sekarang kita dapat mengintegrasikan Seledri ke dalam Proyek Django kita hanya dalam tiga langkah mudah.
Langkah 1:Tambahkan seledri.py
Di dalam direktori “picha”, buat file baru bernama celery.py :
from __future__ import absolute_import
import os
from celery import Celery
from django.conf import settings
# set the default Django settings module for the 'celery' program.
os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'picha.settings')
app = Celery('picha')
# Using a string here means the worker will not have to
# pickle the object when using Windows.
app.config_from_object('django.conf:settings')
app.autodiscover_tasks(lambda: settings.INSTALLED_APPS)
@app.task(bind=True)
def debug_task(self):
print('Request: {0!r}'.format(self.request))
Perhatikan komentar dalam kode.
Langkah 2:Impor aplikasi Seledri baru Anda
Untuk memastikan bahwa aplikasi Seledri dimuat saat Django dimulai, tambahkan kode berikut ke dalam __init__.py file yang berada di sebelah settings.py your Anda berkas:
from __future__ import absolute_import
# This will make sure the app is always imported when
# Django starts so that shared_task will use this app.
from .celery import app as celery_app
Setelah melakukan itu, tata letak proyek Anda sekarang akan terlihat seperti:
├── manage.py
├── picha
│ ├── __init__.py
│ ├── celery.py
│ ├── settings.py
│ ├── urls.py
│ └── wsgi.py
└── requirements.txt
Langkah 3:Instal Redis sebagai “Broker” Seledri
Celery menggunakan "broker" untuk meneruskan pesan antara Proyek Django dan pekerja Seledri. Dalam tutorial ini, kita akan menggunakan Redis sebagai perantara pesan.
Pertama, instal Redis dari halaman unduhan resmi atau melalui brew (brew install redis
) dan kemudian beralih ke terminal Anda, di jendela terminal baru, jalankan server:
$ redis-server
Anda dapat menguji apakah Redis berfungsi dengan baik dengan mengetikkan ini ke terminal Anda:
$ redis-cli ping
Redis harus membalas dengan PONG
- coba!
Setelah Redis aktif, tambahkan kode berikut ke file settings.py Anda:
# CELERY STUFF
BROKER_URL = 'redis://localhost:6379'
CELERY_RESULT_BACKEND = 'redis://localhost:6379'
CELERY_ACCEPT_CONTENT = ['application/json']
CELERY_TASK_SERIALIZER = 'json'
CELERY_RESULT_SERIALIZER = 'json'
CELERY_TIMEZONE = 'Africa/Nairobi'
Anda juga perlu menambahkan Redis sebagai ketergantungan dalam Proyek Django:
$ pip install redis==2.10.3
$ pip freeze > requirements.txt
Itu dia! Anda sekarang seharusnya dapat menggunakan Seledri dengan Django. Untuk informasi lebih lanjut tentang pengaturan Seledri dengan Django, silakan lihat dokumentasi Seledri resmi.
Sebelum melanjutkan, mari kita lakukan beberapa pemeriksaan kewarasan untuk memastikan semuanya baik-baik saja…
Uji apakah pekerja Seledri siap menerima tugas:
$ celery -A picha worker -l info
...
[2015-07-07 14:07:07,398: INFO/MainProcess] Connected to redis://localhost:6379//
[2015-07-07 14:07:07,410: INFO/MainProcess] mingle: searching for neighbors
[2015-07-07 14:07:08,419: INFO/MainProcess] mingle: all alone
Matikan proses dengan CTRL-C. Sekarang, uji apakah penjadwal tugas Seledri siap beraksi:
$ celery -A picha beat -l info
...
[2015-07-07 14:08:23,054: INFO/MainProcess] beat: Starting...
Boom!
Sekali lagi, matikan proses setelah selesai.
Tugas Seledri
Seledri menggunakan tugas, yang dapat dianggap sebagai fungsi Python biasa yang disebut dengan Seledri.
Misalnya, mari kita ubah fungsi dasar ini menjadi tugas Seledri:
def add(x, y):
return x + y
Pertama, tambahkan dekorator:
from celery.decorators import task
@task(name="sum_two_numbers")
def add(x, y):
return x + y
Kemudian Anda dapat menjalankan tugas ini secara asinkron dengan Seledri seperti:
add.delay(7, 8)
Sederhana, bukan?
Jadi, jenis tugas ini sangat cocok ketika Anda ingin memuat halaman web tanpa membuat pengguna menunggu beberapa proses latar belakang selesai.
Mari kita lihat contohnya…
Kembali ke Proyek Django, ambil versi tiga, yang menyertakan aplikasi yang menerima umpan balik dari pengguna, dengan tepat disebut feedback
:
├── feedback
│ ├── __init__.py
│ ├── admin.py
│ ├── emails.py
│ ├── forms.py
│ ├── models.py
│ ├── tests.py
│ └── views.py
├── manage.py
├── picha
│ ├── __init__.py
│ ├── celery.py
│ ├── settings.py
│ ├── urls.py
│ └── wsgi.py
├── requirements.txt
└── templates
├── base.html
└── feedback
├── contact.html
└── email
├── feedback_email_body.txt
└── feedback_email_subject.txt
Instal persyaratan baru, jalankan aplikasi, dan navigasikan ke http://localhost:8000/feedback/. Anda akan melihat:
Mari selesaikan tugas Seledri.
Tambahkan Tugas
Pada dasarnya, setelah pengguna mengirimkan formulir umpan balik, kami ingin segera membiarkannya melanjutkan perjalanannya yang menyenangkan sementara kami memproses umpan balik, mengirim email, dll., semuanya di latar belakang.
Untuk melakukannya, pertama-tama tambahkan file bernama tasks.py ke direktori “umpan balik”:
from celery.decorators import task
from celery.utils.log import get_task_logger
from feedback.emails import send_feedback_email
logger = get_task_logger(__name__)
@task(name="send_feedback_email_task")
def send_feedback_email_task(email, message):
"""sends an email when feedback form is filled successfully"""
logger.info("Sent feedback email")
return send_feedback_email(email, message)
Kemudian perbarui forms.py seperti ini:
from django import forms
from feedback.tasks import send_feedback_email_task
class FeedbackForm(forms.Form):
email = forms.EmailField(label="Email Address")
message = forms.CharField(
label="Message", widget=forms.Textarea(attrs={'rows': 5}))
honeypot = forms.CharField(widget=forms.HiddenInput(), required=False)
def send_email(self):
# try to trick spammers by checking whether the honeypot field is
# filled in; not super complicated/effective but it works
if self.cleaned_data['honeypot']:
return False
send_feedback_email_task.delay(
self.cleaned_data['email'], self.cleaned_data['message'])
Intinya, send_feedback_email_task.delay(email, message)
fungsi memproses dan mengirimkan email umpan balik di latar belakang saat pengguna terus menggunakan situs.
CATATAN :
success_url
di views.py diatur untuk mengarahkan pengguna ke/
, yang belum ada. Kami akan mengatur titik akhir ini di bagian berikutnya.
Tugas Berkala
Seringkali, Anda perlu menjadwalkan tugas untuk dijalankan pada waktu tertentu sesering mungkin - misalnya, web scraper mungkin perlu dijalankan setiap hari. Tugas seperti itu, yang disebut tugas periodik, mudah diatur dengan Seledri.
Seledri menggunakan "ketukan seledri" untuk menjadwalkan tugas berkala. Celery beat menjalankan tugas secara berkala, yang kemudian dieksekusi oleh pekerja seledri.
Misalnya, tugas berikut dijadwalkan untuk dijalankan setiap lima belas menit:
from celery.task.schedules import crontab
from celery.decorators import periodic_task
@periodic_task(run_every=(crontab(minute='*/15')), name="some_task", ignore_result=True)
def some_task():
# do something
Mari kita lihat contoh yang lebih kuat dengan menambahkan fungsionalitas ini ke dalam Proyek Django…
Kembali ke Proyek Django, ambil versi empat, yang menyertakan aplikasi baru lainnya, yang disebut photos
, yang menggunakan Flickr API untuk mendapatkan foto baru untuk ditampilkan di situs:
├── feedback
│ ├── __init__.py
│ ├── admin.py
│ ├── emails.py
│ ├── forms.py
│ ├── models.py
│ ├── tasks.py
│ ├── tests.py
│ └── views.py
├── manage.py
├── photos
│ ├── __init__.py
│ ├── admin.py
│ ├── models.py
│ ├── settings.py
│ ├── tests.py
│ ├── utils.py
│ └── views.py
├── picha
│ ├── __init__.py
│ ├── celery.py
│ ├── settings.py
│ ├── urls.py
│ └── wsgi.py
├── requirements.txt
└── templates
├── base.html
├── feedback
│ ├── contact.html
│ └── email
│ ├── feedback_email_body.txt
│ └── feedback_email_subject.txt
└── photos
└── photo_list.html
Instal persyaratan baru, jalankan migrasi, lalu jalankan server untuk memastikan semuanya baik-baik saja. Coba uji formulir umpan balik lagi. Kali ini seharusnya mengarahkan ulang dengan baik.
Apa selanjutnya?
Karena kita perlu memanggil Flickr API secara berkala untuk menambahkan lebih banyak foto ke situs kita, kita dapat menambahkan tugas Seledri.
Tambahkan Tugas
Tambahkan tasks.py ke photos
aplikasi:
from celery.task.schedules import crontab
from celery.decorators import periodic_task
from celery.utils.log import get_task_logger
from photos.utils import save_latest_flickr_image
logger = get_task_logger(__name__)
@periodic_task(
run_every=(crontab(minute='*/15')),
name="task_save_latest_flickr_image",
ignore_result=True
)
def task_save_latest_flickr_image():
"""
Saves latest image from Flickr
"""
save_latest_flickr_image()
logger.info("Saved image from Flickr")
Di sini, kami menjalankan save_latest_flickr_image()
berfungsi setiap lima belas menit dengan membungkus panggilan fungsi dalam task
. @periodic_task
dekorator mengabstraksi kode untuk menjalankan tugas Seledri, meninggalkan tasks.py file bersih dan mudah dibaca!
Berjalan Secara Lokal
Siap menjalankan hal ini?
Dengan Aplikasi Django dan Redis Anda berjalan, buka dua jendela/tab terminal baru. Di setiap jendela baru, navigasikan ke direktori proyek Anda, aktifkan virtualenv Anda, lalu jalankan perintah berikut (satu di setiap jendela):
$ celery -A picha worker -l info
$ celery -A picha beat -l info
Saat Anda mengunjungi situs di http://127.0.0.1:8000/ Anda sekarang akan melihat satu gambar. Aplikasi kami mendapatkan satu gambar dari Flickr setiap 15 menit:
Lihat photos/tasks.py
untuk melihat kode. Mengeklik tombol “Umpan Balik” memungkinkan Anda untuk… mengirim umpan balik:
Ini bekerja melalui tugas seledri. Lihat feedback/tasks.py
untuk lebih.
Itu saja, Anda sudah menjalankan dan menjalankan proyek Picha!
Ini bagus untuk pengujian saat mengembangkan Proyek Django Anda secara lokal, tetapi tidak bekerja dengan baik ketika Anda perlu menyebarkan ke produksi - seperti di DigitalOcean, mungkin. Untuk itu, Anda disarankan untuk menjalankan pekerja Celery dan penjadwal di latar belakang sebagai daemon dengan Supervisor.
Berjalan dari Jarak Jauh
Instalasi sederhana. Ambil versi lima dari repo (jika Anda belum memilikinya). Kemudian SSH ke server jarak jauh Anda dan jalankan:
$ sudo apt-get install supervisor
Kami kemudian perlu memberi tahu Supervisor tentang pekerja Celery kami dengan menambahkan file konfigurasi ke direktori “/etc/supervisor/conf.d/” di server jauh. Dalam kasus kami, kami memerlukan dua file konfigurasi seperti itu - satu untuk pekerja Seledri dan satu untuk penjadwal Seledri.
Secara lokal, buat folder bernama "supervisor" di root proyek. Kemudian tambahkan file berikut…
Pekerja Seledri:picha_celery.conf
; ==================================
; celery worker supervisor example
; ==================================
; the name of your supervisord program
[program:pichacelery]
; Set full path to celery program if using virtualenv
command=/home/mosh/.virtualenvs/picha/bin/celery worker -A picha --loglevel=INFO
; The directory to your Django project
directory=/home/mosh/sites/picha
; If supervisord is run as the root user, switch users to this UNIX user account
; before doing any processing.
user=mosh
; Supervisor will start as many instances of this program as named by numprocs
numprocs=1
; Put process stdout output in this file
stdout_logfile=/var/log/celery/picha_worker.log
; Put process stderr output in this file
stderr_logfile=/var/log/celery/picha_worker.log
; If true, this program will start automatically when supervisord is started
autostart=true
; May be one of false, unexpected, or true. If false, the process will never
; be autorestarted. If unexpected, the process will be restart when the program
; exits with an exit code that is not one of the exit codes associated with this
; process’ configuration (see exitcodes). If true, the process will be
; unconditionally restarted when it exits, without regard to its exit code.
autorestart=true
; The total number of seconds which the program needs to stay running after
; a startup to consider the start successful.
startsecs=10
; Need to wait for currently executing tasks to finish at shutdown.
; Increase this if you have very long running tasks.
stopwaitsecs = 600
; When resorting to send SIGKILL to the program to terminate it
; send SIGKILL to its whole process group instead,
; taking care of its children as well.
killasgroup=true
; if your broker is supervised, set its priority higher
; so it starts first
priority=998
Penjadwal Seledri:picha_celerybeat.conf
; ================================
; celery beat supervisor example
; ================================
; the name of your supervisord program
[program:pichacelerybeat]
; Set full path to celery program if using virtualenv
command=/home/mosh/.virtualenvs/picha/bin/celerybeat -A picha --loglevel=INFO
; The directory to your Django project
directory=/home/mosh/sites/picha
; If supervisord is run as the root user, switch users to this UNIX user account
; before doing any processing.
user=mosh
; Supervisor will start as many instances of this program as named by numprocs
numprocs=1
; Put process stdout output in this file
stdout_logfile=/var/log/celery/picha_beat.log
; Put process stderr output in this file
stderr_logfile=/var/log/celery/picha_beat.log
; If true, this program will start automatically when supervisord is started
autostart=true
; May be one of false, unexpected, or true. If false, the process will never
; be autorestarted. If unexpected, the process will be restart when the program
; exits with an exit code that is not one of the exit codes associated with this
; process’ configuration (see exitcodes). If true, the process will be
; unconditionally restarted when it exits, without regard to its exit code.
autorestart=true
; The total number of seconds which the program needs to stay running after
; a startup to consider the start successful.
startsecs=10
; if your broker is supervised, set its priority higher
; so it starts first
priority=999
Pastikan untuk memperbarui jalur dalam file ini agar sesuai dengan sistem file server jauh.
Pada dasarnya, file konfigurasi supervisor ini memberi tahu supervisor cara menjalankan dan mengelola 'program' kami (seperti yang disebut oleh supervisord).
Pada contoh di atas, kami telah membuat dua program pengawas bernama “pichacelery” dan “pichacelerybeat”.
Sekarang salin saja file-file ini ke server jauh di direktori “/etc/supervisor/conf.d/”.
Kita juga perlu membuat file log yang disebutkan dalam skrip di atas di server jauh:
$ touch /var/log/celery/picha_worker.log
$ touch /var/log/celery/picha_beat.log
Terakhir, jalankan perintah berikut untuk membuat Supervisor mengetahui program - mis., pichacelery
dan pichacelerybeat
:
$ sudo supervisorctl reread
$ sudo supervisorctl update
Jalankan perintah berikut untuk menghentikan, memulai, dan/atau memeriksa status pichacelery
program:
$ sudo supervisorctl stop pichacelery
$ sudo supervisorctl start pichacelery
$ sudo supervisorctl status pichacelery
Anda dapat membaca lebih lanjut tentang Supervisor dari dokumentasi resmi.
Kiat Terakhir
- Jangan meneruskan objek model Django ke tugas Seledri. Untuk menghindari kasus di mana objek model telah berubah sebelum diteruskan ke tugas Seledri, berikan kunci utama objek ke Seledri. Anda kemudian, tentu saja, harus menggunakan kunci utama untuk mendapatkan objek dari database sebelum mengerjakannya.
- Penjadwal Seledri default membuat beberapa file untuk menyimpan jadwalnya secara lokal. File-file ini akan menjadi "celerybeat-schedule.db" dan "celerybeat.pid". Jika Anda menggunakan sistem kontrol versi seperti Git (yang seharusnya Anda lakukan!), sebaiknya abaikan file ini dan jangan tambahkan ke repositori Anda karena file tersebut untuk menjalankan proses secara lokal.
Langkah selanjutnya
Nah, itu saja untuk pengenalan dasar untuk mengintegrasikan Seledri ke dalam Proyek Django.
Ingin lebih?
- Selami Panduan Pengguna Seledri resmi untuk mempelajari lebih lanjut.
- Buat Fabfile untuk mengatur Supervisor dan file konfigurasi. Pastikan untuk menambahkan perintah ke
reread
danupdate
Pengawas. - Fork Proyek dari repo dan buka Pull Request untuk menambahkan tugas Seledri baru.
Bonus Gratis: Klik di sini untuk mendapatkan akses ke Panduan Sumber Daya Pembelajaran Django (PDF) gratis yang menunjukkan kepada Anda tip dan trik serta perangkap umum yang harus dihindari saat membangun aplikasi web Python + Django.
Selamat membuat kode!