Data Engineering

کافکا مقاوم: فشار بازگشتی و صف‌های نامه‌های مرده

مقدمه

در معماری‌های رویدادمحور مدرن، آپاچی کافکا به عنوان سیستم عصبی مرکزی برای توزیع داده عمل می‌کند. با این حال، ماهیت «شلیک و فراموش کردن» در عبور پیام‌ها اغلب شکنندگی‌های زیربنایی را پنهان می‌کند. زمانی که مصرف‌کنندگان نمی‌توانند با تولیدکنندگان همگام بمانند، یا زمانی که پردازش به دلیل باگ‌ها یا خطاهای گذرا شکست بخورد، سیستم‌ها می‌توانند فرو بریزند. این مقاله دو الگوی حیاتی برای ایجاد مقاومت را بررسی می‌کند: مدیریت فشار بازگشتی برای جلوگیری از تخلیه حافظه و پیاده‌سازی صف‌های نامه‌های مرده (DLQs) برای عایق‌بندی پیام‌های آلوده که در پردازش شکست می‌خورند.

درک فشار بازگشتی در کافکا

فشار بازگشتی مکانیزمی است که طی آن یک مصرف‌کننده به بروتکر (Broker) سیگنال می‌دهد که تحت فشار است. در کافکا، این موضوع عمدتاً از طریق تعهد آفست‌ها (Offset Commits) و اندازه‌های دریافت (Fetch Sizes) مدیریت می‌شود. اگر یک مصرف‌کننده پیام‌ها را کندتر از رسیدن آن‌ها پردازش کند، بروتکر به ذخیره پیام‌ها در موضوع (Topic) ادامه می‌دهد و به طور مؤثر یک بافر فراهم می‌کند. با این حال، برنامه باید همچنان حافظه داخلی خود را مدیریت کند. کلید مدیریت فشار بازگشتی فقط مربوط به بروتکرهای کافکا نیست، بلکه مربوط به منطق مصرف‌کننده شماست. اگر مصرف‌کننده شما عملیات‌های I/O سنگین (مانند نوشتن در پایگاه داده) انجام دهد، مسدود خواهد شد. بدون محدودیت‌های مناسب، این امر منجر به خطاهای OutOfMemoryError می‌شود.

// مثال: پیکربندی محدودیت‌های دریافت برای مدیریت حافظه
Properties props = new Properties();
props.put(ConsumerConfig.MAX_POLL_RECORDS_CONFIG, 500);
props.put(ConsumerConfig.FETCH_MAX_BYTES_CONFIG, 5 * 1024 * 1024); // 5MB
props.put(ConsumerConfig.MAX_POLL_INTERVAL_MS_CONFIG, 30000);
با محدود کردن max.poll.records، اطمینان حاصل می‌کنید که یک تکرار پلینگ (Polling) داده‌های بیش از حد زیادی را به حافظه نمی‌کشد. اگر پردازش طولانی‌تر از max.poll.interval.ms طول بکشد، مصرف‌کننده از گروه اخراج می‌شود و یک تعادل‌یابی مجدد (Rebalance) را فعال می‌کند. این یک شیر اطمینان است، اما تعادل‌یابی‌های مکرر پرهزینه هستند. هدف این است که این پارامترها را به گونه‌ای تنظیم کنید که مصرف‌کننده بتواند با نرخ ورود داده همگام بماند بدون اینکه بیش از حد بارگذاری شود.

نقش صف‌های نامه‌های مرده

چه اتفاقی می‌افتد وقتی پردازش یک پیام شکست بخورد؟ یک throw new Exception() ساده در حلقه مصرف‌کننده، مصرف‌کننده را متوقف می‌کند و باعث می‌شود آفست‌ها متوقف شوند. این امر کل پارتیشن را مسدود می‌کند. برای جلوگیری از این موضوع، از DLQs استفاده می‌کنیم. DLQ یک موضوع (یا صف) جداگانه است که پیام‌های شکست‌خورده در آن نوشته می‌شوند. مصرف‌کننده اصلی پردازش پیام بعدی را ادامه می‌دهد، در حالی که مصرف‌کننده DLQ (یا اپراتور دستی) شکست را بررسی می‌کند.

try {
    processMessage(record);
    // تعهد آفست به صورت دستی یا اتکا به تعهد خودکار در صورت موفقیت
} catch (Exception e) {
    // ثبت خطا در لاگ
    logger.error("Failed to process record: {}", record.value(), e);
    
    // ارسال به DLQ
    dlqProducer.send(dlqTopic, record);
    
    // تعهد آفست برای رکورد اصلی برای حرکت به جلو
    consumer.commitSync(Map.of(topicPartition, new OffsetAndMetadata(record.offset() + 1)));
}
توجه: در محیط تولید، باید از یک چارچوب قوی مانند Spring Kafka یا قابلیت DLQ در Confluent Cloud برای مدیریت صحیح این منطق تراکنشی استفاده کنید و اطمینان حاصل کنید که پیام یا پردازش می‌شود یا بدون تکرار یا از دست رفتن به DLQ منتقل می‌شود.

استراتژی پیاده‌سازی عملی

1. پایش تأخیر (Lag): تأخیر گروه مصرف‌کننده را ردیابی کنید تا فشار بازگشتی را زود تشخیص دهید. اگر تأخیر به طور مداوم افزایش یابد، نشان‌دهنده یک مشکل ظرفیت است. 2. ایدمپوتنس (Idempotency): اطمینان حاصل کنید که منطق پردازش شما ایدمپوتنت است. از آنجا که تلاش‌های مجدد و ارسال مجدد DLQ می‌توانند باعث تکرار شوند، برنامه شما باید آن‌ها را به طور مناسب مدیریت کند. 3. حالت‌های تخریب‌شده (Degraded Modes): اگر یک سرویس پایین‌دست از کار بیفتد، تصمیم بگیرید که آیا پیام‌ها را حذف کنید (نادر)، آن‌ها را به صورت محلی بافر کنید (پرخطر)، یا آن‌ها را برای پخش مجدد بعدی به DLQ هدایت کنید.

نتیجه‌گیری

ساخت سیستم‌های رویدادمحور مقاوم نیاز به چیزی فراتر از صرفاً استقرار کافکا دارد. این کار نیازمند تنظیم دقیق رفتار مصرف‌کننده برای مدیریت فشار بازگشتی و یک استراتژی قوی برای مدیریت شکست‌ها از طریق صف‌های نامه‌های مرده است. با در نظر گرفتن پیام‌ها به عنوان داده‌های شکننده‌ای که می‌توانند در هر مرحله شکست بخورند، خط لوله‌هایی می‌سازید که نه تنها مقیاس‌پذیر هستند، بلکه قابل بازیابی نیز می‌باشند. با پایش تأخیر خود و پیاده‌سازی یک DLQ پایه برای موضوعات حیاتی‌ترین خود شروع کنید.
Share: