مقدمه
اگر هنوز اسکریپهای کرون را شبها بالا و پایین میبری، وقتش رسیده معماریات را به مدل «وظیفهمحور» منتقل کنی. در این راهنما یاد میگیری چگونه با ترکیب Celery و RabbitMQ یک سامانهٔ اسکریپینگ مقاوم، مانیتورشدنی و مقیاسپذیر بسازی. پس از خواندن این مقاله تواناییهای زیر را خواهی داشت:
- درک نقش پیامبروکر و صفها در پردازش تسکها
- پیادهسازی یک اپ Celery ساده برای اسکریپ کردن صفحات وب
- راهاندازی workerها، زمانبندی با Beat و مانیتورینگ با Flower
- نکات عملی برای مدیریت نرخ، پروکسی، retry و پایداری در مقیاس
موقعیت استفاده: Cron یا Celery + RabbitMQ
قانون سرانگشتی: اگر فقط چند کار ساده و نادر داری، Cron کافی است؛ اما وقتی تعداد تسکها یا نیاز به قابلیتهای پیشرفته (ریزتریها، مانیتورینگ، توزیع بار) افزایش پیدا کند، صفبندی مبتنی بر Celery به مراتب قابلاعتمادتر است.
- Cron: ساده، آماده، اما بدون retry و دید مناسب برای خطاها.
- Celery + RabbitMQ: خودکارسازی retry، backoff، توزیع وظایف و یک نقطهٔ مرکزی برای مانیتورینگ.
پیشنیازها
- آشنایی با Python 3.7+
- کتابخانهها: Celery، kombu، requests، beautifulsoup4
- RabbitMQ بهعنوان message broker
- ابزارهای مانیتورینگ مثل Flower و در محیطهای بزرگتر، راهکارهایی برای autoscaling (مثلاً Kubernetes)
Step 1: آمادهسازی محیط
برای جدا نگه داشتن وابستگیها بهتر است از virtual environment استفاده کنی:
python -m venv venv
source venv/bin/activate # Linux/macOS
# یا در ویندوز:
# venv\Scripts\activate
سپس بستههای مورد نیاز را نصب کن:
pip install celery kombu requests beautifulsoup4
توضیح: Celery وظیفهٔ orkestration را بر عهده دارد، kombu لایهٔ ارتباطی با RabbitMQ است، و requests و BeautifulSoup برای دریافت و پارس HTML استفاده میشوند.
Step 2: نصب و نقش RabbitMQ
RabbitMQ صفها را نگهداری میکند و پیامها را بین کلاینت و worker منتقل میکند. جریان کلی اینگونه است:
- کلاینت یک تسک (مثلاً «این URL را اسکریپ کن») به Celery میفرستد.
- Celery تسک را به RabbitMQ میسپارد و RabbitMQ آن را صف میکند.
- یکی از workerها تسک را برداشته و اجرا میکند.
- نتیجه ذخیره یا بازگردانده میشود (بسته به backend).
نمونهٔ دستورات نصب روی سیستمهای مختلف:
# Ubuntu / Debian
sudo apt-get install curl gnupg apt-transport-https -y
sudo apt-get update -y
sudo apt-get install rabbitmq-server -y --fix-missing
sudo systemctl start rabbitmq-server
# macOS (Homebrew)
brew install rabbitmq
brew services start rabbitmq
# Windows (Chocolatey)
choco install rabbitmq
rabbitmq-service.bat start
برای اطمینان از اجرا بودن سرویس از فرمان مناسب سیستمعامل استفاده کن؛ مثلاً sudo systemctl status rabbitmq-server در لینوکس یا rabbitmqctl status در macOS.
Step 3: ایجاد اپ Celery
یک فایل پایتون بساز که پیکربندی Celery را نگه دارد، معمولاً نامش celery_config.py است. نمونهٔ ساده:
from celery import Celery
app = Celery('scraper', broker='pyamqp://guest@localhost//', backend='rpc://')
# قرار دادن تسکها در صف پیشفرض 'scraping'
app.conf.task_default_queue = 'scraping'
app.conf.task_routes = {
'tasks.scrape': {'queue': 'scraping'},
}
# برای autodiscover یا ثبت ماژول تسکها
import tasks
app.autodiscover_tasks(['tasks'])
چه کاری انجام میدهد (شرح بخشها):
- Celery('scraper', broker=...): نمونهای از اپ Celery میسازد و آن را به RabbitMQ متصل میکند.
- backend='rpc://': اجازه میدهد نتایج تسک قابل بازیابی باشند (برای استفاده از result.get()).
- task_default_queue و task_routes: صف پیشفرض و روتینگ را مشخص میکنند تا کنترل بار و جداسازی کارها سادهتر شود.
- autodiscover_tasks: ماژولهای وظیفه را بارگذاری میکند تا Celery بداند چه توابعی را میتواند اجرا کند.
Step 4: تعریف تسکهای اسکریپینگ
یک فایل tasks.py بساز. ابتدا نمونهٔ بسیار ساده:
from celery_config import app
import requests
from bs4 import BeautifulSoup
from celery import shared_task
@shared_task
def scrape(url):
response = requests.get(url, timeout=10)
soup = BeautifulSoup(response.text, 'html.parser')
return soup.title.text if soup.title else "No title found"
توضیح ورودی/خروجی و نقش تابع:
- ورودی: url (رشتهٔ URL).
- خروجی: متن تگ <title> یا پیام خطا بهعنوان مقدار برگشتی تسک.
- نقش: این تابع بهعنوان یک تسک Celery ثبت شده و میتواند بهصورت غیرهمزمان اجرا شود.
نسخهٔ بهبود یافته با retry و backoff و پشتیبانی از پروکسی:
from celery import shared_task
import requests
from bs4 import BeautifulSoup
import time
@shared_task(bind=True, max_retries=3, default_retry_delay=5)
def scrape(self, url, proxy=None):
try:
headers = {'User-Agent': 'Mozilla/5.0 (compatible; Scraper/1.0)'}
proxies = {'http': proxy, 'https': proxy} if proxy else None
resp = requests.get(url, timeout=15, headers=headers, proxies=proxies)
resp.raise_for_status()
soup = BeautifulSoup(resp.text, 'html.parser')
return soup.title.text if soup.title else 'No title found'
except requests.RequestException as exc:
# مثال backoff ساده: افزایش تأخیر براساس تعداد تلاشهاn
try:
delay = min(60, 2 ** self.request.retries)
except Exception:
delay = 5
raise self.retry(exc=exc, countdown=delay)
نکات مهم:
- استفاده از bind=True تا بتوانیم از self.retry برای تلاش مجدد استفاده کنیم.
- تنظیم max_retries و default_retry_delay برای جلوگیری از حلقههای بینهایت.
- اضافه کردن هدرها و گزینهٔ پروکسی برای کاهش ریسک بلوک شدن IP.
Step 5: اجرای Worker
برای شروع یک worker که فقط از صف scraping کار میگیرد:
celery -A celery_config worker --loglevel=info -Q scraping
توضیح پرچمها:
- -A celery_config: بارگذاری اپ از فایل مشخص.
- worker: اجرای پردازش worker.
- --loglevel=info: مقداردهی مناسب برای لاگگیری توسعه و دیباگ.
- -Q scraping: محدود کردن worker به صف موردنظر.
برای افزایش همزمانی میتوان از --concurrency استفاده کرد:
celery -A celery_config worker --loglevel=info -Q scraping --concurrency=4
Step 6: زمانبندی با Celery Beat (اختیاری)
اگر میخواهی تسکها بهصورت دورهای اجرا شوند، از Beat استفاده کن. در celery_config.py میتوان برنامهٔ زمانی افزود:
from celery.schedules import crontab
app.conf.beat_schedule = {
'scrape-every-hour': {
'task': 'tasks.scrape',
'schedule': crontab(minute=16), # هر ساعت در دقیقهٔ 16
'args': ('https://example.com',)
},
}
سپس Beat را اجرا کن:
celery -A celery_config beat --loglevel=info
نکته: اگر schedule تغییر کند ممکن است نیاز باشد workerها را ریاستارت کنی تا هماهنگسازی کامل شود.
Step 7: اجرای تسکها بهصورت On-Demand
برای فراخوانی یک تسک از اسکریپت یا API بدون انتظار، از .delay() استفاده کن. نمونهٔ ساده:
from tasks import scrape
# enqueue و ادامهٔ کار بدون بلاک
result = scrape.delay('https://example.com')
# اگر نیاز به نتیجه داری، میتوانی منتظر بمانی
print(result.get())
توضیح: delay یک پیام در صف قرار میدهد و کنترل را بلافاصله باز میگرداند؛ get() برای خواندن نتیجهٔ نهایی استفاده میشود (ترجیحاً در محیطی که انتظار بلوک شدن منطقی است).
Step 8: مانیتورینگ و اشکالزدایی
پایش فعال برای مقیاس واقعی حیاتی است. برخی فرمانها و ابزارهای مفید:
# بررسی وضعیت تسکها
celery -A celery_config inspect active
celery -A celery_config inspect scheduled
celery -A celery_config inspect reserved
# اجرا کردن Flower برای داشبورد
pip install flower
celery -A celery_config flower
با Flower میتوانی وضعیت تسکها، تعداد retryها، لاگ خطا و health هر worker را ببینی. همچنین برای تولیدیها معیارهایی مثل طول صف، نرخ خطا و تاخیر هر تسک را مانیتور کن و آلارم تعریف کن.
ملاحظات تولیدی و رایجترین مشکلات
- Rate limits و بلوک شدن IP: از روتیشن پروکسی، تغییر User-Agent و تأخیر بین درخواستها استفاده کن.
- تسکهای طولانی: تسکهای پیچیده را به زیرتسکهای کوچک تقسیم کن تا retry و timeout قابل مدیریت شود.
- Memory leak: workerها را با استراتژی زمانبندی بازنشانی کن یا از مدل pre-fork با محدودیت حافظه استفاده کن.
- Failover بروکر: در سیستمهای حیاتی، RabbitMQ را کانفیگ کن که در خوشه اجرا شود تا single point of failure نداشته باشی.
- Idempotency: تسکها را طوری طراحی کن که اجرای مجدد آنها مشکلی ایجاد نکند (مثلاً هنگام نوشتن در DB از upsert استفاده کن).
چکلیست مقیاسبندی (عملی)
- صفبندی منطقی: جداسازی صفها بر اساس نوع سایت یا اولویت.
- بافرینگ و batching: اجرای 50k تسک یکباره ممنوع—تسکها را برحسب زمان توزیع کن.
- استراتژی retry و backoff: از exponential backoff برای شکستهای موقتی استفاده کن.
- مانیتورینگ و alerting: طول صف، نرخ خطا و زمان اجرای تسکها را دنبال کن.
- ذخیرهٔ نتایج: نتایج بزرگ را سریعاً به DB یا ذخیرهٔ خارجی ارسال کن تا حافظه worker آزاد بماند.
- آزمون سیاه/مسیریابی: سناریوهای خطا را شبیهسازی و رفتار retry و DLQ را بررسی کن.
جمعبندی
ترکیب Celery و RabbitMQ مسیر مشخصی برای ساخت یک پلتفرم اسکریپینگ مقیاسپذیر و قابلاعتماد فراهم میکند: توزیع کار، retry خودکار، زمانبندی کدمحور و مانیتورینگ زمانواقعی. با رعایت اصولی مثل idempotency، تقسیم تسکها، و استفاده از پروکسی و backoff، میتوانی از دردسرهای کرون شبانه خلاص شوی و سیستم خود را بهصورت ایمن و قابل رشد مدیریت کنی.
حالا یک قدم عملی بردار: یک worker محلی راهاندازی کن، یک تسک نمونه enqueue کن، و لاگها را با Flower زیر نظر بگیر تا تفاوت بین اسکریپ کردن دستی و پلتفرم مبتنی بر صف را احساس کنی.
آسان اسکریپ را در گوگل بهعنوان منبع ترجیحی انتخاب کن
مقالههای جدید ما زودتر و پررنگتر در نتایج گوگل و Discover برایت نمایش داده میشود. افزودن از تنظیمات گوگل





