چگونه درخواست‌های زمان‌بر را در جنگو هندل کنیم یا رساله‌ در حکمت مطلقه‌ی celery

ساختار تعاملی کاربر/django/RabbitMQ/Celery
ساختار تعاملی کاربر/django/RabbitMQ/Celery

شاید براتون پیش اومده باشه که با افزایش لود سرور، سرورتون موقع پاسخ به ریکوئست‌ها خیلی کند عمل کنه. یک کاری که برای مقابله با این اتفاق می‌شه انجام داد اینه که، یک سری تسک‌هایی رُ پیدا کنیم که باید در موقع درخواست کاربر انجام بشن ولی دو شرط زیر رُ داشته باشن:
۱. اجرای بلافاصله‌ی آن‌ها الزامی نباشه
۲. در پاسخی که به کاربر می‌دیم تغییری ایجاد نکنه(به نوعی در بالایی هم اومده!)

حالا با پیدا کردن این تسک‌ها، اون‌ها رُ توی یک صف می‌ذاریم و در نهایت اون‌ها رُ با یه ترتیبی در پشت‌زمینه انجام می‌دیم.

مثال واقعی

در ابتدای کار ما در بیستون با یک اپلیکیشن شروع کردیم، و این اپلیکیشن حدود ۱۰۰ کاربر فعال داشت. با کمک uWsgi و nginx لود این کاربرها و هندل می‌شد. با گذشت زمان و بیشتر شدن اپلیکیشن‌ها و همچنین کاربرانمان، با لود وحشتناکی مواجه شدیم! میانگین زمان پاسخ‌گویی بسیار بالا (در حد ۲۰ ثانیه) رسید و ما برای ارسال کد فعال‌سازی با مشکلات فراوانی روبرو شدیم. اولین کاری که باید می‌کردیم این بود که فرآیندی پیدا کنیم تا بتوانیم از رفع شدن مشکل مطلع شویم!

برای این کار به ابزار استرس تست ab (مال حضرت آپاچی) رو آوردیم (در فرصت‌های آتی، عمری باشه خدمتتون این رُ هم می‌گم ولی فقط موقع بازی باهاش مواظب باشین به خودتونو DoS نکنین :) ). حالا می‌تونستیم با هر تغییر بطور دقیق میانگین زمان پاسخ را با شرایط تقریبا مساوی بسنجیم.

لطفا خودتون رُ Dos نکنین :)
لطفا خودتون رُ Dos نکنین :)

,oالبته کاربر دیگری که برای ما داشت، انجام بعضی فرآیند‌های گزارش‌گیری زمان‌بر بود که با استفاده از سلری در پشت پرده انجام می‌شود.

بطور خلاصه celery چطوری کار می‌کنه؟

جنگو به یک message broker (اینجا ما از RabbitMQ استفاده کردیم ولی شما می‌تونین از ردیس یا چیزهای دیگه هم استفاده کنین) یک پیامی (تسکی) رُ می‌فرسته. message broker هم این پیام رُ به یکی از ورکرهایی که مختص این کاره ارجاع می‌ده. در مرحله‌ی آخر هم ورکر مربوطه کار رُ‌ در پشت پرده انجام می‌ده و تمام :)

یک مثال از پیاده‌سازی

در این مثال فرض شده سیستم عاملتون اوبونتو هست ولی اگه از سیستم عامل دیگه‌ای استفاده می‌کنین sudo supervisorctl reread

sudo supervisorctl update

sudo supervisorctl start celeryمی‌تونین با یه سری تغییرات ریز از همین دستورات استفاده کنین.

اول باید بروکر رُ نصب کنیم، که اینجا ما از RabbitMQ استفاده می‌کنیم:

sudo apt-get install rabbitmq-server

حالا باید RabbitMQ رُ تنظیم کنیم:

sudo rabbitmqctl add_user [user_name] [password]
sudo rabbitmqctl add_vhost [vhost_name]
sudo rabbitmqctl set_permissions -p [vhost_name] [user_name] &quot.*&quot &quot.*&quot &quot.*&quot

در این‌جا باید قسمت‌های که بولد شدن رُ خودتون ست کنین. دقت کنین که ما اینجا همه‌ی پرمیشن‌ها روی virtural host رُ به یوزری که تازه ساختیم می‌دیم. اگه کاربری خاصی دارین به این موضوع دقت کنین.

حالا باید celery رُ نصب کنیم. دقت کنین که اگه دارین از virtual env خاصی استفاده می‌کنین حتما مراحل نصب رُ روی اون env خاص اجرا کنین:

pip install celery

حالا توی پروژه‌ی جنگوتون در فایل settings.py این‌ها رُ اضافه کنین:

BROKER_URL = 'amqp://guest:guest@localhost:5672//'
CELERY_ACCEPT_CONTENT = ['json']
CELERY_TASK_SERIALIZER = 'json'
CELERY_RESULT_SERIALIZER = 'json'

در کنار فایل settings.py یک فایل جدید به نام celery.py ایجاد کنین و این‌ها رُ داخلش بذارین:

from __future__ import absolute_import
import os
from celery import Celery
from django.conf import settings
os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'project_name.settings')
app = Celery('project_name')
app.config_from_object('django.conf:settings')
#این قسمت باعث می‌شه که اپلیکیشن بتونه تسک‌هایی که برای سلری تعریف می‌شه رُ
# خودکار تشخیص بده
app.autodiscover_tasks(lambda: settings.INSTALLED_APPS)

و در نهایت فایل __init__.py رُ به این صورت تغییر بدین:

from __future__ import absolute_import
from .celery import app as celery_app

حالا کافیه برای هر تسکی که می‌خواین توی پشت‌زمینه اجرا بشه یک انوتیشن @shared_task بذارین:

@shared_task
def handle_notification(text, keyword, channel, sender, reciever, notification_id, req_ip=None):
    pass # یه کار خفن طولانی :)

حالا هر جا لازمه از این متد استفاده کنین کافیه اینطوری صداش کنین:

handle_notification.delay(text, keyword, channel, sender, reciever, notification_id,ip)

حالا تنها کاری که باید بکنیم اینه که سلری رُ ران کنیم. برای این‌که بصورت سرویس ران بشه ما از supervisor استفاده می‌کنیم:

sudo apt-get install supervisor

حالا یک فایل با اسم celery.conf برای سلری می‌سازیم و این‌ها رُ داخلش می‌ذاریم:

[program:celery]
command=[path_to_your_virtual_env]/bin/celery --app=project_name worker --loglevel=INFO
directory=[path_to_your_project]
user=[system_user_name]
autostart=true
autorestart=true
redirect_stderr=true

و در نهایت با اجرای این دستورات کارمون تموم می‌شه:

sudo supervisorctl reread
sudo supervisorctl update
sudo supervisorctl start celery


توجه داشته باشین که این فقط یه کاربرد خیلی محدود از سلری هست و کارهای خفن بیشتری باهاش می‌شه کرد، که توصیه می‌کنم حتما دنبالش برین :)

پی‌نوشت:

عکس اول از اینجا برداشتم.