يُعَد Apache Kafka منصة بث أحداث عالية الأداء وقابلة للتوسع بشكل كبير. لإطلاق العنان لإمكانات Kafka الكاملة، عليك أن تفكِّر بعناية في تصميم التطبيق الخاص بك. من السهل جدًا كتابة تطبيقات Kafka التي تعمل بشكل سيئ أو تصطدم في النهاية بحائط التوسع. منذ عام 2015، تقدِّم IBM خدمة IBM Event Streams، وهي خدمة Apache Kafka مُدارة بالكامل تعمل على IBM Cloud. منذ ذلك الحين، ساعدت الخدمة العديد من العملاء، بالإضافة إلى الفِرق داخل IBM، على حل مشكلات التوسع والأداء مع تطبيقات Kafka التي كتبوها.
تتناول هذه المقالة بعض المشكلات الشائعة في Apache Kafka وتقدِّم بعض التوصيات حول كيفية تجنُّب الوقوع في مشكلات قابلية التوسع مع تطبيقاتك.
تعتمد بعض عمليات Kafka على قيام العميل بإرسال البيانات إلى الوسيط وانتظار الرد. قد تستغرق الرحلة الشبكية كاملة 10 ميلي ثانية، وهذا يبدو سريعًا، إلا إنه يقتصر على ما لا يزيد عن 100 عملية في الثانية. لهذا السبب، يُنصَح بمحاولة تجنُّب هذا النوع من العمليات كلما أمكن. لحسن الحظ، يوفر لك عملاء Kafka طرقًا لتجنُّب الانتظار في هذه الأوقات التي تستغرقها هذه الرحلات ذهابًا وإيابًا. ما عليك سوى التأكد من أنك تستفيد منها.
نصائح لتحقيق أقصى قدر من الإنتاجية:
إذا قرأت ما سبق وفكرت، "ألن يجعل ذلك تطبيقي أكثر تعقيدًا؟"، فالجواب نعم، من المحتمل أن يحدث ذلك. هناك مقايضة بين الإنتاجية وتعقيد التطبيق. ما يجعل زمن الرحلة الشبكية ذهابًا وإيابًا عقبة خطيرة هو أنه بمجرد الوصول إلى هذا الحد، قد يتطلب تحقيق تحسينات إضافية في الإنتاجية تغييرات واسعة في التطبيق.
إحدى الميزات المفيدة في Kafka هي أنه يراقب "حالة النشاط" للتطبيقات المستهلكة ويفصل أي تطبيق يمكن أن يكون قد تعطَّل. يتم ذلك من خلال متابعة الوسيط لآخر مرة قام فيها كل عميل مستهلك باستدعاء وظيفة "poll" (المصطلح المستخدم في Kafka لطلب المزيد من الرسائل). في حال لم يطلب العميل الرسائل بشكل متكرر بما فيه الكفاية، يعتبره الوسيط معطلًا ويقوم بفصله. تم تصميم هذا لتمكين العملاء الذين لا يواجهون مشكلات من التدخل واستلام المهام من العميل الذي تعطَّل.
للأسف، مع هذه الطريقة لا يستطيع وسيط Kafka التمييز بين العميل الذي يستغرق وقتًا طويلًا لمعالجة الرسائل التي استلمها والعميل الذي فشل فعليًا. ضَع في اعتبارك تطبيقًا مستهلكًا يعمل على حلقة: 1) يستدعي poll ويحصل على دفعة من الرسائل؛ أو 2) يعالج كل رسالة في الدفعة، مع استغراق ثانية واحدة لمعالجة كل رسالة.
إذا كان هذا المستهلك يستقبل دفعات من 10 رسائل، فسيكون الفاصل الزمني بين كل استدعاء لـ poll حوالي 10 ثوانٍ. بشكل افتراضي، يسمح Kafka بفاصل يصل إلى 300 ثانية (5 دقائق) بين الاستدعاءات لـ poll قبل فصل العميل، لذا كل شيء سيعمل بشكل طبيعي في هذا السيناريو. لكن ماذا يحدث في يوم مزدحم جدًا عندما يبدأ تراكم الرسائل على الموضوع الذي يستهلك منه التطبيق؟ بدلًا من استرجاع 10 رسائل فقط من كل استدعاء لـ poll، يحصل تطبيقك على 500 رسالة (وهو العدد الأقصى للرسائل التي يمكن إرجاعها بشكل افتراضي لكل استدعاء poll). وهذا سيؤدي إلى استغراق وقت معالجة كافٍ بحيث يقرر Kafka أن مثيل التطبيق قد فشل ويفصله. وهذه أخبار سيئة.
ستسعد عندما تعلم أن الأمر يمكن أن يزداد سوءًا. من الممكن أن يحدث نوع من حلقة تفاعلية. مع بدء Kafka بفصل العملاء لأنهم لا يستدعون poll بشكل متكرر بما يكفي، يقل عدد مثيلات التطبيق المتاحة لمعالجة الرسائل. تزداد احتمالية وجود تراكم كبير من الرسائل على الموضوع، ما يؤدي إلى زيادة احتمال حصول المزيد من العملاء على دفعات كبيرة من الرسائل واستغراقهم وقتًا طويلًا لمعالجتها. في نهاية المطاف، تدخل جميع مثيلات التطبيق المستهلك في حلقة إعادة تشغيل، ولا يتم إنجاز أي عمل مفيد.
ما الخطوات التي يمكن اتخاذها لتجنُّب حدوث ذلك؟
سنعود لموضوع فشل المستهلكين لاحقًا في هذا المقال، عندما نناقش كيف يمكن أن يؤدي ذلك إلى إعادة توازن مجموعة المستهلكين والتأثير المُربك الذي قد يحدث.
في الخلفية، يعمل البروتوكول الذي يستخدمه مستهلك Kafka لاستلام الرسائل عن طريق إرسال طلب "إحضار" إلى وسيط Kafka. كجزء من هذا الطلب، يوضِّح العميل ما يجب على الوسيط فعله إذا لم تتوفر رسائل، بما في ذلك المدة التي يجب أن ينتظرها الوسيط قبل إرسال استجابة فارغة. بشكل افتراضي، يوجِّه مستهلكو Kafka الوسطاء للانتظار حتى 500 ميلي ثانية (يتم التحكم بهذا عبر تكوين المستهلك "fetch.max.wait.ms") حتى يتوفر على الأقل بايت واحد من البيانات (يتم التحكم بذلك باستخدام "fetch.min.bytes") .
الانتظار لمدة 500 ميلي ثانية قد يبدو منطقيًا، لكن إذا كان لدى تطبيقك مستهلكون معظم الوقت في حالة خمول، وتمت توسعته ليصل إلى 5,000 مثيل، فهذا يعني احتمال وجود 2,500 طلب في الثانية لا تفعل أي شيء على الإطلاق. كل واحد من هذه الطلبات يستهلك وقت المعالج (CPU) في الوسيط لمعالجته، وفي أقصى الحالات قد يؤثِّر ذلك في أداء واستقرار عملاء Kafka الذين يريدون القيام بمهام فعلية.
عادةً، يتمثل نهج Kafka في التوسع بإضافة المزيد من الوسطاء، ثم إعادة توزيع أقسام الموضوع (partitions) بالتساوي عبر جميع الوسطاء، القدامى والجُدُد. للأسف، قد لا يساعد هذا النهج إذا كان عملاؤك يرسلون إلى Kafka طلبات "إحضار" غير ضرورية بكثرة. سيرسل كل عميل طلبات الإحضار إلى كل وسيط يقود قسم الموضوع (partition) الذي يستهلك منه الرسائل. لذلك، من الممكن أنه حتى بعد توسيع مجموعة Kafka وإعادة توزيع الأقسام، سيظل معظم العملاء يرسلون طلبات إحضار إلى معظم الوسطاء.
فما الذي يمكنك فعله إذن؟
إذا كنت معتادًا على أنظمة النشر والاشتراك الأخرى (مثل بروتوكول Message Queuing Telemetry Transport المعروف بالاختصار MQTT)، فقد تتوقع أن تكون موضوعات Kafka خفيفة جدًا، وشبه مؤقتة. إنها ليست كذلك. يتعامل Kafka يتعامل بشكل أفضل مع عدد من الموضوعات يُقاس بالآلاف. ومن المتوقع أيضًا أن تكون موضوعات Kafka طويلة العمر نسبيًا. الممارسات مثل إنشاء موضوع لتلقي رسالة واحدة فقط ثم حذف الموضوع نادرًا ما يتم استخدامها مع Kafka ولا تستفيد من نقاط القوة فيه.
بدلًا من ذلك، من الأفضل التخطيط لموضوعات ذات عمر طويل. ربما تشترك في مدة حياة التطبيق أو النشاط نفسه. كما يُنصَح بالحد من عدد الموضوعات ليصل إلى مئات أو ربما آلاف قليلة. وقد يتطلب هذا تبنّي منظور مختلف حول الرسائل المتداخلة في موضوع معين.
سؤال مرتبط يتكرر كثيرًا هو: كم عدد الأقسام التي يجب أن يحتويها موضوعي؟ تقليديًا، يُنصَح بالمبالغة في تقدير عدد الأقسام، لأن إضافة أقسام بعد إنشاء الموضوع لا تغيِّر تقسيم البيانات الموجودة بالفعل على الموضوع، وبالتالي قد يؤثِّر ذلك في المستهلكين الذين يعتمدون على التقسيم لضمان ترتيب الرسائل داخل القسم. هذه نصيحة جيدة؛ ومع ذلك، نود اقتراح بعض الاعتبارات الإضافية:
تستفيد معظم تطبيقات Kafka التي تستهلك الرسائل من قدرات مجموعات المستهلكين في Kafka لتنسيق استهلاك العملاء من أقسام الموضوع المختلفة. إذا كان لديك بعض الغموض حول مجموعات المستهلكين، فإليك تذكيرًا سريعًا بالنقاط الرئيسية:
مع نضوج كافكا، تم ابتكار خوارزميات إعادة التوازن المتطورة بشكل متزايد (ولا يزال). في الإصدارات المبكرة من Kafka، عندما تعيد مجموعة المستهلكين توازنها، كان على جميع العملاء في المجموعة التوقف عن الاستهلاك، وكانت تقسيمات المواضيع تعاد توزيعها بين الأعضاء الجدد في المجموعة ويبدأ جميع العملاء في الاستهلاك مرة أخرى. وهذا النهج له عيبان (لا تقلق، فقد تم تحسينهما منذ ذلك الحين):
حققت خوارزميات إعادة التوازن الحديثة تحسينات كبيرة من خلال إضافة ما يُعرَف في مصطلحات Kafka بـ "الالتصاق" (stickiness) و"التعاون" (cooperation).
على الرغم من هذه التحسينات التي تم إدخالها على خوارزميات إعادة التوازن الأحدث، فإذا كانت تطبيقاتك تخضع بشكل متكرر لإعادة التوازن بين مجموعات المستهلكين، فستظل ترى تأثيرًا في إجمالي معدل نقل الرسائل وإهدار عرض النطاق الترددي للشبكة حيث يتجاهل العملاء بيانات الرسائل المخزّنة مؤقتًا ثم يعيدون إحضارها. وفيما يلي بعض الاقتراحات بشأن ما يمكنك فعله:
النشرة الإخبارية الخاصة بالمجال
ابقَ على اطلاع دائم على أبرز الاتجاهات في مجالات الذكاء الاصطناعي، والأتمتة، والبيانات، وغيرها الكثير من خلال رسالة Think الإخبارية. راجع بيان الخصوصية لشركة IBM.
سيصلك محتوى الاشتراك باللغة الإنجليزية. ستجد رابط إلغاء الاشتراك في كل رسالة إخبارية. يمكنك إدارة اشتراكاتك أو إلغاء اشتراكك من هنا. لمزيد من المعلومات، راجع بيان خصوصية IBM.
أنت الآن خبير في توسيع نطاق تطبيقات Kafka. أنت مدعو لتطبيق هذه النقاط وتجربة العرض المُدار بالكامل لنظام Kafka على IBM Cloud. إذا واجهتك أي صعوبات في الإعداد، فيُرجى الرجوع إلى دليل بدء التشغيل والأسئلة الشائعة.
يُعَد IBM® Event Streams برنامجًا لبث الأحداث تم تطويره بالاعتماد على Apache Kafka مفتوح المصدر. وهو متوفر كخدمة مُدارة بالكامل على IBM® Cloud أو يمكن استخدامه كاستضافة ذاتية.
أطلِق العنان لإمكانات الأعمال مع حلول التكامل من IBM، والتي تربط التطبيقات والأنظمة للوصول إلى البيانات الحساسة بسرعة وأمان.
اكتشِف قدرات جديدة وعزِّز مرونة الأعمال من خلال خدمات IBM الاستشارية للسحابة.