صف کار در بک اند چیست و چه وقت به آن نیاز پیدا می کنیم؟
اگه ایمیل، پیامک و فاکتور رو توی همون درخواست کاربر انجام بدی، سایت کند می شه و کار از دست می ره. اینجا می بینیم چه کارهایی رو باید به صف سپرد و اولین صف رو چطور راه بندازیم.

صف کار در بک اند یعنی کارهای کند یا سنگین، مثل ارسال ایمیل و پردازش سفارش، رو توی همون درخواست کاربر انجام ندی. به جاش هر کدوم رو به شکل یه «کار» توی یه صف می ذاری و یه پردازشگر جدا توی پس زمینه انجامش می ده. نتیجه اش اینه که کاربر زود جواب می گیره، سایت روان می مونه و اگه یه سرویس بیرونی کند یا خراب باشه، کار از دست نمی ره. توی این راهنما قدم به قدم می بینیم چه کارهایی رو باید به صف سپرد، اولین صف رو چطور راه بندازیم و شکست کارها رو چطور مدیریت کنیم.
گام صفر: ببین سایتت دقیقاً کجا منتظر می مونه
قبل از نصب هر ابزاری، مطمئن شو مشکل واقعاً از کارهای پشت صحنه است. نشونه هاش آشناست: دکمه «ثبت سفارش» چند ثانیه می چرخه، بعضی درخواست ها تایم اوت می شن، یا کاربر از ترس اینکه سفارشش ثبت نشده دوباره دکمه رو می زنه و دو تا سفارش ساخته می شه.
علتش معمولاً اینه که توی همون درخواست HTTP کلی کار دیگه هم انجام می شه: ایمیل تایید، پیامک، ساخت فاکتور PDF، تغییر اندازه عکس ها، خبر دادن به سیستم انبار یا حسابداری. هر کدوم تنهایی کوتاهه، ولی وقتی پشت سر هم میان، کاربر باید منتظر همه شون بمونه.
قبل از هر تغییری اندازه بگیر
ساده ترین روش اینه که دور هر مرحله از مسیر ثبت سفارش یه زمان سنج بذاری و مدت اجراش رو لاگ کنی. چند روز که لاگ جمع شد، ببین کدوم قسمت بیشترین زمان رو می خوره. فقط به میانگین نگاه نکن. کندترین درخواست ها همونایی هستن که کاربرها یادشون می مونه، پس سراغ اون ها برو.
وقتی درگاه پرداخت وسط ماجراست
شب یلدا یا وسط یه کمپین حراج، کاربر پرداخت رو انجام می ده و برمی گرده به سایت. اگه سایت توی همون درخواست بازگشت، پیامک و ایمیل و فاکتور رو هم بسازه و درگاه پیامک کند باشه، کاربری که پولش رو داده یه صفحه چرخان می بینه. اگه مشکل «برگشت از درگاه» رو داری، مقاله سفارش ثبت نمی شود؛ مشکل بازگشت از درگاه پرداخت را چطور پیدا کنیم؟ هم کمکت می کنه.
صف کار در بک اند دقیقاً چیه؟
یه رستوران شلوغ رو در نظر بگیر. اگه گارسون بعد از گرفتن سفارش بره پشت اجاق بایسته تا غذا آماده بشه، بقیه مشتری ها بی جواب می مونن. به جاش گارسون سفارش رو روی یه برگه می نویسه، به آشپزخونه می ده و می ره سراغ میز بعدی. آشپزها هم به ترتیب برگه ها رو برمی دارن و غذا رو می پزن. صف کار همین برگه هاست.
چهار تا مفهوم هست که باید بشناسی:
- تولیدکننده (producer): بخشی از برنامه که کار رو می سازه، مثلاً کنترلری که سفارش رو ثبت می کنه.
- کار (job): یه پیام کوچک که می گه چه کاری باید انجام بشه و چه داده ای لازمه، مثلاً «برای سفارش شماره فلان فاکتور بساز».
- صف (queue): جایی که کارها منتظر نوبت می مونن. می تونه یه جدول توی دیتابیس باشه، Redis باشه یا یه message broker.
- پردازشگر (worker): یه پروسه جدا که کارها رو یکی یکی برمی داره و اجرا می کنه.
پردازشگر یه پروسه مستقله و بخشی از وب سرور نیست. اگه کندتر از ورود کارها باشه، کارها توی صف جمع می شن ولی سایت کند نمی شه. اگه هم خراب بشه، بقیه سایت سر جاش کار می کنه.

چه کارهایی رو بسپاریم به صف و چه کارهایی نه
قاعده ساده اینه: هر کاری که نتیجه اش لازم نیست همون لحظه به کاربر نشون داده بشه، یا به سرویسی بیرون از کنترل ما وابسته است، کاندیدای صفه. ولی نباید هر کاری رو کورکورانه صف کرد. بعضی کارها باید همون لحظه و داخل تراکنش اصلی انجام بشن.
| کار | بره توی صف؟ | دلیلش |
|---|---|---|
| ارسال ایمیل و پیامک اطلاع رسانی | بله | کاربر لازم نیست منتظر بمونه، و درگاه پیامک ممکنه کند باشه |
| ساخت فاکتور PDF | بله | کار سنگینیه و نتیجه اش رو می شه بعداً نشون داد |
| تغییر اندازه و بهینه سازی تصویر | بله | زمانش به حجم فایل بستگی داره |
| اطلاع دادن به انبار یا حسابداری بیرونی | بله | وابسته به سیستم دیگه است و باید قابل تکرار باشه |
| تایید پرداخت از درگاه (verify) | معمولاً نه | نتیجه اش تعیین می کنه کاربر چه صفحه ای ببینه |
| ثبت خود سفارش و کم کردن موجودی | نه | باید توی یه تراکنش و با تضمین کامل انجام بشه |
| اعتبارسنجی فرم | نه | کاربر باید همون لحظه خطا رو ببینه |
اولین صف کار در بک اند رو چطور راه بندازیم؟
اینجا بیشتر آدم ها گیر می کنن، چون اسم ابزارها زیاده. بیا بده بستان ها رو ساده کنیم.
گزینه اول: صف روی دیتابیس خودت
اگه از قبل PostgreSQL یا MySQL داری، می تونی کارها رو توی یه جدول ذخیره کنی و پردازشگر هر چند ثانیه یه بار بخونتشون. مزیتش اینه که زیرساخت جدید نمی خواد و می تونی کار رو توی همون تراکنش ثبت سفارش بسازی. عیبش اینه که با بالا رفتن حجم کار، فشار اضافه ای میاد روی دیتابیس اصلی. برای فروشگاه کوچک تا متوسط معمولاً کافیه. اگه هنوز دیتابیست رو انتخاب نکردی، PostgreSQL یا MySQL برای سایت و اپلیکیشن؛ کدام را انتخاب کنیم؟ به کارت میاد.
گزینه دوم: Redis
Redis سریعه و اکثر فریم ورک ها پشتیبانی اش می کنن، مثل Laravel، Celery یا BullMQ. راه انداختنش هم راحته. ولی اگه تنظیم ماندگاری داده اش رو درست نکنی و سرور ری استارت بشه، ممکنه کارهای توی صف از بین برن. پس قبل از اینکه برای پردازش سفارش ازش استفاده کنی، مطمئن شو تنظیماتش رو فهمیدی.
گزینه سوم: message broker مثل RabbitMQ
وقتی چند سرویس مختلف داری، مسیریابی پیچیده تری برای پیام ها لازم داری، یا تضمین های قوی تری برای رسیدن پیام می خوای، سراغ broker برو. در عوض نصب، پایش و نگه داری بیشتری می خواد. برای یه تیم کوچک، اغلب دردسرش از فایده اش بیشتره.
یه ملاحظه ایرانی
سرویس های صف مدیریت شده خارجی ممکنه از ایران مشکل پرداخت داشته باشن، یا به اینترنت بین الملل وابسته باشن که نوسان داره. پس صف و پردازشگر رو کنار برنامه خودت، روی یه سرور داخل ایران بذار. اگه برنامه ات روی میزبانی وب و سرور مجازی ایران هست، Redis یا پردازشگر رو هم همون جا بالا بیار تا به شبکه بیرونی وابسته نباشی.
پردازشگر باید همیشه زنده بمونه
یه اشتباه رایج اینه که پردازشگر رو با دست توی ترمینال اجرا کنی و بعد یادت بره. با اولین ری استارت سرور، صف پر می شه و هیچ کس کاری نمی کنه. پردازشگر رو باید مثل یه سرویس واقعی مدیریت کنی. مثلاً با systemd:
[Unit]
Description=Shop queue worker
After=network.target
[Service]
ExecStart=/opt/shop/bin/worker
Restart=always
User=shop
[Install]
WantedBy=multi-user.target
مسیرها و نام ها نمونه اند و باید با پروژه خودت عوض بشن. مهم ترین خط، Restart=always ـ اینه که اگه پروسه افتاد، دوباره بالا بیاد.
گام دوم: اولین کار ساده، ایمیل خوش آمدگویی
برای شروع یه کار کم ریسک انتخاب کن. ارسال ایمیل بعد از ثبت نام نمونه خوبیه، چون اگه یه بار گم بشه فاجعه نیست.
مسیر جدید این شکلی می شه:
- کاربر فرم ثبت نام رو می فرسته.
- برنامه حساب کاربری رو می سازه.
- به جای فرستادن ایمیل، یه کار با شناسه کاربر توی صف می ذاره.
- همون لحظه به کاربر می گه «ثبت نام انجام شد».
- پردازشگر کار رو برمی داره، اطلاعات کاربر رو از دیتابیس می خونه و ایمیل رو می فرسته.
توی کار فقط شناسه کاربر رو بذار، نه کل اطلاعاتش رو. تا موقع اجرا ممکنه اطلاعات عوض شده باشه، و حجم پیام هم کوچیک می مونه.
گام سوم: پردازش سفارش، جایی که اشتباه گرون تموم می شه
ایمیل ساده بود. سفارش فرق داره، چون پول و موجودی وسطه. دو تا اشتباه کلاسیک اینجا هست.
اشتباه اول: ثبت سفارش و ثبت کار، دو مرحله جدا
فرض کن برنامه سفارش رو توی دیتابیس ذخیره می کنه و بعد کار رو توی صف می ذاره. اگه بین این دو مرحله سرور بپره، سفارش ثبت شده ولی هیچ پیامک و فاکتوری نمی ره. برعکسش هم ممکنه: کار توی صف بره ولی ثبت سفارش شکست بخوره، و کاربر پیامک تایید یه سفارش ناموجود رو بگیره.
راه حلش اینه که ثبت سفارش و ثبت کار رو توی یه تراکنش انجام بدی. اگه صف روی دیتابیس باشه، این کار ساده است. اگه Redis یا broker جداگانه داری، الگوی ثبت کار توی جدول و انتقال بعدی اش به صف رو بشناس (بهش outbox می گن). پیاده سازی اش یه کم پیچیده تره، ولی جلوی گم شدن کارها رو می گیره.
اشتباه دوم: فرض کردن اینکه هر کار فقط یک بار اجرا می شه
خیلی از سیستم های صف تضمین می کنن که هر کار «حداقل یک بار» اجرا بشه، نه «دقیقاً یک بار». یعنی اگه پردازشگر وسط کار بمیره، همون کار دوباره به پردازشگر دیگه ای داده می شه. نتیجه اش می تونه دو تا پیامک باشه یا دو بار کم شدن موجودی.
پس هر کار باید «تکرارپذیر و امن» نوشته بشه (به اصطلاح idempotent). چند روش ساده:
- قبل از فرستادن پیامک، توی دیتابیس چک کن برای این سفارش قبلاً فرستاده شده یا نه.
- برای هر کار یه شناسه یکتا بذار و اگه همون شناسه دوباره اومد، نادیده اش بگیر.
- عملیات رو طوری بنویس که اجرای دوباره اش نتیجه رو عوض نکنه، مثل «وضعیت سفارش رو بذار روی پرداخت شده» به جای «یکی به شمارنده اضافه کن».
گام چهارم: وقتی کار شکست می خوره
کارها شکست می خورن. سرویس پیامک جواب نمی ده، دیتابیس لحظه ای قفل می شه، یا یه باگ توی کد هست. صف کار خوب اون نیست که هیچ وقت خطا نداره. اونیه که خطا رو قابل مدیریت می کنه.
تلاش دوباره با فاصله
اولین ابزار retry ـه: اگه کار شکست خورد، چند بار دیگه با فاصله زمانی امتحانش کن. فاصله رو کم کم زیاد کن (backoff)، مثلاً اول چند ثانیه، بعد چند دقیقه. دلیلش اینه که اگه سرویس بیرونی مشکل داره، تکرار سریع فقط بارش رو بیشتر می کنه. شرطش همون تکرارپذیری گام قبله.
سقف تلاش و صف کارهای مرده
تلاش بی نهایت خوب نیست. بعد از چند بار شکست، کار باید برچسب «شکست خورده» بگیره و بره یه جای جدا، که بهش dead letter queue هم می گن. اینجوری یه نفر می تونه بعداً ببینه چرا خراب شد و دستی دوباره اجراش کنه. برای هر کار یه timeout هم بذار، وگرنه یه کار گیرکرده می تونه پردازشگر و بقیه کارها رو پشت سر خودش نگه داره.
چیزهایی که باید زیر نظر داشته باشی
بدون پایش، صف مثل یه جعبه سیاهه. حداقل این ها رو دنبال کن:
- تعداد کارهای منتظر توی صف، و اینکه دارن کم می شن یا زیاد.
- قدیمی ترین کار منتظر: اگه خیلی از حد معمول قدیمی تره، پردازشگر یا مرده یا گیر کرده.
- تعداد کارهای شکست خورده و دلیل شون.
- زنده بودن پردازشگر.
برای هر کدوم هشدار تعریف کن، چون صف خراب معمولاً بی صدا خراب می شه.
تجربه کاربر: صف نباید کاربر رو توی تاریکی بذاره
وقتی کاری رو به پس زمینه می فرستی، کاربر دیگه نتیجه اش رو توی همون صفحه نمی بینه. اگه این شکاف رو طراحی نکنی، یه مشکل دیگه جای قبلی رو می گیره.
چند اصل ساده:
- وضعیت رو نشون بده. سفارش باید وضعیت روشن داشته باشه: «ثبت شد»، «در حال پردازش»، «تکمیل شد»، «نیاز به بررسی». کاربر باید همیشه بدونه الان کجای ماجراست.
- قول بیش از حد نده. به جای «فاکتور برات ایمیل شد» بنویس «فاکتور تا چند دقیقه دیگه ارسال می شه و اینجا هم قابل دانلوده». یعنی دقیقاً همونی که قراره اتفاق بیفته.
- دکمه رو غیرفعال کن. بعد از زدن «ثبت سفارش»، دکمه رو قفل کن و یه شناسه یکتا برای درخواست بفرست تا کلیک مضاعف دو تا سفارش نسازه.
برای به روز موندن صفحه وضعیت، polling ساده (پرسیدن وضعیت از سرور هر چند ثانیه) برای شروع کافیه و سنگین هم نیست.

کی صف لازم نیست؟
صف داروی همه دردها نیست و بده بستان هایی داره:
- پیچیدگی بیشتر. یه پروسه، یه ابزار و چند حالت خطای جدید به سیستمت اضافه می شه.
- دیباگ سخت تر. خطا دیگه توی همون درخواست دیده نمی شه و باید لاگ پردازشگر رو هم بخونی.
- نتیجه فوری وجود نداره. هر چیزی که کاربر بلافاصله بهش نیاز داره، نباید صف بشه.
اگه سایتت کوچیکه، ترافیک کمه و فقط یه ایمیل ساده می فرستی، شاید یه cron job یا حتی ارسال مستقیم کافی باشه. یه تست خوب اینه: اگه توی لاگ های گام صفر کاری ندیدی که ارزش صف شدن داشته باشه، فعلاً سراغش نرو. اگه هم کندی سایتت از چیز دیگه ای میاد، مثلاً کوئری های سنگین یا تصویرهای بزرگ، صف مشکل رو حل نمی کنه و باید بری سراغ بهینه سازی سرعت.
سوالات متداول
صف کار در بک اند با cron job چه فرقی داره؟
cron job یه کار رو در زمان مشخص اجرا می کنه، مثلاً هر شب ساعت دو. صف کار در بک اند هر وقت کاری اومد، همون موقع (یا خیلی نزدیک بهش) پردازشش می کنه و می تونه تعداد زیادی کار پراکنده رو مدیریت کنه. اگه کار زمان بندی ثابت داره، cron کافیه. اگه به رویداد کاربر وابسته است، صف مناسب تره.
اگه سرور وسط پردازش یه کار ری استارت بشه، کار از بین می ره؟
بستگی به ابزار و تنظیم هاش داره. بیشتر سیستم های صف جدی، کار نیمه تموم رو دوباره به صف برمی گردونن، ولی بعضی تنظیمات Redis ماندگاری قوی ندارن. برای همین هم باید کارها رو تکرارپذیر بنویسی و تنظیم ماندگاری رو قبل از استفاده بخونی.
برای یه سایت کوچک هم صف لازمه؟
همیشه نه. اگه کارهای کند و بیرونی محدودی داری و کاربر هنوز با تاخیر مواجه نشده، پیچیده کردن سیستم ارزشش رو نداره. وقتی دیدی ثبت سفارش یا ارسال ایمیل و پیامک کاربر رو معطل می کنه، یا خطای سرویس بیرونی روی تجربه کاربر اثر می ذاره، موقع شروعشه.
صف رو روی دیتابیس بسازم یا از Redis استفاده کنم؟
اگه حجم کارها کمه و می خوای ثبت کار توی تراکنش سفارش انجام بشه، جدول توی دیتابیس ساده ترین و امن ترین گزینه است. اگه حجم بالاتره و سرعت مهمه، Redis معمولاً انتخاب بعدیه. ولی قبلش تنظیمات ماندگاری و پایش اش رو جدی بگیر.
از کجا شروع کنی؟
یه کار کم ریسک انتخاب کن، مثل ایمیل یا پیامک اطلاع رسانی، و اول زمان درخواست رو لاگ کن تا بدونی واقعاً کجا معطلی. بعد یه صف ساده روی همون دیتابیس یا Redis بساز، پردازشگر رو با systemd یا Docker همیشه زنده نگه دار، و برای شکست، retry و سقف تلاش تعریف کن. بعدش سراغ پردازش سفارش برو، با تراکنش درست و کارهای تکرارپذیر.


