صف پیام چیه و کی باید ازش استفاده کرد
خلاصهٔ کاملتر
الکس اورلوف تو این مقاله از سری معماری مقاوم، message queue رو با یه مثال توضیح میده: یه اپ اشتراک عکس مثل اینستاگرام. وقتی کاربر عکس آپلود میکنه، باید عکس resize بشه، از نظر محتوا بررسی بشه (moderation) و نوتیفیکیشنها فرستاده بشن. اگه همهی اینها پشت سر هم روی یه سرور انجام بشه، کاربر منتظر میمونه، شکست یه مرحله کل آپلود رو خراب میکنه و با یه ترافیک ناگهانی سرور از پا درمیاد.
راهحل اینه که سرور فقط عکس رو ذخیره کنه، یه پیام بذاره تو صف و فوراً به کاربر بگه آپلود تموم شد. بعد سرویسهای جدا مثل Resize و Moderation کارها رو از صف برمیدارن. صف یه بافر بین producer (کسی که پیام میسازه) و consumer (کسی که پیامو پردازش میکنه) هست و این دو تا اصلاً همدیگه رو نمیشناسن. یعنی هر کدوم جدا scale میشن و میشه سختافزار مخصوص خودشونو داشته باشن، مثلاً GPU برای moderation.
زیر کاپوت، مکانیزم ACK (یعنی تأیید صریح مصرفکننده که کارش تموم شده) نمیذاره پیام با کرش کردن worker گم بشه. برای جلوگیری از پردازش تکراری، SQS پیام رو برای یه مدت قابل تنظیم (از 0s تا 12h، پیشفرض 30s) از بقیه مخفی میکنه. RabbitMQ پیامو unack علامت میزنه (پیشفرض 30m) و Kafka هر partition رو دقیقاً به یه worker تو هر consumer group میده.
تو تحویل پیام سه حالت داریم. At-Least-Once پیشفرض هر سه ابزاره و ممکنه پیام تکراری بیاد، که برای کارهای idempotent (یعنی کارهایی که تکرارشون نتیجه رو عوض نمیکنه) مشکلی نیست. ولی اگه پیام ۱۳۰ کرون از کاربر کم کنه، دوبار اجراش یعنی ۲۶۰ کرون! At-Most-Once تکرار نداره ولی ممکنه داده گم بشه. Exactly-Once هم تو عمل با ترکیب At-Least-Once و یه consumer که با کلید deduplication یا تراکنش دیتابیس idempotent شده پیاده میشه.
نویسنده نکتههای ظریف رو هم میگه. partition key به اندازهی shard key دیتابیس مهمه: ترتیب فقط داخل یه partition تضمین میشه، و اگه مثلاً تو اپ تاکسی بر اساس شهر partition کنی، شهرهای بزرگ یه hot partition میسازن. تعداد consumerها هم نمیتونه از تعداد partitionها بیشتر باشه. برای پیامی که هیچوقت درست پردازش نمیشه (poisoned message) یه سقف retry میذاری و بعدش میره تو Dead Letter Queue. برای وقتی هم که تولید از مصرف سریعتره، autoscaling، back pressure و هشدار روی عمق صف لازمه.
به گفتهی نویسنده، صف برای کارهای async، ترافیک ناگهانی، جداسازی سرویسها و وقتی که نباید هیچ کاری گم بشه مناسبه. ولی تو کارهای همزمان با latency زیر 500ms تقریباً حتماً شرط رو میشکنه. Kafka برای throughput بالا و replay پیامهای قدیمی بهترینه، SQS سرویس managed و سادهی AWS هست و RabbitMQ برای routing پیچیده. یه خواننده هم اضافه کرده که صف خطا رو حذف نمیکنه، فقط عقبش میندازه، و بهتره به جای عمق صف روی سن قدیمیترین پیام هشدار بذاری.
نکات کلیدی:
- تو SQS بازهی مخفی شدن پیام از 0 ثانیه تا 12 ساعته و پیشفرضش 30 ثانیهست.
- تو RabbitMQ اگه consumer تو 30 دقیقه ACK نکنه، پیام دوباره در دسترس قرار میگیره.
- At-Least-Once پیشفرض SQS، RabbitMQ و Kafka هست.
- تو یه consumer group، تعداد consumerها نمیتونه از تعداد partitionها بیشتر باشه.
- SQS FIFO ترتیب دقیق و Exactly-Once میده ولی throughput کمتری از Standard داره.
- بهتره روی سن قدیمیترین پیام هشدار بذاری، نه فقط عمق صف.




