أباتشي كافكا و RabbitMQ: دلالات وضمان تسليم الرسالة



أعددنا ترجمة الجزء التالي من المقالة التسلسلية ، والذي يقارن وظيفة Apache Kafka و RabbitMQ. يتناول هذا المنشور الدلالات وضمانات تسليم الرسائل. يرجى ملاحظة أن المؤلف أخذ في الحسبان كافكا حتى الإصدار 0.10 بما في ذلك ، وظهر بالضبط في الإصدار 0.11. ومع ذلك ، تظل المقالة ذات صلة ومليئة بالنقاط المفيدة من الناحية العملية.
الأجزاء السابقة: الأولى والثانية .

يوفر كل من RabbitMQ و Kafka ضمانات موثوقة لتسليم الرسائل. توفر كلتا المنصتين ضمانات على مبدأي "التسليم في المرة الواحدة على الأكثر" و "التسليم لمرة واحدة على الأقل" ، ولكن مع مبدأ "التسليم لمرة واحدة فقط" ، تنطبق ضمانات Kafka وفقًا لسيناريو محدود للغاية.

أولاً ، دعونا نتعرف على معنى هذه الضمانات:

  • في معظم مرة التسليم. هذا يعني أنه لا يمكن تسليم الرسالة أكثر من مرة. ومع ذلك ، قد يتم فقد الرسالة.
  • على الأقل مرة واحدة التسليم. هذا يعني أن الرسالة لن تضيع أبدًا. في هذه الحالة ، يمكن تسليم الرسالة أكثر من مرة.
  • بالضبط مرة واحدة التسليم. الكأس المقدسة لأنظمة المراسلة. يتم تسليم جميع الرسائل مرة واحدة بدقة.

من المحتمل ألا تكون كلمة "تسليم" هنا مصطلحًا دقيقًا. سيكون أكثر دقة لقول "المعالجة". على أي حال ، فإن ما يهمنا الآن هو ما إذا كان بإمكان المستهلك معالجة الرسائل وبأي مبدأ يحدث هذا: "لا أكثر من واحد" ، "واحد على الأقل" أو "مرة واحدة بدقة". لكن كلمة "معالجة" تعقد التصور ، ولن يكون تعبير "التسليم وفقًا لمبدأ" مرة واحدة فقط "في هذه الحالة تعريفًا دقيقًا ، لأنه قد يكون من الضروري تسليم الرسالة مرتين حتى تتم معالجتها بشكل صحيح مرة واحدة. إذا تم قطع اتصال المستلم أثناء المعالجة ، فيجب إرسال الرسالة مرة أخرى إلى المستلم الجديد.

والثاني. مناقشة مسألة معالجة الرسائل ، نأتي إلى موضوع الفشل الجزئي ، وهو صداع للمطورين. في عملية معالجة الرسالة هناك عدة مراحل. يتكون من جلسات التواصل بين التطبيق ونظام الرسائل في البداية وفي النهاية والتطبيق نفسه يعمل مع البيانات في الوسط. يجب معالجة سيناريوهات فشل التطبيق الجزئي بواسطة التطبيق نفسه. إذا كانت العمليات المنفذة معاملات بالكامل وتمت صياغة النتائج وفقًا لمبدأ "كل شيء أو لا شيء" ، فيمكن تجنب حالات الفشل الجزئي في منطق التطبيق. ولكن في كثير من الأحيان ، تشمل العديد من المراحل التفاعل مع الأنظمة الأخرى ، حيث تكون المعاملة مستحيلة. إذا أدرجنا في التفاعل العلاقات بين أنظمة المراسلة والتطبيقات وذاكرة التخزين المؤقت وقاعدة البيانات ، فهل يمكننا أن نضمن المعالجة على أساس "مرة واحدة فقط"؟ الجواب هو لا.

تقتصر استراتيجية "مرة واحدة بدقة" على سيناريو يكون فيه المستلم الوحيد للرسائل التي تتم معالجتها هو نظام المراسلة نفسه ، ويوفر هذا النظام الأساسي معاملات كاملة. في هذا السيناريو المحدود ، يمكنك معالجة الرسائل وكتابتها وإرسال الإشارات التي تمت معالجتها كجزء من معاملة تتم بمبدأ "كل شيء أو لا شيء". يتم توفيرها من قبل مكتبة Kafka Streams.

ولكن إذا كانت معالجة الرسائل غير مناسبة دائمًا ، فيمكنك تجنب الحاجة إلى تنفيذ الاستراتيجية "مرة واحدة بدقة" من خلال المعاملات. إذا كانت المعالجة النهائية للرسائل غير مناسبة ، فيمكنك القلق بشأن قبول التكرارات. ولكن ليس كل الإجراءات يمكن تنفيذها بشكل غير صحيح.

تنبيه نهاية إلى نهاية

ما لم يتم تمثيله في أي أجهزة من جميع أنظمة المراسلة التي عملت معها هو التأكيد من طرف إلى طرف. بالنظر إلى أنه يمكن وضع قائمة انتظار في رسالة في RabbitMQ ، فإن الإخطار من طرف إلى طرف لا معنى له. في Kafka ، بالمثل ، يمكن لمجموعات مختلفة من المستلمين قراءة المعلومات في وقت واحد من موضوع واحد. في تجربتي ، التنبيهات من طرف إلى طرف هي ما يسأل عنه الأشخاص الجدد على مفهوم المراسلة. في مثل هذه الحالات ، من الأفضل أن توضح على الفور أن هذا غير ممكن.

سلسلة من المسؤولية

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

رسالة نقل الإجراء

تم تخصيص هذه المقالة بشكل أساسي لكيفية قيام كل منصة بتوفير استراتيجيات إرسال "واحدة على الأقل" و "لا أكثر من واحدة". ولكن لا يزال هناك أمر مراسلة. في الأجزاء السابقة من هذه السلسلة ، كتبت عن الترتيب الذي يتم به إرسال الرسائل والترتيب الذي تتم معالجتها به ، وأنصحك بالرجوع إلى هذه الأجزاء.

باختصار ، يوفر كل من RabbitMQ و Kafka ضمانًا أولي من أصل (FIFO). يحتفظ RabbitMQ بهذا الترتيب على مستوى قائمة الانتظار ، و Kafka على مستوى التجزئة. تمت مناقشة الآثار المترتبة على قرارات التصميم هذه في المقالات السابقة.

ضمانات التسليم في RabbitMQ

يتم توفير ضمانات التسليم:

  • موثوقية الرسالة - لن تختفي أثناء تخزينها على RabbitMQ ؛
  • إشعارات الرسائل - يتبادل RabbitMQ الإشارات مع المرسلين والمستلمين.

عناصر الموثوقية


قائمة انتظار النسخ المتطابق

يمكن عمل نسخ متطابقة لقوائم الانتظار (النسخ المتماثل) على العديد من العقد (الخوادم). تحتوي كل قائمة انتظار على قائمة انتظار بادئة في أحد العقد. على سبيل المثال ، هناك ثلاث عقدات و 10 قوائم انتظار ونسختين متماثلتين لكل قائمة انتظار. سيتم توزيع 10 قوائم انتظار التحكم و 20 النسخ المتماثلة عبر ثلاثة العقد. يمكن تكوين توزيع قوائم التحكم بواسطة العقد. في حالة تجميد العقدة:

  • بدلاً من كل قائمة انتظار بادئة على عقدة التعليق ، يتم توفير نسخة متماثلة من قائمة الانتظار هذه على عقدة أخرى ؛
  • يتم إنشاء نسخ متماثلة جديدة على العقد الأخرى لاستبدال النسخ المتماثلة المفقودة على العقدة الصادرة ، وبالتالي دعم عامل النسخ المتماثل.

ستتم مناقشة مسألة التسامح مع الخطأ في الجزء التالي من المقال.

قوائم انتظار موثوقة

هناك نوعان من قوائم الانتظار على RabbitMQ: موثوقة وغير موثوق بها. تتم كتابة قوائم الانتظار الموثوق بها على القرص وحفظها في حالة إعادة تشغيل عقدة. عندما تبدأ العقدة ، يتم تجاوزها.

الرسائل المستمرة

إذا كانت قائمة الانتظار موثوقة ، فإن هذا لا يعني أن رسائلها يتم حفظها عند إعادة تشغيل العقدة. سيتم استرداد الرسائل التي تم تعليمها على أنها ثابتة من قبل المرسل فقط.

عند العمل على RabbitMQ ، كلما زادت موثوقية الرسالة ، انخفض الأداء المحتمل. إذا كان هناك دفق من الأحداث في الوقت الفعلي وليس من الضروري فقدان العديد منها أو فاصل زمني صغير من الدفق ، فمن الأفضل عدم استخدام النسخ المتماثل لقائمة الانتظار ونقل جميع الرسائل باعتبارها غير مستقرة. ولكن إذا كان من غير المرغوب فيه فقدان الرسائل بسبب حدوث خلل في العقدة ، فمن الأفضل استخدام قوائم انتظار موثوق بها مع النسخ المتماثل والرسائل المستقرة.

إخطارات الرسالة


المراسلة

قد يتم فقد الرسائل أو تكرارها أثناء الإرسال. ذلك يعتمد على سلوك المرسل.

"أطلق عليه الرصاص ونسيت"

قد يقرر المصدر عدم طلب التأكيد من المستلم (إشعار باستلام رسالة إلى المرسل) وإرسال الرسالة تلقائيًا. لن يتم تكرار الرسائل ، ولكن قد يتم فقدها (والتي تفي بالاستراتيجية "كحد أقصى للتسليم لمرة واحدة").

تأكيدات إلى المرسل

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

  • basic.ack. تأكيد إيجابي. تم استلام الرسالة ، وتقع المسؤولية عنها الآن على عاتق RabbitMQ ؛
  • basic.nack. تأكيد سلبي. حدث شيء ولم تتم معالجة الرسالة. المسؤولية عن ذلك تبقى في المصدر. إذا رغبت في ذلك ، يمكنه إرسال رسالة مرة ثانية.

بالإضافة إلى إخطارات التسليم الإيجابية والسلبية ، يتم توفير رسالة basic.return. في بعض الأحيان يحتاج المرسل إلى معرفة ليس فقط أن الرسالة وصلت إلى RabbitMQ ، ولكن أيضًا سقطت في قائمة انتظار واحدة أو أكثر. قد يحدث أن يرسل المصدر رسالة إلى نظام تبادل الموضوعات ، حيث لا يتم توجيه الرسالة إلى أي من قوائم انتظار التسليم. في هذه الحالة ، يتخلص الوسيط ببساطة من الرسالة. في بعض السيناريوهات ، يكون هذا أمرًا طبيعيًا ، في حالات أخرى ، يجب أن يعرف المصدر ما إذا كانت الرسالة قد تم تجاهلها والمضي قدمًا وفقًا لذلك. يمكنك تعيين علامة "إلزامي" للرسائل الفردية ، وإذا لم يتم تعريف الرسالة في أي قائمة انتظار للتسليم ، فسيتم إرجاع basic.return إلى المرسل.

قد ينتظر المصدر التأكيد بعد إرسال كل رسالة ، ولكن هذا سيقلل من أدائه إلى حد كبير. بدلاً من ذلك ، يمكن للمصادر إرسال دفق ثابت من الرسائل ، مع وضع حد لعدد الرسائل غير المعترف بها. عند الوصول إلى الحد الأقصى للرسالة المؤقتة ، سيتم إيقاف الإرسال مؤقتًا حتى يتم استلام جميع التأكيدات.

الآن وبعد أن أصبحت هناك العديد من الرسائل التي يتم نقلها من المرسل إلى RabbitMQ ، يتم تجميع التأكيدات باستخدام علامة متعددة لتحسين الأداء. جميع الرسائل المرسلة عبر القناة يتم تعيين قيمة عدد صحيح متزايد رتيبة ، "رقم تسلسل". يتضمن إشعار الرسالة رقم تسلسل الرسالة المقابلة. وإذا كانت القيمة في نفس الوقت متعددة = صواب ، يجب على المرسل تتبع أرقام تسلسل رسائله لمعرفة الرسائل التي تم تسليمها بنجاح وأيها لا. كتبت مقالا مفصلا حول هذا الموضوع.

بفضل التأكيدات ، نتجنب فقد الرسائل بالطرق التالية:

  • إعادة إرسال الرسائل في حالة الإخطار السلبي ؛
  • استمرار تخزين الرسائل في مكان ما في حالة وجود إشعار سلبي أو basic.return.

المعاملات

نادراً ما تستخدم المعاملات في RabbitMQ للأسباب التالية:

  • ضمانات ضعيفة. إذا تم توجيه الرسائل إلى طوابير متعددة أو تحتوي على رمز إلزامي ، فلن يتم دعم استمرارية المعاملة ؛
  • انخفاض الإنتاجية.

بصراحة ، لم أستخدمها أبدًا ، فهي لا تقدم أي ضمانات إضافية ، باستثناء التأكيدات إلى المرسل ، وتزيد فقط من عدم اليقين في مسألة كيفية تفسير الإقرار باستلام الرسائل الناشئة عن إتمام المعاملات.

أخطاء الاتصال / القناة

بالإضافة إلى إخطارات استلام الرسائل ، يجب على المرسل أن يضع في الاعتبار إخفاقات أدوات الاتصال والوسطاء. كل من هذه العوامل تؤدي إلى فقدان قناة الاتصال. مع فقد القنوات ، تختفي فرصة تلقي أي إخطارات لم تتلق بعد لتلقي الرسائل. هنا ، يجب أن يختار المرسل بين خطر فقد الرسالة وخطر التكرار.

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

وبالمثل ، فإن فشل وسائل الاتصال يؤثر على الوضع. هل حدث عطل أثناء إرسال الرسالة؟ أو بعد وضع الرسالة في قائمة الانتظار ، ولكن قبل تلقي إشعار إيجابي؟

لا يمكن للمرسل تحديد ذلك ، لذلك يجب عليه اختيار أحد الخيارات التالية:

  • لا تقم بإعادة توجيه الرسالة ، مما يؤدي إلى خطر فقدها ؛
  • إعادة إرسال الرسالة وإنشاء خطر الازدواجية.

إذا كانت العديد من رسائل المرسل في حالة عبور ، فستكون المشكلة أكثر تعقيدًا. الشيء الوحيد الذي يمكن للمرسل القيام به هو إعطاء المستلمين تلميحًا عن طريق إضافة رأس خاص للرسالة ، مما يشير إلى أنه يتم إرسال الرسالة مرة ثانية. يمكن للمستلمين أن يقرّروا التحقق من الرسائل لوجود هذه الرؤوس ، وإذا تم العثور عليها ، فتحققوا أيضًا من الرسائل المستلمة بحثًا عن التكرارات (إذا لم يتم إجراء هذا الفحص من قبل).

المستلمون


هناك خياران متاحان للمستلمين لتلقي الإخطارات:

  • لا يوجد وضع إخطار
  • وضع الإخطار اليدوي.

لا يوجد وضع إخطار

إنه وضع الإشعارات التلقائية. وهو خطير. بادئ ذي بدء ، لأنه عند وصول رسالة إلى التطبيق الخاص بك ، تتم إزالته من قائمة الانتظار. هذا يمكن أن يؤدي إلى فقدان الرسالة إذا:

  • تمت مقاطعة الاتصال قبل استلام الرسالة ؛
  • الرسالة لا تزال في المخزن المؤقت الداخلي ، وتم تعطيل التطبيق ؛
  • فشلت معالجة الرسائل.

بالإضافة إلى ذلك ، فإننا نفقد آليات الضغط الخلفي كوسيلة لمراقبة جودة تسليم الرسائل. من خلال ضبط وضع إرسال الإشعارات يدويًا ، يمكنك ضبط الجلب المسبق (أو ضبط مستوى الخدمات المقدمة ، جودة الخدمة) للحد من عدد الرسائل لمرة واحدة التي لم يؤكد النظام استلامها بعد. بدون ذلك ، يرسل RabbitMQ رسائل بأسرع ما يسمح به الاتصال ، ويمكن أن يكون ذلك أسرع من أن يتمكن المتلقي من معالجتها. نتيجة لذلك ، تكون المخازن المؤقتة ممتلئة وتحدث أخطاء في الذاكرة.

وضع الإخطار اليدوي

يجب على المستلم إرسال إشعار باستلام كل رسالة يدويًا. يمكنه ضبط الجلب المسبق في حال كان عدد الرسائل أكثر من واحدة ، ومعالجة العديد من الرسائل في نفس الوقت. يمكنه أن يقرر إرسال إخطار لكل رسالة ، أو يمكنه تطبيق إشارة متعددة وإرسال عدة إشعارات في وقت واحد. تجميع الإعلامات يحسن الأداء.

عندما يفتح المستلم القناة ، تحتوي الرسائل التي تمر عبرها على معلمة "علامة التسليم" ، والتي تكون قيمتها عددًا صحيحًا يزداد رتابة. يتم تضمينه في كل إشعار استلام ويستخدم معرف الرسالة.

قد تكون الإخطارات كما يلي:

  • basic.ack. بعد ذلك ، RabbitMQ يزيل الرسالة من قائمة الانتظار. يمكن تطبيق العلم المتعدد هنا.
  • basic.nack. يجب على المستلم تعيين علامة لإخبار RabbitMQ ما إذا كان يجب وضع قائمة الانتظار في الرسالة مرة أخرى. عند إعادة تعيين الرسالة ، تنتقل الرسالة إلى بداية قائمة الانتظار. من هناك يتم إرسالها إلى المستلم مرة أخرى (حتى لنفس المستلم). يدعم إعلام basic.nack الإشارة المتعددة.
  • basic.reject. مثل basic.nack ، لكن لا يدعم العلامة المتعددة.

وبالتالي ، فإن basic.ack و basic.nack مع دلالة requeue = false هما نفس الشيء. كلا المشغلين يعني إزالة رسالة من قائمة الانتظار.

السؤال التالي هو متى يتم إرسال إعلامات الاستلام. إذا تمت معالجة الرسالة بسرعة ، فقد ترغب في إرسال إشعار على الفور بعد الانتهاء من هذه العملية (ناجحة أو غير ناجحة). ولكن إذا كانت الرسالة في قائمة انتظار RabbitMQ والمعالجة تستغرق عدة دقائق؟ سيكون إرسال إشعار بعد ذلك مشكلة ، لأنه إذا تم إغلاق القناة ، فستتم إعادة جميع الرسائل التي لم يتم إرسال إعلامات إليها إلى قائمة الانتظار ، وسيتم إرسالها مرة ثانية.

خطأ في اتصال / رسالة الوسيط

إذا تم قطع الاتصال أو حدث خطأ في الوسيط ، وبعد ذلك توقفت القناة عن العمل ، فإن جميع الرسائل التي لم يتم تأكيد استلامها يتم وضعها في قائمة الانتظار وإعادة توجيهها. هذا جيد لأنه يمنع فقد البيانات ، ولكنه سيء ​​لأنه يسبب تكرارًا مفرطًا.

كلما طالت مدة استلام المستلم رسائل لفترة طويلة ، لم يؤكد استلامها ، زادت مخاطر إعادة التوجيه. عند إعادة إرسال رسالة ، يتم تعيين RabbitMQ لعلامة إعادة التوجيه على "صحيح". نتيجة لهذا ، لدى المستلم على الأقل إشارة إلى أن الرسالة ربما تمت معالجتها بالفعل.

العاطفة

إذا كانت هناك حاجة إلى العاطلين عن العمل وتضمن عدم فقد أي رسالة ، فيجب بناء بعض الشيكات المكررة أو المخططات العاطفية الأخرى. , , , .

الخاتمة


RabbitMQ , , , .

, :

  • , , , , , “ ”.
  • “ ”, .
  • , , , . , . “multiple”.

Kafka

:

  • — , , ;
  • — Kafka (, , Apache Zookeeper) / — .



RabbitMQ Kafka .

RabbitMQ - :

  • , . RabbitMQ , «multiple».
  • «prefetch» «multiple».

. “multiple”. TCP.

Kafka . , . RabbitMQ, , , . , .

Kafka , , . , . RabbitMQ API , . RabbitMQ .

,



Kafka - , , . . , , , , , .

Kafka (In Sync Replicas, ISR). . , , ( 10 ). , . - , .. . .



, Kafka , , , Kafka .



, Kafka, , :

  • , . Acks=0.
  • . Acks=1
  • . Acks=All

, RabbitMQ. , , ( , ). , .

Kafka . :

  • enable.idempotence “true”,
  • max.in.flight.requests.per.connection 5 ,
  • retries 1 ,
  • acks “all”.

, acks=0/1 , .



, , . ZooKeeper Kafka.

(), , :

  • . . . . , .
  • , . “ ”. , ; , . , 10 , 4 , , , ;
  • , . “ ”. , , , . , 10 , , 4 ;
  • . , .

“ ” Kafka Streams, Java. Java . “ ”, , , . , , “ ” . , , () .

, Kafka Streams, , , “ ”. Kafka: . , . , , , ( ), .



Kafka “--”. . , , .

“ ”, , (, , ). “ ”, , . .

: “ ” ? . , , . . (Last Stable Offset, LSO) — ; “ ” .

الاستنتاجات

. , , . , Kafka , .

لتلخيص


  • “ ” “ ”.
  • .
  • , . Kafka , .
  • , , .
  • .
  • Kafka , “--”. .
  • Kafka, - , ( ). RabbitMQ .
  • يمكن لـ Kafka زيادة فوائد الحزم نظرًا لقدرات توزيع الحزم الخاصة به ، ولا يحتوي RabbitMQ على الحزم نظرًا لنموذج الاستلام السلبي الذي لا يمنع تعارضات المستلم.

Source: https://habr.com/ru/post/ar437446/


All Articles