Asosiy kontentga o‘tish

Maqolalar 6 daqiqa 62 ko‘rish

Celery: sekin ishlarni fonga o‘tkazib, foydalanuvchini kutdirmaslik

Ro‘yxatdan o‘tish tugmasini bosgan odam email yuborilishini kutib o‘tirmasligi kerak. Celery bilan sekin ishlarni so‘rovdan ajratamiz: navbat, worker va vazifani noldan yig‘ib, o‘zimiz ishga tushiramiz.

python backend

Mundarija

Foydalanuvchi ro‘yxatdan o‘tish tugmasini bosdi. Sizga esa unga xush kelibsiz xati yuborish, avatar rasmini kichiklashtirib saqlash, balki adminga bildirishnoma jo‘natish kerak. Agar bularning hammasini so‘rov ichida qilsangiz, foydalanuvchi tugmani bosib, brauzer aylanayotgan holatga qarab bir necha soniya kutadi. Ayniqsa email yuborish yomon: tashqi pochta xizmati sekin javob bersa, foydalanuvchi butun shu vaqt davomida osilib turadi.

To‘g‘ri yechim — bu ishlarni so‘rovdan ajratib, fonga o‘tkazish. Foydalanuvchiga darhol "muvaffaqiyatli ro‘yxatdan o‘tdingiz" deb javob qaytariladi, email esa orqada, o‘z vaqtida yuboriladi. Python dunyosida bu ishning standart tool'i — Celery.

Ish qanday oqadi#

Uchta ishtirokchi bor. Web-ilova bajarilishi kerak bo‘lgan ishni navbatga qo‘yadi — navbat rolini odatda Redis o‘ynaydi. Shu zahoti foydalanuvchiga javob qaytadi. Alohida ishlab turgan worker jarayoni esa navbatni kuzatib turadi, undan vazifani oladi va bajaradi. Ya'ni web-ilova "bu ishni kimdir qilsin" deb yozib qo‘yadi va o‘z yo‘lida davom etadi, worker esa o‘sha yozuvni ko‘rib, ishni bajaradi.

O‘zimiz yig‘ib ko‘ramiz#

Buni sinash uchun Redis kerak. Docker bilan bir zumda ko‘tariladi. Celery'ni esa virtual muhitga o‘rnatamiz — zamonaviy tizimlarda (masalan, Ubuntu 24.04) tizim Python'iga to‘g‘ridan-to‘g‘ri pip install qilish externally-managed-environment xatosi bilan rad etiladi:

Terminal
docker run -d --name redis-sinov -p 6379:6379 redis

python3 -m venv .venv
source .venv/bin/activate
pip install celery redis

Windows'da

Celery 4-versiyadan beri Windows'ni rasmiy qo‘llab-quvvatlamaydi. Windows kompyuterda o‘rganayotgan bo‘lsangiz, quyidagi buyruqlarni WSL (Windows ichidagi Linux) yoki Docker ichida bajaring — shunda maqoladagi natijani aynan o‘zingizda ko‘rasiz.

Endi tasks.py faylini yozamiz. Bu yerda ataylab email yuborishni time.sleep bilan taqlid qilamiz — real xizmat ham xuddi shunday sekin bo‘ladi:

Python
# tasks.py
import time

from celery import Celery

app = Celery("tasks", broker="redis://localhost:6379/0")


@app.task
def send_welcome_email(email):
    print(f"{email} manziliga xat yuborilyapti...")
    time.sleep(3)  # sekin tashqi xizmatni taqlid qilamiz
    print(f"{email} — yuborildi!")

Worker'ni ishga tushiramiz#

Endi bitta terminalda worker'ni yoqamiz — u navbatni kuzatib turadi:

Terminal
celery -A tasks worker --loglevel=info

Ekranda Celery ishga tushgani va vazifalarni kutayotgani haqidagi loglar chiqadi. Bu terminalni ochiq qoldiring.

Endi ikkinchi terminalda Python ochib, vazifani navbatga qo‘yamiz:

Python
>>> from tasks import send_welcome_email
>>> send_welcome_email.delay("olim@misol.uz")
<AsyncResult: 7b2c...>

E'tibor bering, .delay() deyishingiz bilan Python darhol qaytdi, uch soniya kutmadi. Endi birinchi terminalga — worker turgan joyga — qarang: o‘sha yerda "yuborilyapti" va uch soniyadan keyin "yuborildi" degan satrlar paydo bo‘ladi.

Mana shu — butun g‘oyaning jonli namoyishi. .delay() ishni faqat navbatga yozib qo‘ydi, haqiqiy uch soniyalik ishni esa worker o‘z zimmasiga oldi. Web-ilovada bu shuni anglatadiki, foydalanuvchi hech narsa kutmaydi.

Qaysi ishlarni fonga o‘tkazish kerak#

Amaliyotda fon vazifasiga tushadigan ishlar bir-biriga o‘xshaydi. Email va SMS yuborish — deyarli har doim, chunki tashqi xizmatning tezligi sizning qo‘lingizda emas. Rasm yoki video qayta ishlash — protsessorni ko‘p oladi, uni so‘rov ichida qilib bo‘lmaydi. Uzoq davom etadigan hisobotlarni tayyorlash ham fonga o‘tadi. Va nihoyat, muntazam takrorlanadigan ishlar — har kuni zaxira olish, eski ma'lumotni tozalash — Celery Beat degan qismi orqali jadval bilan bajariladi.

Fon vazifalarning o‘z tuzoqlari#

Fonda ishlash bir necha yangi holatni yuzaga keltiradi va ular haqida oldindan bilgan yaxshi.

Birinchisi: worker ish yarmida qulab tushsa, nima bo‘ladi? Javob sozlamaga bog‘liq, va standart javob ko‘pchilik kutganidek emas. Celery standart holatda vazifani navbatdan olishi bilanoq "qabul qildim" deb tasdiqlaydi — bajarib bo‘lmasdan oldin. Worker shu payt qulasa, vazifa qayta berilmaydi, shunchaki yo‘qoladi. Sinab ko‘rdim: sakkiz soniyalik vazifaning o‘rtasida worker'ni kill -9 bilan o‘ldirdim, yangi worker uni hech qachon olmadi.

Muhim vazifa yo‘qolmasligi kerak bo‘lsa, tasdiqlashni ish tugagandan keyinga suramiz:

Python
app.conf.task_acks_late = True  # ish tugagandagina "bajarildi" deymiz
app.conf.task_reject_on_worker_lost = True  # worker o‘lsa, vazifa navbatga qaytsin

Shu sozlama bilan xuddi o‘sha sinovda vazifa yangi worker'da qaytadan boshlanib, oxiriga yetdi. Redis bilan bir nozik joy bor: tasdiqlanmagan vazifa visibility_timeout muddati o‘tgach qaytariladi, standart holatda bu bir soat. Ya'ni vazifa yo‘qolmaydi, lekin darhol ham qaytmaydi — sinovda kutib o‘tirmaslik uchun bu muddatni app.conf.broker_transport_options = {"visibility_timeout": 5} bilan besh soniyaga qisqartirgan edim.

Endi teskari tomoni: bu sozlama bilan bitta vazifa ikki marta bajarilishi mumkin — birinchi urinish yarmida uzilgan, ikkinchisi boshidan boshlanadi. Shuning uchun vazifalar idempotent bo‘lishi kerak, ya'ni ikki marta bajarilsa ham zarar bo‘lmasligi lozim. Xush kelibsiz xati ikki marta ketsa unchalik yomon emas, lekin masalan hisobdan pul yechish ikki marta bajarilsa — bu jiddiy muammo.

Ikkinchisi: vazifaga obyektning o‘zini emas, uning ID'sini uzating. Yuqoridagi misolda men email satrini uzatdim, real kodda esa odatda foydalanuvchi ID'si uzatiladi va worker uni bazadan yangidan oladi. Sababi shuki, vazifa navbatda biroz turishi mumkin va shu orada ma'lumot o‘zgargan bo‘lishi mumkin — ID bo‘yicha olsangiz, doim eng yangi holatni olasiz.

Uchinchisi: worker jimgina yiqilishi mumkin. Web-ilovadan farqli o‘laroq, fon vazifasidagi xato hech kimning ekraniga chiqmaydi. Shuning uchun xatolarni kuzatib turadigan biror tizim qo‘yish kerak, aks holda vazifalar bajarilmay qolayotganini uzoq vaqt sezmasligingiz mumkin.

Har sekin ish ham Celery talab qilmaydi

Celery qudratli, lekin u loyihaga jiddiy murakkablik qo‘shadi: yana bitta jarayon, uni kuzatish, navbatni boshqarish. Agar sizda kuniga bir necha marta bajariladigan yengil ish bo‘lsa, ba'zan oddiy yechim yetarli — masalan Django ichida oddiy thread yoki hatto boshqaruv buyrug‘i (management command) va cron. Celery o‘zini oqlaydigan joy — ishlar tez-tez, ko‘p va ishonchli bajarilishi kerak bo‘lgan tizim.

Xulosa#

Celery so‘rov ichida bajarib bo‘lmaydigan sekin ishlarni fonga chiqaradi va foydalanuvchini kutishdan xalos qiladi. G‘oyasi uchta so‘zga sig‘adi: ilova navbatga yozadi, worker o‘qib bajaradi. Ilova o‘sib, foydalanuvchilar ko‘paygan sari bu ajratish deyarli muqarrar kerak bo‘ladi — birinchi sekinlashuv ko‘pincha aynan email yoki rasm ishlovidan boshlanadi.

6 daqiqa 62 marta o‘qildi