بنية التخزين المؤقت والمراسلة

موثوقية كافكا وسيناريوهات الكوارث

18 دقيقة الدرس 7 من 30

موثوقية كافكا وسيناريوهات الكوارث

نموذج موثوقية كافكا بسيط في ظاهره: نسخ البيانات عبر وسطاء متعددين، وانتخاب قائد لكل قسم، والإقرار بالكتابات لعدد كافٍ من النسخ. لكن الدقة الحقيقية — ومصدر كل الانقطاعات في بيئات الإنتاج — تكمن في الفجوات بين هذه الخطوات. يتناول هذا الدرس السيناريوهات الثلاثة الأكثر تسبباً في فقدان البيانات وانعدام التوافر على نطاق الإنتاج الفعلي: انتخابات القادة غير النظيفة، والمسارات المتعددة لفقدان البيانات المُقرّة، وتعقيدات نسخ البيانات عبر مراكز البيانات.

انتخابات القائد غير النظيفة: المقايضة بين الأمان والتوافر

لكل قسم في كافكا قائد واحد بالضبط في أي لحظة. عند فشل هذا القائد، يجب على وحدة التحكم انتخاب قائد جديد. المسار الآمن: الانتخاب فقط من مجموعة النسخ المتزامنة (ISR) — وهي المجموعة الفرعية من النسخ التي لحقت بالقائد بالكامل. تضمن هذه النسخ احتواءها على جميع الرسائل التي أقرها القائد السابق، فلا يوجد فجوة في البيانات عند انتخاب قائد جديد منها.

المسار الخطير هو الانتخاب غير النظيف: انتخاب نسخة ليست في مجموعة ISR لأنه لا توجد نسخة متزامنة متاحة. يحدث هذا عندما يفشل القائد بينما تكون نسخه التابعة متأخرة — سيناريو يحدث بشكل أكثر مما تتوقع، خاصةً أثناء إعادة التشغيل المتدرجة أو أعطال الشبكة التي تتجاوز مدة replica.lag.time.max.ms (الافتراضي: 30 ثانية).

خطر في بيئة الإنتاج: الإعداد unclean.leader.election.enable=true هو الافتراضي في بعض توزيعات كافكا السابقة للإصدار 3.x. عند تفعيله، يمكن لوسيط متأخر كثيراً أن يفوز بانتخاب القيادة، ليصبح مرجعاً نهائياً مع التخلص الدائم من كل رسالة أقرها القائد السابق لكن هذه النسخة لم تستلمها قط. المنتجون لا يتلقون أي خطأ. المستهلكون يتجاوزون الرسائل في صمت. هذا فقدان دائم وصامت للبيانات.

اضبط unclean.leader.election.enable=false على مستوى الكلستر بالكامل لأي حمل عمل لا يُقبل فيه فقدان البيانات. المقايضة: إذا تقلصت مجموعة ISR إلى صفر (فشلت جميع النسخ المتزامنة في وقت واحد)، يصبح القسم غير متاح حتى يتعافى أحد أعضاء ISR. هذا هو السلوك الصحيح — عدم التوافر قابل للتعافي؛ أما فقدان البيانات فلا. احتفظ بالقيمة true فقط لمواضيع التسجيل ذات الإنتاجية العالية حيث تقبل صراحةً بالخسارة مقابل التوافر المستمر.

# server.properties — إعداد أساسي آمن لبيئة الإنتاج unclean.leader.election.enable=false default.replication.factor=3 min.insync.replicas=2 # تجاوز على مستوى الموضوع (يأخذ الأولوية على إعداد الوسيط) kafka-topics.sh --alter \ --bootstrap-server kafka-1:9092 \ --topic payments \ --config unclean.leader.election.enable=false \ --config min.insync.replicas=2 # فحص حالة ISR — الحالة الصحية مقابل المتدهورة kafka-topics.sh --describe \ --bootstrap-server kafka-1:9092 \ --topic payments # صحي: Partition: 0 Leader: 2 Replicas: 2,0,1 Isr: 2,0,1 # متدهور: Partition: 0 Leader: 2 Replicas: 2,0,1 Isr: 2 # (ISR=1 يعني أنك على بُعد فشل وسيط واحد من عدم التوافر)

سيناريوهات فقدان البيانات: التصنيف الكامل

الانتخابات غير النظيفة ليست سوى مسار واحد لفقدان البيانات. يحتفظ المشغلون المتمرسون بخريطة ذهنية لجميع المسارات، لأن كلاً منها يستلزم تخفيفاً مختلفاً:

  • acks=0 أو acks=1: مع acks=0، يُرسل المنتج الرسالة وينسى — أي فشل للوسيط قبل الكتابة على القرص يؤدي إلى فقدان الرسالة. مع acks=1، يُقرّ القائد بعد الكتابة في سجله الخاص لكن قبل نسخ أي تابع؛ إذا انهار القائد في تلك الفترة، تُفقد الرسالة رغم تلقي المنتج استجابة نجاح. استخدم acks=all (مكافئ لـ acks=-1) لأي موضوع مهم.
  • تقلص ISR دون min.insync.replicas: مع acks=all وmin.insync.replicas=2، تستلزم الكتابة إقراراً من نسختين على الأقل. إذا كانت نسخة واحدة فقط في ISR (بسبب فشل وسيط أو تأخر)، يتلقى المنتج استثناء NotEnoughReplicasException. هذا هو السلوك الصحيح — يرفض الكلستر الكتابة بدلاً من قبولها بشكل غير آمن. راقب حجم ISR عبر مقياس JMX: kafka.server:type=ReplicaManager,name=UnderReplicatedPartitions.
  • اقتطاع السجل عند ترقية التابع: عند انتخاب تابع قائداً، يقتطع سجله إلى آخر إزاحة ملتزمة — علامة الحد الأعلى للمياه. تُهمل أي رسائل كتبها القائد القديم بعد هذه العلامة. مع acks=all لا يحدث هذا بحكم التعريف، لكن مع acks=1 هو سيناريو خسارة حقيقي.
  • إيداع إزاحات المستهلك قبل اكتمال المعالجة: يمكن للمستهلك إيداع إزاحة قبل معالجة الرسالة المقابلة بالكامل أو تخزينها بشكل دائم. إذا انهارت العملية بعد الإيداع لكن قبل اكتمال الأثر الجانبي، تُفقد الرسالة فعلياً من منظور المستهلك. أودع الإزاحات فقط بعد أن يصبح أثرك الجانبي دائماً.
  • انتهاء صلاحية علامات الحذف في المواضيع المضغوطة: تحذف المواضيع المضغوطة سجلات الحذف بعد delete.retention.ms (الافتراضي: 24 ساعة). المستهلك الذي كان غير متصل لمدة أطول من هذا الحد يفوته الحذف ويحتفظ بحالة قديمة إلى أجل غير مسمى.
الفكرة الجوهرية: توليفة acks=all مع min.insync.replicas=N-1 (حيث N هو عامل النسخ) مع unclean.leader.election.enable=false تمنحك أقوى ضمان للمتانة يمكن لكافكا تقديمه. هذا هو الخط الأساسي لأي موضوع مالي أو تعاقدي حساس على نطاق كبير. شركات مثل Stripe وLinkedIn وكلسترات كافكا الداخلية لـ Confluent تعمل جميعها بهذه التهيئة.
مسارات فقدان البيانات في كافكا Kafka Durability Decision Tree Producer acks=all write Leader Broker 0 replicate Follower 1 Broker 1 (ISR) Follower 2 Broker 2 (ISR) ack returned only after ISR confirms (min.insync.replicas=2) Failure Scenarios & Outcomes acks=1 + Leader Crash Leader acked, followers not yet replicated. Result: DATA LOSS Unclean Election ISR=0, lagging replica elected as new leader. Result: DATA LOSS acks=all + min.isr=2 + no unclean Write rejected if ISR < 2. Partition unavailable if ISR=0. Result: NO DATA LOSS Key Config Summary unclean.leader.election.enable=false | acks=all | min.insync.replicas=2 | replication.factor=3 enable.idempotence=true | max.in.flight.requests.per.connection=5 | retries=Integer.MAX_VALUE JMX alert: UnderReplicatedPartitions > 0 | OfflinePartitionsCount > 0 | ActiveControllerCount != 1
مسارات تهيئة المتانة في كافكا: كيف تتفاعل إعدادات acks وحجم ISR وانتخاب القائد غير النظيف لتحديد ما إذا كانت البيانات آمنة أم مفقودة في صمت.

نسخ الكلستر المتعدد: MirrorMaker 2 في بيئة الإنتاج

كلستر كافكا المنفرد، مهما كان مضبوطاً جيداً، هو نطاق فشل واحد. تستلزم أعطال الشبكة وأعطال مناطق التوافر والأحداث على مستوى مركز البيانات توبولوجيا متعددة الكلسترات. الحل الأصلي لكافكا هو MirrorMaker 2 (MM2)، المُقدَّم في KIP-382 والمبني على إطار عمل Kafka Connect. يحل MM2 محل MirrorMaker الأصلي — الذي كانت لديه ثغرات خطيرة في ترسيم الإزاحات ونسخ مجموعات المستهلكين — وهو الأداة المعيارية في بيئة الإنتاج لدى LinkedIn وConfluent وAWS MSK.

يوفر MM2 ثلاث قدرات رئيسية افتقر إليها سلفه:

  • ترجمة الإزاحات: تُنسخ إزاحات مجموعات المستهلكين بين الكلسترات مع طبقة ترجمة تأخذ في الحسبان أن نفس الرسالة المنطقية قد تحمل إزاحات مختلفة في الكلسترات المصدر والهدف. هذا ضروري للتعافي من الأعطال — يمكن للمستهلكين الاستئناف من الموضع الصحيح في الكلستر الهدف دون إعادة تشغيل الموضوع بأكمله.
  • عزل مساحات أسماء المواضيع: تُضاف بادئة اسم الكلستر المصدر إلى المواضيع (مثل us-east.payments في كلستر eu-west)، مما يمنع التعارضات ويجعل التوبولوجيا صريحة.
  • مواضيع النبضات والنقاط التفتيشية: يكتب MM2 سجلات اصطناعية في mm2-heartbeats وmm2-checkpoints — تستخدمها أدوات التعافي من الأعطال (مثل API الخاص بـ RemoteClusterUtils) لحساب ترجمة إزاحات المستهلك عند الطلب.
# mm2.properties — نسخ نشط/سلبي (us-east -> eu-west) clusters = us-east, eu-west us-east.bootstrap.servers = kafka-us-east-1:9092,kafka-us-east-2:9092,kafka-us-east-3:9092 eu-west.bootstrap.servers = kafka-eu-west-1:9092,kafka-eu-west-2:9092,kafka-eu-west-3:9092 # نسخ جميع المواضيع من us-east إلى eu-west (نسخ احتياطي للتعافي من الكوارث) us-east->eu-west.enabled = true us-east->eu-west.topics = .* us-east->eu-west.groups = .* # إعداد نسخ المواضيع replication.factor = 3 tasks.max = 8 # فترة مزامنة الإزاحات — أقل = تعافٍ أسرع، أكبر = عبء أقل emit.checkpoints.interval.seconds = 10 emit.heartbeats.interval.seconds = 5 # عدم نسخ مواضيع MM2 الداخلية (يمنع الحلقات) us-east->eu-west.topics.blacklist = mm2-.*,__.* # تشغيل MM2 ككلستر Connect موزع connect-mirror-maker.sh mm2.properties # بعد التعافي: ترجمة الإزاحات لمجموعة المستهلك 'payment-processor' kafka-consumer-groups.sh \ --bootstrap-server kafka-eu-west-1:9092 \ --group payment-processor \ --reset-offsets \ --to-datetime 2025-11-01T00:00:00.000 \ --topic us-east.payments \ --execute

النشط/السلبي مقابل النشط/النشط: المقايضة التشغيلية

يدعم MM2 كلا التوبولوجيتين، والاختيار له تداعيات تشغيلية كبيرة:

النشط/السلبي أبسط تشغيلياً. كلستر واحد هو مرجع الحقيقة؛ والآخر نسخة احتياطية دافئة. التعافي من الأعطال إجراء تشغيلي متعمد: إعادة توجيه المنتجين إلى الكلستر الاحتياطي، والتحقق من ترجمة إزاحات المستهلكين، وقطع DNS. الخطر أن الكلستر الاحتياطي لا يُختبر أبداً تحت حمل الإنتاج الفعلي، مما يجعل التعافي أبطأ وأكثر عرضة للأخطاء عند الحاجة الفعلية. استهدف إجراء تمرين تعافٍ من الكوارث بانتظام — بحد أدنى ربع سنوي — للتحقق من صحة دليل التشغيل.

النشط/النشط يوجه مساحات أسماء المواضيع المختلفة إلى كلسترات مختلفة ويسمح لكل كلستر بنسخ مساحات الأسماء الخاصة بالآخر. يمنحك هذا صفر RPO للقراءات والتوزيع التلقائي، لكنه يُدخل أصعب مشكلة في الأنظمة الموزعة: تجنب حلقات الرسائل. يستخدم MM2 بادئات المواضيع وإعداد replication.policy.class لكسر الحلقات، لكن هذا يجب اختباره صراحةً.

ممارسة احترافية: راقب تأخر نسخ MM2 باستخدام مقياس JMX: kafka.connect:type=MirrorSourceConnector,target=(*),topic=(*),name=replication-latency-ms. أنشئ تنبيهاً عند تجاوز التأخر ميزانية RTO الخاصة بك. على نطاق LinkedIn (تريليونات الرسائل يومياً عبر مراكز البيانات)، هذا المقياس موجود على كل لوحة تحكم SRE. ارتفاع التأخر المستمر هو عادةً أول علامة على انهيار شبكي بطيء أو فشل وسيط في الكلستر الهدف قبل أن يُبلغ الكلستر نفسه عن تدهور.

دليل تشغيل التعافي من الكوارث: الخمس عشرة دقيقة الأولى

عند توقف الكلستر الأساسي، تسلسل القرارات لا يقل أهمية عن الميكانيكا التقنية:

  1. تأكيد النطاق (الدقائق 0-2): هل هو وسيط واحد، أم الكلستر بأكمله، أم مسار الشبكة بين الكلسترات؟ افحص ActiveControllerCount وOfflinePartitionsCount ومقاييس تأخر MM2. فشل وسيط واحد في كلستر مُهيَّأ بشكل صحيح يُصلح نفسه — لا تُطلق التعافي من الكوارث.
  2. إعلان الحادثة وتجميد الكتابات (الدقائق 2-5): إذا تأكد الفشل على مستوى الكلستر، نسّق مع المناوب لتجميد الكتابات على الكلستر الأساسي. يمنع هذا المنتجين من الاستمرار في الكتابة على كلستر مجهول المصير، مما يُعقّد تسوية الإزاحات لاحقاً.
  3. ترجمة إزاحات المستهلكين (الدقائق 5-10): استخدم RemoteClusterUtils.translateOffsets() أو سير عمل kafka-consumer-groups.sh --reset-offsets لتعيين نقاط تفتيش مجموعات المستهلكين من الكلستر الأساسي إلى كلستر التعافي. هذه هي الخطوة التي تتدرب عليها معظم الفرق بشكل أقل والتي تُخطئ فيها أكثر تحت الضغط.
  4. إعادة توجيه حركة المرور والتحقق (الدقائق 10-15): حدّث تهيئة خادم الإقلاع في عملاء المنتجين والمستهلكين (عادةً عبر اكتشاف الخدمات أو تهيئة البيئة)، وأعد تشغيل المستهلكين على كلستر التعافي، وتحقق من تقدم مجموعات المستهلكين واستعادة مقاييس الأعمال الرئيسية (مثل معدل معالجة المدفوعات).
الفكرة الجوهرية: الفرق بين التعافي في 15 دقيقة وفي 4 ساعات لا يكمن تقريباً في البنية التحتية — بل في وضوح دليل التشغيل وتكرار التمارين. يجب تنفيذ كل إجراء لتعافي الكلستر في بيئة التطوير بحد أدنى مرة واحدة في الربع. الفرق التي تتعافى بأسرع وقت هي تلك التي يعدّ تحولها إجراءً مملاً، لأنهم نفذوه عشرات المرات.