أباتشي كافكا وتدفق الأحداث
أباتشي كافكا وتدفق الأحداث
تحذف قوائم الانتظار التقليدية الرسالة فور استهلاكها. أما Apache Kafka فيتبع نهجاً مختلفاً جوهرياً: إنه سجل إيداع موزّع (Commit Log). كل حدث يُكتب في سجل دائم لا يقبل سوى الإلحاق، ويبقى فيه طوال مدة الاحتفاظ المُهيَّأة — أياماً أو أسابيع أو للأبد. المستهلكون لا يحذفون الرسائل؛ بل يتقدمون بمؤشر يُسمى الإزاحة (offset) عبر السجل. هذا القرار التصميمي وحده يُتيح قدرات لا تستطيع قوائم الانتظار العادية تقديمها.
سجل الإيداع
تخيّل الموضوع (topic) في كافكا كملف على القرص لا يدعم سوى الإلحاق. يكتب المنتجون السجلات في نهاية هذا الملف، وكل سجل يحمل إزاحة متصاعدة تُحدد موضعه بشكل فريد في السجل. يقرأ المستهلكون بتحديد إزاحة: "أعطني كل شيء بدءاً من الإزاحة 1 000 000".
ولأن السجل ثابت ومحتفظ به، تُصبح أنماط قوية عديدة سهلة التطبيق:
- إعادة التشغيل — يستطيع أي خدمة جديدة في اتجاه المصب استهلاك جميع الأحداث التاريخية من الإزاحة 0، واللحاق بالحاضر دون أي مهمة تصدير خاصة.
- مستهلكون مستقلون متعددون — تستطيع خدمتان قراءة نفس الموضوع من مواضع مختلفة تماماً دون أن تتدخل إحداهما في الأخرى.
- مسار التدقيق — السجل هو سجل دقيق ومرتب لكل ما جرى، مما يبسّط التصحيح والامتثال القانوني.
الأقسام: وحدة التوازي
يتحول ملف سجل واحد سريعاً إلى عنق زجاجة عند الإنتاجية العالية. يحل كافكا هذا بتقسيم كل موضوع إلى أقسام (partitions) — سجلات فرعية مرتبة ومستقلة مخزنة على وسطاء مختلفين. موضوع بـ 12 قسماً يستطيع تحمّل 12 ضعف إنتاجية الكتابة مقارنةً بالموضوع أحادي القسم، لأن المنتجين يكتبون في أقسام متعددة بالتوازي.
عند إرسال رسالة، يقرر كافكا القسم المناسب لها:
- حسب المفتاح —
hash(key) % num_partitions. جميع أحداث نفس رقم الطلب أو المستخدم أو المستشعر تنتهي في القسم ذاته، مما يضمن الترتيب لكل مفتاح. - التوزيع الدوري (Round-robin) — إذا لم يُحدد مفتاح، تُوزَّع السجلات بالتساوي لتحقيق أقصى إنتاجية.
- مقسِّم مخصص — منطقك الخاص، مثل توجيه عملاء VIP إلى أقسام مخصصة.
مجموعات المستهلكين: توسيع نطاق الاستهلاك
مجموعة المستهلكين هي مجموعة من عمليات المستهلكين التي تستهلك موضوعاً بشكل مشترك. يعيّن كافكا كل قسم لمستهلك واحد بالضبط في المجموعة في أي وقت — يُسمى هذا تعيين الأقسام. والنتيجة أن المجموعة تعالج جميع الأقسام بالتوازي دون تكرار.
القاعدة الأساسية: أقصى توازٍ لمجموعة مستهلكين يساوي عدد الأقسام. إذا كان الموضوع يحتوي 6 أقسام وشغّلت 8 مستهلكين في نفس المجموعة، سيبقى 2 منهم خاملين. قم بتوسيع عدد الأقسام أولاً، ثم وسّع المستهلكين.
مجموعات المستهلكين المختلفة مستقلة تماماً. موضوع بثلاث مجموعات مستهلكين يحتوي 3 مؤشرات إزاحة منفصلة لكل قسم — كل مجموعة تقرأ بوتيرتها الخاصة، وتأخر مجموعة واحدة لا يؤثر على الأخريات. هكذا يُوزّع كافكا تدفق حدث واحد على تحليلات البيانات وفهرسة البحث وتسجيل التدقيق في آنٍ واحد.
كافكا مقابل قوائم الانتظار التقليدية
لجعل المقايضات ملموسة، قارن قائمة انتظار نموذجية (RabbitMQ أو SQS) بكافكا عبر الأبعاد الأكثر أهمية على نطاق واسع:
- الإنتاجية: يحافظ كافكا عادةً على مليون إلى مليوني رسالة في الثانية لكل وسيط عبر الكتابة الدفعية المتسلسلة على القرص. وسيط RabbitMQ نموذجي يصل إلى حده الأقصى عند 20 000–50 000 رسالة/ث لأنه يحتفظ بحالة لكل رسالة.
- الاحتفاظ: تحذف قوائم الانتظار الرسائل بعد الإقرار. يحتفظ كافكا بها للمدة المُهيَّأة (مثلاً 7 أيام)، مما يُتيح إعادة التشغيل والمستهلكين المتأخرين.
- نموذج المستهلك: تدفع قوائم الانتظار الرسائل إلى المستهلكين. مستهلكو كافكا يسحبون دفعات بحجم قابل للتهيئة، مما يُبقي الوسيط عديم الحالة نسبةً إلى موضع المستهلك.
- الترتيب: معظم قوائم الانتظار تُقدم ترتيباً أفضل-ما-يمكن تحت الحمل العالي. يضمن كافكا ترتيباً صارماً داخل كل قسم.
- حالة الاستخدام: تتفوق قوائع الانتظار في توزيع المهام (مستهلك واحد ينفذ العمل). يتفوق كافكا في تدفق الأحداث (مستهلكون مستقلون كثيرون يراقبون نفس التدفق).
حلقة إيداع الإزاحة
فهم كيفية تتبع كافكا لتقدم المستهلكين أمر أساسي لبناء مستهلكين موثوقين. تسير الحلقة على النحو التالي:
- يستطلع المستهلك كافكا ويستقبل دفعة من السجلات حتى
max.poll.records(الافتراضي 500). - يعالج المستهلك جميع السجلات في الدفعة.
- يُيدع المستهلك أعلى إزاحة عالج بها بنجاح إلى كافكا (مخزنة في موضوع
__consumer_offsetsالداخلي). - إذا تعطل المستهلك قبل الإيداع، يُعاد تشغيله من آخر إزاحة مُودَعة — مما يعيد معالجة بعض السجلات (التسليم مرة على الأقل).
- إذا أودع المستهلك الإزاحة قبل نجاح المعالجة، فإن العطل يعني تخطي تلك السجلات (التسليم مرة على الأكثر).
معظم التطبيقات تختار التسليم مرة على الأقل مع المعالجة الانسيابية (idempotent) — وهو ما تتناوله الدرس السادس بعمق. أبرز معاملات التهيئة في كافكا هي enable.auto.commit (الافتراضي true، وهو خطر) والاستدعاءات الصريحة commitSync() / commitAsync() التي يجب تفضيلها في الإنتاج.
النسخ المتماثل والمتانة
كل قسم في كافكا له عامل نسخ قابل للتهيئة (عادةً 3). يحمل وسيط واحد نسخة القائد (leader) للقسم؛ وتمر جميع عمليات القراءة والكتابة عبر القائد. تحمل الوسطاء الأخرى نسخ تابعة (follower) تُنسخ السجل بشكل غير متزامن. إذا فشل وسيط القائد، ينتخب كافكا قائداً جديداً من مجموعة النسخ المتزامنة (ISR) في غضون ثوانٍ، دون فقدان أي بيانات طالما تم استيفاء min.insync.replicas قبل الفشل.
تهيئة المنتج acks=all (يُكتب أيضاً acks=-1) مع min.insync.replicas=2 هي الإعداد الإنتاجي القياسي: تُقرّ الكتابة فقط بعد أن تحتفظ بها نسختان على الأقل. عند acks=1 (القائد فحسب) تكون الإنتاجية أعلى لكنك تخاطر بفقدان آخر دفعة رسائل إذا تعطل القائد قبل لحاق التوابع.