کار در پس زمینه با فریمورک جنگو

سلام سلامحمید هستم و با یک قسمت دیگر از نیمچه آموزش‌های جالب در خدمتتونم.شوخی بسه برم سر اصل مطلب...حدود یک سال پیش که می‌خواستم برای یکی از پروژه‌هایی که دستم بود سرویس پوش نوتیفیکیشن راه اندازی کنم. به یه مشکل برخوردم.مدیر اپلیکیشن می‌خواست با اپلیکیشن مدیریت یک ریکوئست بده و پیغامی رو برای تمام کاربران اپلیکیشن ارسال بکنه. برای ارسال نوتیفیکیشن نیاز داشتم پیغام رو به سرور firebase برسونم که توسط سرویس firebase cloud messaging به دست کاربران برسه. و برای این کار باید یک ریکوئست به سرور firebase ارسال می‌کردم.این روند به نظر ساده و روون میاد اما مشکل دقیقا اونجا بود که بعضی اوقات سرور firebase دیر جواب ریکوئست من رو می‌داد و از اون طرف هم مدیر اپلیکیشن منتظر بود تاییدیه ارسال پوش رو بگیره و خب مشکل پیش می‌اومد دیگه =)))یکمی سرچ کردم و راه حلش رو پیدا کردم و مشکل رو حل کردم ، هم من راضی ، هم مدیر اپلیکیشن راضی و...حالا بعد از یک سال که می‌خواستم به یکی از کارآموزام این مطلب رو آموزش بدم دنبال آموزش روون بودم اما پیدا نمی‌شد برای همین تصمیم گرفتم یکی توی این بلاگ بنویسم که شاااااید به درد بقیه هم بخوره =)))توضیح کلی روند کاردر کنار جنگو از دو پکیج دیگه استفاده می‌کنیماولی که Celery نام داره. یک "صف اعمال درست کن" ( task queue ) هست. ما یک سری عمل یا همون تسک رو براش تعیین می‌کنیم و اون یه سری صف درست میکنه. هرموقع که ما یکی از عمل هامون رو صدا زدیم. اون عمل رو می‌اندازه توی صف تا وقتی که نوبتش رسید انجام بشه و وقت کار اصلی ما رو نگیره.دومی هم redis نام داره ردیس در اصل یک ساختمان داده نگهدارنده درون حافظه ای هست ( مثل یک دیتابیس که درون حافظه کار میکنه ).ما از ردیس برای انتقال اطلاعات بین اپلیکیشن جنگو و celery استفاده می‌کنیم ( راستی این هم بدونید که ردیس به عنوان یک message broker یا همون انتقال دهنده پیام هم استفاده میشه )فکر می‌کنم توی همین دو مورد بالا روند کار هم به صورت خلاصه توضیح دادم بهتون =)))فرضیات و اهداففرض می‌کنیم که یک فضای ایزوله (virtual environment) به نام venv و درون اون یک پروژه جنگو به نام prj و درون اون یک اپلیکیشن جنگو به نام prapp داریم.پس اطلاعات ما به صورت زیر هستن : venv prj ├── manage.py ├── prj │ ├── __init__.py │ ├── settings.py │ ├── urls.py │ └── wsgi.py ├── prapp │ ├── __init__.py │ ├── admin.py │ ├── apps.py │ ├── models.py │ ├── tests.py │ └── views.pyهدفمون هم اینه که یک عمل در پس زمینه راه بیندازیم که دو مقدار رو با همدیگه جمع بکنه و توی فایلی به نام alaki.txt مقدار رو ذخیره بکنه و یک ویو بسازیم که با هر بار صدا کردنش صد بار این کار رو انجام بده.شروع کارابتدا با نصب ردیس شروع می‌کنیم :sudo apt install redis-serverبعد از اون باید ماژول‌های ردیس و سلری برای پایتون رو نصب بکنیم :pipenv install celery redisسپس با اضافه کردن تنظیمات سلری به settings پروژه کار رو ادامه میدیم : 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 = 'Asia/Tehran'الان باید یک فایل celery.py توی آدرس prj/prj بسازیم و محتویات زیر رو توی اون بنویسیم :from __future__ import absolute_import import os from celery import Celery from django.conf import settings os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'prj.settings') app = Celery('prj') app.config_from_object('django.conf.settings') app.autodiscover_tasks(lambda: settings.INSTALLED_APPS)هدف از ساخت این فایل این هست که با اجرا کردن این فایل با هربار اجرا شدن پروژه جنگو خودمون تنظیمات سلری بر اساس تنظیماتی که توی فایل settings.py اضافه کردیم انجام بشه و سلری ما آماده استفاده باشه. حالا برای صدا زدن این فایل و اجرا شدن سلری کافیه که به فایل prj/prj/__init__.py اطلاعات زیر رو اضافه بکنیم :from __future__ import absolute_import from .celery import app as celery_app __all__ = ('celery_app',)خب حالا که تمام تنظیمات اولیه انجام شده ، کافیه که اعمال مورد نیاز برای انجام شدن در پس زمینه رو برای سلری مشخص کنیم. برای این کار یک فایل به نام tasks.py در آدرس prj/prapp می‌سازیم و تسک‌های خودمون رو توی اون می‌نویسیم ( به عنوان مثال ما در این آموزش می‌خواستیم یک تسک برای جمع کردن دو عدد و ذخیره اون بنویسیم ) :from celery import shared_task import time @shared_task() def plus(x, y): time.sleep(5) alaki = open(&quotalaki.txt&quot, &quota+&quot) alaki.write(str( x + y ) + &quot\n&quot) alaki.close()خب ما از یک decorator به نام shared_task استفاده کردیم که این تابع رو به یک عمل معتبر celery تبدیل می‌کنه و ما می‌تونیم با هر Celery که داریم این تسک رو صدا بزنیم.همچنین برای بهتر شدن تستمون ۵ ثانیه توقف گذاشتیم که تفاوت انجام شدن عمل در background و foreground رو متوجه بشیم.و در انتها ویو هم می‌نویسم تا با نحوه صدا زدن یک تسک سلری آشنا بشید.این هم از محتویات فایل views.py :from .tasks import plus from django.http import HttpResponse def alaki(request): for i in range(100): plus.delay(1398, 1) return HttpResponse(&quotOK&quot)خب باقی تنظیمات جنگو و ران کردن پروژه رو به عهده خودتون باقی می‌گذارم اما اینو بگم که شما بعد از ران کردن پروژه جنگو باید worker سلری رو ران بکنید تا تسک های شما رو انجام بده. برای ران کردن worker سلری از دستور زیر می‌تونید استفاده بکنید :celery -A prj worker -l infoتامام تامام =)))نتیجه مطلوبهمونطور که به احتمال زیاد خودتون می‌دونید ما می‌تونستیم ویو خودمون رو به شکل زیر بنویسیم :from django.http import HttpResponse import time def alaki(request): for i in range(100): time.sleep(5) alaki = open(&quotalaki.txt&quot, &quota+&quot) alaki.write(str( x + y ) + &quot\n&quot) alaki.close() return HttpResponse(&quotOK&quot)اما در این صورت وقتی که به این ویو ریکوئست ارسال می‌کردیم باید ۵۰۰ ثانیه صبر می‌کردیم تا یک OK تحویل بگیریمدر حالی که الان با استفاده از celery می‌تونیم همون لحظه OK رو تحویل بگیریم و باقی کار ها توی پس زمینه انجام بشه =))ممنونم از این که این پست رو مطالعه کردیدامیدوارم که براتون مفید باشه =))لایک و کامنت فراموش نشه...سوالی بود در خدمتم.نوشته شده با ❤️ توسط کوچیکتون حمیدرضا شجراوی =)))