Django va Celery bilan kengaytiriladigan API#Texnologiya
Okt 01, 2026 6 0
Ulashish

Django va Celery bilan kengaytiriladigan API

Og'ir ishlarni so'rovdan Celery navbatiga chiqarish: transaction.on_commit, idempotent vazifalar, API limitlari va production'dagi konteynerlar — WeliCoDev va NutriZone misollarida.

Otabek Xurramov
Python Backend Developer · StartUp CEO
3 daqiqa o'qish

API sekin ishlay boshlaganda birinchi xayolga keladigan narsa — serverni kuchaytirish. Ko'pincha muammo boshqa joyda: so'rov ichida bajarilmasligi kerak bo'lgan ishlar bajarilyapti. Xat yuborish, Telegram'ga xabar, rasm qayta ishlash, tashqi API'ga murojaat — bularning hech biri foydalanuvchi javobni kutib turishiga arziydi. Bu maqolada Django va Celery bilan shunday ishlarni fonga qanday chiqarishimni, WeliCoDev va NutriZone'dagi amaliy misollar bilan ko'rsataman.

1. Qoida: so'rov — faqat javob uchun

HTTP so'rov ichida faqat foydalanuvchiga javob qaytarish uchun zarur bo'lgan ish qolishi kerak: tekshiruv, bazaga yozish, javob. Qolgan hammasi — navbatga. Arxitektura oddiy:

  • Django — so'rovni qabul qiladi va vazifani navbatga qo'yadi;
  • Redis — broker: vazifalar navbati;
  • Celery worker — vazifalarni fonda bajaradi;
  • Celery beat — jadval bo'yicha ishlar (har daqiqa, har kuni).

2. Eng ko'p uchraydigan xato: tranzaksiya tugamasdan vazifa yuborish

Model saqlangan zahoti task.delay(obj.pk) chaqirsangiz, worker obyektni bazadan topa olmasligi mumkin — chunki tranzaksiya hali commit qilinmagan. Yechim — transaction.on_commit:

from django.db import transaction

def on_article_published(instance):
    transaction.on_commit(
        lambda: post_to_channel.delay("blog.Article", instance.pk)
    )

Shu bilan vazifa faqat ma'lumot haqiqatan saqlangandan keyin navbatga tushadi.

3. Idempotentlik: vazifa ikki marta ishlasa ham zarar yo'q

Celery vazifani qayta urinishi (retry) yoki worker qayta ishga tushishi mumkin. Shuning uchun har bir vazifa “ikki marta bajarilsa nima bo'ladi?” degan savolga javob berishi kerak. Kanalga post yuborishda men ikki himoya qo'yaman: obyektdagi belgi va yuborilgan postlar jadvali.

@shared_task
def post_to_channel(model_label, pk):
    obj = Model.objects.filter(pk=pk).first()
    if obj is None or obj.posted_to_channel_at:
        return  # allaqachon yuborilgan
    if ChannelPost.objects.filter(object_id=pk, status="sent").exists():
        return
    post = channel.post_object(obj)
    if post.status == "sent":
        Model.objects.filter(pk=pk).update(posted_to_channel_at=timezone.now())

E'tibor bering: vazifaga obyektning o'zi emas, faqat pk uzatiladi. Vazifa bajarilganda eng yangi holat bazadan o'qiladi.

4. Tashqi API limitlari va qayta urinish

Telegram bitta bot uchun soniyasiga taxminan 25 ta xabarga ruxsat beradi. Ommaviy xabar yuborishda buni hisobga olmasangiz, 429 xatolarini olasiz. Ikki qoida:

  • yuborish tezligini cheklash (xabarlar orasida qisqa pauza);
  • vaqtinchalik xatolarda retry, doimiy xatolarda (masalan, foydalanuvchi botni bloklagan — 403) to'xtash va buni bazaga yozib qo'yish.
@shared_task(bind=True, max_retries=3, default_retry_delay=10)
def notify_admins(self, text):
    for chat_id in admin_chat_ids():
        try:
            api.send_message(chat_id, text)
        except api.TelegramError as e:
            log.warning("notify failed for %s: %s", chat_id, e)

5. Jadval bo'yicha ishlar: Celery beat

Rejalashtirilgan kanal postlari va ommaviy xabarlar uchun cron emas, Celery beat ishlataman — jadval kod bilan birga versiyalanadi:

CELERY_BEAT_SCHEDULE = {
    "send-due-telegram-posts": {
        "task": "apps.telegram_bot.tasks.send_due",
        "schedule": 60.0,
    },
    "nudge-learners": {
        "task": "apps.telegram_bot.tasks.nudge_learners",
        "schedule": crontab(hour=11, minute=0),
    },
}

CELERY_TIMEZONE ni loyiha vaqt zonasiga (bizda Asia/Tashkent) tenglashtirishni unutmang, aks holda “soat 11:00” UTC bo'yicha ishlaydi.

6. Production'da: alohida konteynerlar

Docker Compose'da web, worker va beat — bitta image'dan, lekin alohida servislar. Worker'ni web'dan mustaqil kengaytirish mumkin, beat esa doim bitta nusxada bo'lishi shart (aks holda vazifalar ikki marta yuboriladi).

services:
  web:
    command: gunicorn config.wsgi -w 3 -b 0.0.0.0:8000
  celery-worker:
    command: celery -A config worker -l info
  celery-beat:
    command: celery -A config beat -l info

7. Tekshiruv ro'yxati

  • Vazifaga faqat ID uzating, obyekt emas.
  • Model saqlangandan keyin — transaction.on_commit.
  • Har bir vazifa idempotent bo'lsin.
  • Tashqi API limitlari va 4xx/5xx xatolarini alohida ko'ring.
  • Beat — faqat bitta nusxa.
  • Testlarda tashqi API'ni mock qiling va “ikki marta chaqirildi” holatini tekshiring.

Xulosa

Celery murakkab texnologiya emas, lekin uni to'g'ri ishlatish intizom talab qiladi. So'rovni yengil tuting, og'ir ishni navbatga bering va har bir vazifani qayta bajarilishga tayyor qilib yozing — API o'z-o'zidan tez va barqaror bo'ladi.

BackendDjangoPython

Ovoz sozlamalari

Ekvalayzer
Tezlik
Uyqu taymeri

Klaviatura: Probel — ijro/pauza, ← → — 10 soniya, Shift+← → — trek, M — ovozsiz

Navbat