Khi tôi mới làm quen với Django, một trong những điều khó chịu nhất mà tôi trải qua là phải chạy một chút mã định kỳ. Tôi đã viết một hàm hay thực hiện một hành động cần chạy hàng ngày lúc 12 giờ sáng. Dễ dàng, phải không? Sai. Điều này hóa ra là một vấn đề lớn đối với tôi vì vào thời điểm đó tôi đã quen với dịch vụ lưu trữ web "Cpanel-type", nơi có một GUI tiện dụng tốt đẹp để thiết lập cron job cho mục đích này.
Sau nhiều nghiên cứu, tôi đã tìm ra một giải pháp hay — Celery, một hàng đợi công việc không đồng bộ mạnh mẽ được sử dụng để chạy các tác vụ trong nền. Nhưng điều này dẫn đến các vấn đề khác, vì tôi không thể tìm thấy một bộ hướng dẫn dễ dàng để tích hợp Cần tây vào Dự án Django.
Tất nhiên cuối cùng tôi cũng đã tìm ra được — đó là nội dung mà bài viết này sẽ đề cập: Cách tích hợp Cần tây vào Dự án Django và tạo Nhiệm vụ định kỳ.
Tiền thưởng miễn phí: Nhấp vào đây để có quyền truy cập vào Hướng dẫn tài nguyên học tập Django (PDF) miễn phí, chỉ cho bạn các mẹo và thủ thuật cũng như các cạm bẫy phổ biến cần tránh khi xây dựng ứng dụng web Python + Django.
Dự án này sử dụng Python 3.4, Django 1.8.2, Celery 3.1.18 và Redis 3.0.2.
Tổng quan
Để thuận tiện cho bạn, vì đây là một bài đăng lớn như vậy, vui lòng quay lại bảng này để biết thông tin ngắn gọn về từng bước và lấy mã liên quan.
Bước | Tổng quan | Thẻ Git |
---|---|---|
Bảng điện tử | Tải xuống bảng chuẩn | v1 |
Thiết lập | Tích hợp Celery với Django | v2 |
Nhiệm vụ cần tây | Thêm tác vụ cần tây cơ bản | v3 |
Nhiệm vụ định kỳ | Thêm nhiệm vụ định kỳ | v4 |
Chạy cục bộ | Chạy ứng dụng của chúng tôi cục bộ | v5 |
Chạy từ xa | Chạy ứng dụng của chúng tôi từ xa | v6 |
Cần tây là gì?
“Cần tây là một hàng đợi nhiệm vụ / hàng đợi công việc không đồng bộ dựa trên việc truyền thông điệp phân tán. Nó tập trung vào hoạt động thời gian thực, nhưng cũng hỗ trợ lập lịch trình. ” Đối với bài đăng này, chúng tôi sẽ tập trung vào tính năng lập lịch để thực hiện định kỳ một công việc / nhiệm vụ.
Tại sao điều này hữu ích?
- Nghĩ về tất cả những lần bạn phải thực hiện một nhiệm vụ nhất định trong tương lai. Có lẽ bạn cần truy cập một API mỗi giờ. Hoặc có thể bạn cần gửi một loạt email vào cuối ngày. Dù lớn hay nhỏ, Celery đều giúp bạn dễ dàng lên lịch cho những công việc định kỳ như vậy.
- Bạn không bao giờ muốn người dùng cuối phải đợi các trang tải hoặc các hành động hoàn thành một cách không cần thiết. Nếu quy trình dài là một phần trong quy trình làm việc của ứng dụng, bạn có thể sử dụng Celery để thực thi quy trình đó trong nền, khi các tài nguyên trở nên sẵn có, để ứng dụng của bạn có thể tiếp tục phản hồi các yêu cầu của khách hàng. Điều này giúp nhiệm vụ không nằm trong ngữ cảnh của ứng dụng.
Thiết lập
Trước khi tìm hiểu về Celery, hãy lấy dự án khởi đầu từ repo Github. Đảm bảo kích hoạt virtualenv, cài đặt các yêu cầu và chạy quá trình di chuyển. Sau đó khởi động máy chủ và điều hướng đến http:// localhost:8000 / trong trình duyệt của bạn. Bạn sẽ thấy dòng chữ quen thuộc “Chúc mừng bạn đã có trang hỗ trợ Django đầu tiên”. Khi hoàn tất, hãy tắt máy chủ.
Tiếp theo, hãy cài đặt Celery bằng pip:
$ pip install celery==3.1.18
$ pip freeze > requirements.txt
Giờ đây, chúng tôi có thể tích hợp Cần tây vào Dự án Django của mình chỉ trong ba bước đơn giản.
Bước 1:Thêm celery.py
Bên trong thư mục “picha”, hãy tạo một tệp mới có tên là 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))
Ghi lại các nhận xét trong mã.
Bước 2:Nhập ứng dụng Cần tây mới của bạn
Để đảm bảo rằng ứng dụng Celery được tải khi Django khởi động, hãy thêm mã sau vào __init__.py tệp nằm bên cạnh settings.py của bạn tệp:
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
Sau khi thực hiện điều đó, bố cục dự án của bạn bây giờ sẽ giống như sau:
├── manage.py
├── picha
│ ├── __init__.py
│ ├── celery.py
│ ├── settings.py
│ ├── urls.py
│ └── wsgi.py
└── requirements.txt
Bước 3:Cài đặt Redis làm “Người môi giới” cần tây
Celery sử dụng "nhà môi giới" để chuyển các thông điệp giữa Dự án Django và các công nhân Celery. Trong hướng dẫn này, chúng tôi sẽ sử dụng Redis làm nhà môi giới tin nhắn.
Trước tiên, hãy cài đặt Redis từ trang tải xuống chính thức hoặc qua brew (brew install redis
) và sau đó chuyển sang thiết bị đầu cuối của bạn, trong một cửa sổ đầu cuối mới, kích hoạt máy chủ:
$ redis-server
Bạn có thể kiểm tra xem Redis có hoạt động bình thường hay không bằng cách nhập mã này vào thiết bị đầu cuối của bạn:
$ redis-cli ping
Redis nên trả lời bằng PONG
- hãy thử nó!
Sau khi Redis hoạt động, hãy thêm mã sau vào tệp settings.py của bạn:
# 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'
Bạn cũng cần thêm Redis làm phụ thuộc trong Dự án Django:
$ pip install redis==2.10.3
$ pip freeze > requirements.txt
Đó là nó! Bây giờ bạn có thể sử dụng Celery với Django. Để biết thêm thông tin về cách thiết lập Celery với Django, vui lòng xem tài liệu chính thức về Celery.
Trước khi tiếp tục, hãy chạy một vài kiểm tra sự tỉnh táo để đảm bảo tất cả đều ổn…
Kiểm tra xem Celery worker đã sẵn sàng nhận nhiệm vụ chưa:
$ 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
Diệt quy trình bằng CTRL-C. Bây giờ, hãy kiểm tra xem bộ lập lịch tác vụ Celery đã sẵn sàng hoạt động chưa:
$ celery -A picha beat -l info
...
[2015-07-07 14:08:23,054: INFO/MainProcess] beat: Starting...
Bùng nổ!
Một lần nữa, hãy tắt quá trình khi hoàn tất.
Công việc cần tây
Celery sử dụng các tác vụ, có thể được coi là các hàm Python thông thường được gọi với Celery.
Ví dụ:hãy biến chức năng cơ bản này thành một nhiệm vụ Cần tây:
def add(x, y):
return x + y
Đầu tiên, hãy thêm một người trang trí:
from celery.decorators import task
@task(name="sum_two_numbers")
def add(x, y):
return x + y
Sau đó, bạn có thể chạy tác vụ này không đồng bộ với Celery như sau:
add.delay(7, 8)
Đơn giản, phải không?
Vì vậy, những loại tác vụ này hoàn toàn phù hợp khi bạn muốn tải một trang web mà không bắt người dùng phải đợi một số quá trình nền hoàn thành.
Hãy xem một ví dụ…
Quay trở lại Dự án Django, lấy phiên bản ba, bao gồm một ứng dụng chấp nhận phản hồi từ người dùng, được gọi là feedback
một cách khéo léo :
├── 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
Cài đặt các yêu cầu mới, kích hoạt ứng dụng và điều hướng đến http:// localhost:8000 / feedback /. Bạn sẽ thấy:
Hãy bắt đầu nhiệm vụ Cần tây.
Thêm công việc
Về cơ bản, sau khi người dùng gửi biểu mẫu phản hồi, chúng tôi muốn ngay lập tức để anh ta tiếp tục hoạt động vui vẻ trong khi chúng tôi xử lý phản hồi, gửi email, v.v., tất cả đều ở chế độ nền.
Để thực hiện điều này, trước tiên hãy thêm một tệp có tên là task.py vào thư mục "phản hồi":
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)
Sau đó, cập nhật form.py như vậy:
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'])
Về bản chất, send_feedback_email_task.delay(email, message)
chức năng xử lý và gửi email phản hồi trong nền khi người dùng tiếp tục sử dụng trang web.
LƯU Ý :
success_url
trong views.py được đặt để chuyển hướng người dùng đến/
, mà chưa tồn tại. Chúng tôi sẽ thiết lập điểm cuối này trong phần tiếp theo.
Nhiệm vụ định kỳ
Thông thường, bạn sẽ cần phải lên lịch một tác vụ để chạy vào một thời điểm cụ thể thường xuyên - tức là, một công cụ quét web có thể cần chạy hàng ngày, chẳng hạn. Những nhiệm vụ như vậy, được gọi là nhiệm vụ định kỳ, rất dễ thiết lập với Celery.
Cần tây sử dụng “nhịp cần tây” để lên lịch các công việc định kỳ. Celery beat chạy các tác vụ theo các khoảng thời gian đều đặn, sau đó các công việc này sẽ được thực hiện bởi các công nhân cần tây.
Ví dụ:tác vụ sau được lên lịch chạy mười lăm phút một lần:
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
Hãy xem ví dụ mạnh mẽ hơn bằng cách thêm chức năng này vào Dự án Django…
Quay lại Dự án Django, lấy phiên bản bốn, bao gồm một ứng dụng mới khác, được gọi là photos
, sử dụng API Flickr để tải ảnh mới hiển thị trên trang web:
├── 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
Cài đặt các yêu cầu mới, chạy quá trình di chuyển và sau đó khởi động máy chủ để đảm bảo tất cả đều ổn. Hãy thử kiểm tra lại biểu mẫu phản hồi. Lần này nó sẽ chuyển hướng tốt.
Tiếp theo là gì?
Chà, vì chúng tôi cần gọi API Flickr định kỳ để thêm nhiều ảnh hơn vào trang web của mình, chúng tôi có thể thêm tác vụ Cần tây.
Thêm công việc
Thêm một task.py vào photos
ứng dụng:
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")
Tại đây, chúng tôi chạy save_latest_flickr_image()
hoạt động mười lăm phút một lần bằng cách gói lệnh gọi hàm trong một task
. @periodic_task
decorator tóm tắt mã để chạy tác vụ Celery, để lại task.py tệp sạch sẽ và dễ đọc!
Chạy cục bộ
Sẵn sàng để chạy điều này?
Với Ứng dụng Django và Redis của bạn đang chạy, hãy mở hai cửa sổ / tab thiết bị đầu cuối mới. Trong mỗi cửa sổ mới, điều hướng đến thư mục dự án của bạn, kích hoạt virtualenv của bạn, sau đó chạy các lệnh sau (một trong mỗi cửa sổ):
$ celery -A picha worker -l info
$ celery -A picha beat -l info
Khi bạn truy cập trang web trên http://127.0.0.1:8000/, bây giờ bạn sẽ thấy một hình ảnh. Ứng dụng của chúng tôi nhận được một hình ảnh từ Flickr cứ sau 15 phút:
Xem qua photos/tasks.py
để xem mã. Nhấp vào nút “Phản hồi” cho phép bạn… gửi một số phản hồi:
Điều này hoạt động thông qua một nhiệm vụ cần tây. Hãy xem feedback/tasks.py
để biết thêm.
Vậy là xong, bạn đã thiết lập và chạy dự án Picha!
Điều này tốt để thử nghiệm trong khi phát triển Dự án Django của bạn tại địa phương, nhưng không hoạt động tốt khi bạn cần triển khai sang sản xuất - có lẽ như trên DigitalOcean. Vì vậy, bạn nên chạy Celery worker và lập lịch trong nền dưới dạng daemon với Supervisor.
Chạy từ xa
Cài đặt rất đơn giản. Tải phiên bản năm từ repo (nếu bạn chưa có). Sau đó, SSH vào máy chủ từ xa của bạn và chạy:
$ sudo apt-get install supervisor
Sau đó, chúng tôi cần thông báo cho Người giám sát về công nhân Celery của chúng tôi bằng cách thêm tệp cấu hình vào thư mục “/etc/supervisor/conf.d/” trên máy chủ từ xa. Trong trường hợp của chúng tôi, chúng tôi cần hai tệp cấu hình như vậy - một cho Celery worker và một cho Celery Scheduler.
Tại địa phương, hãy tạo một thư mục có tên là “supervisor” trong thư mục gốc của dự án. Sau đó, thêm các tệp sau…
Công nhân cần tây: 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
Công cụ lập lịch cần tây: 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
Đảm bảo cập nhật đường dẫn trong các tệp này để khớp với hệ thống tệp của máy chủ từ xa.
Về cơ bản, các tệp cấu hình người giám sát này cho người giám sát biết cách chạy và quản lý các ‘chương trình’ của chúng tôi (như chúng được gọi bởi người giám sát).
Trong các ví dụ trên, chúng tôi đã tạo hai chương trình giám sát có tên là “pichacelery” và “pichacelerybeat”.
Bây giờ chỉ cần sao chép các tệp này vào máy chủ từ xa trong thư mục “/etc/supervisor/conf.d/”.
Chúng tôi cũng cần tạo các tệp nhật ký được đề cập trong các tập lệnh trên trên máy chủ từ xa:
$ touch /var/log/celery/picha_worker.log
$ touch /var/log/celery/picha_beat.log
Cuối cùng, hãy chạy các lệnh sau để Người giám sát biết các chương trình - ví dụ:pichacelery
và pichacelerybeat
:
$ sudo supervisorctl reread
$ sudo supervisorctl update
Chạy các lệnh sau để dừng, bắt đầu và / hoặc kiểm tra trạng thái của pichacelery
chương trình:
$ sudo supervisorctl stop pichacelery
$ sudo supervisorctl start pichacelery
$ sudo supervisorctl status pichacelery
Bạn có thể đọc thêm về Người giám sát từ tài liệu chính thức.
Mẹo cuối cùng
- Không chuyển các đối tượng mô hình Django cho các tác vụ Celery. Để tránh trường hợp đối tượng mô hình đã thay đổi trước khi nó được chuyển cho một tác vụ Celery, hãy chuyển khóa chính của đối tượng cho Celery. Tất nhiên, bạn sẽ phải sử dụng khóa chính để lấy đối tượng từ cơ sở dữ liệu trước khi làm việc với nó.
- Bộ lập lịch Celery mặc định tạo một số tệp để lưu trữ cục bộ lịch biểu của nó. Các tệp này sẽ là “celerybeat-calendar.db” và “celerybeat.pid”. Nếu bạn đang sử dụng hệ thống kiểm soát phiên bản như Git (bạn nên làm như vậy!), Bạn nên bỏ qua các tệp này và không thêm chúng vào kho lưu trữ của mình vì chúng dành cho các quy trình đang chạy cục bộ.
Các bước tiếp theo
Chà, đó là phần giới thiệu cơ bản để tích hợp Cần tây vào Dự án Django.
Muốn nhiều hơn?
- Tìm hiểu Hướng dẫn sử dụng Cần tây chính thức để tìm hiểu thêm.
- Tạo Fabfile để thiết lập Supervisor và các tệp cấu hình. Đảm bảo thêm các lệnh vào
reread
vàupdate
Người giám sát. - Chuyển Dự án khỏi repo và mở Yêu cầu kéo để thêm một nhiệm vụ Cần tây mới.
Tiền thưởng miễn phí: Nhấp vào đây để có quyền truy cập vào Hướng dẫn tài nguyên học tập Django (PDF) miễn phí, chỉ cho bạn các mẹo và thủ thuật cũng như các cạm bẫy phổ biến cần tránh khi xây dựng ứng dụng web Python + Django.
Chúc bạn viết mã vui vẻ!