[ترجمة] نموذج المبعوث الخيوط

ترجمة المقال: نموذج Envoy للترابط - https://blog.envoyproxy.io/envoy-threading-model-a8d44b922310

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

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

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

وصف الخيوط (نظرة عامة على الخيوط)

[ترجمة] نموذج المبعوث الخيوط

يستخدم Envoy ثلاثة أنواع مختلفة من التدفقات:

  • الرئيسية الرئيسية): يدير هذا الخيط بداية ونهاية العملية ، كل معالجة واجهة برمجة تطبيقات XDS (خدمة xDiscovery) ، بما في ذلك DNS ، والتحقق الصحي ، والكتلة العامة وإدارة عملية الخدمة (وقت التشغيل) ، وإعادة تعيين الإحصائيات ، والإدارة وإدارة العمليات العامة - إشارات Linux ، وإعادة التشغيل السريع ، إلخ. كل ما يحدث في هذا الموضوع هو غير متزامن و "غير محجوب". بشكل عام ، ينسق الخيط الرئيسي جميع عمليات الوظائف الهامة التي لا تتطلب عددًا كبيرًا من وحدات المعالجة المركزية لإكمالها. هذا يسمح لكتابة معظم كود التحكم كما لو كان مترابطًا واحدًا.
  • عامل: بشكل افتراضي ، يقوم Envoy بإنشاء مؤشر ترابط عامل لكل مؤشر ترابط للأجهزة في النظام ، ويمكن التحكم في ذلك باستخدام الخيار --concurrency. يبدأ كل مؤشر ترابط عامل حلقة حدث "غير محظورة" (حلقة حدث) ، وهي مسؤولة عن الاستماع (الاستماع) إلى كل مستمع (مستمع) ، وقت كتابة هذا التقرير (29 يوليو 2017) لا يوجد تجزئة (تجزئة) المستمع (المستمع) ، وقبول الاتصالات الجديدة ، وإنشاء مثيل لمكدس المرشح للاتصال ، والتعامل مع جميع عمليات الإدخال / الإخراج (IO) خلال عمر الاتصال. مرة أخرى ، هذا يسمح لكتابة معظم كود معالجة الاتصال كما لو كان مترابطًا واحدًا.
  • ملف (ملف flusher): كل ملف يكتبه Envoy ، معظمه يصل إلى السجلات ، يحتوي حاليًا على مؤشر ترابط حظر مستقل. هذا يرجع إلى حقيقة أن الكتابة إلى الملفات المخزنة مؤقتًا بواسطة نظام الملفات حتى عند استخدام ملفات O_NONBLOCK يمكن في بعض الأحيان منع (تنهد). عندما تحتاج مؤشرات الترابط العاملة إلى الكتابة إلى ملف ، يتم نقل البيانات فعليًا إلى مخزن مؤقت في الذاكرة حيث يتم مسحها في النهاية عبر مؤشر الترابط تدفق الملف. هذا هو أحد مجالات التعليمات البرمجية حيث ، من الناحية الفنية ، يمكن لجميع خيوط العاملين حظر نفس القفل أثناء محاولة ملء مخزن الذاكرة المؤقت.

التعامل مع الاتصال

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

هذا له العديد من الآثار الهامة:

  • جميع تجمعات الاتصال في Envoy مخصصة لكل مؤشر ترابط عامل. وبالتالي ، على الرغم من أن تجمعات اتصال HTTP / 2 تقوم بإجراء اتصال واحد فقط لكل مضيف رئيسي في وقت واحد ، إذا كان هناك أربعة سلاسل عمليات عاملة ، فسيكون هناك أربعة اتصالات HTTP / 2 لكل مضيف رئيسي في حالة مستقرة.
  • السبب وراء عمل Envoy بهذه الطريقة هو أنه من خلال الاحتفاظ بكل شيء في مؤشر ترابط عامل واحد ، يمكن كتابة جميع التعليمات البرمجية تقريبًا خالية من الكتل وكأنها سلسلة واحدة. يجعل هذا التصميم من السهل كتابة الكثير من التعليمات البرمجية والمقاييس بشكل لا يصدق لعدد غير محدود تقريبًا من سلاسل رسائل العمال.
  • ومع ذلك ، فإن إحدى النتائج الرئيسية هي أنه من حيث كفاءة تجمع الذاكرة والوصلات ، فمن المهم جدًا في الواقع ضبط المعلمة --concurrency. سيؤدي وجود المزيد من مؤشرات الترابط العاملة أكثر من اللازم إلى إهدار الذاكرة وإنشاء المزيد من الاتصالات الخاملة وإبطاء معدل دخول تجمع الاتصال. في Lyft ، تعمل حاويات المبعوثين الجانبية الخاصة بنا بتزامن منخفض للغاية ، لذا فإن الأداء يتساوى تقريبًا مع الخدمات التي يجلسون بجوارها. نقوم بتشغيل Envoy كوكيل حافة فقط بأقصى قدر من التزامن.

ماذا يعني عدم الحجب

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

يستخدم Envoy العديد من أقفال المعالجة الطويلة:

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

التخزين المحلي الموضوع

نظرًا للطريقة التي يفصل بها Envoy مسؤوليات سلسلة الرسائل الرئيسية عن مسؤوليات سلسلة العمليات ، هناك شرط بإمكانية إجراء معالجة معقدة على الخيط الرئيسي ثم توفيرها لكل سلسلة ترابط عامل بدرجة عالية من التزامن. يصف هذا القسم نظام Envoy Thread Local Storage (TLS) بمستوى عالٍ. في القسم التالي ، سأصف كيف يتم استخدامه لإدارة الكتلة.
[ترجمة] نموذج المبعوث الخيوط

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

يعمل نظام TLS (التخزين المحلي للخيط) Envoy على النحو التالي:

  • يمكن للكود الذي يعمل على الخيط الرئيسي تخصيص فتحة TLS للعملية بأكملها. على الرغم من أن هذا مجرد تجريدي ، إلا أنه من الناحية العملية هو فهرس إلى متجه ، مما يوفر وصول O (1).
  • يمكن للخيط الرئيسي تعيين البيانات التعسفية إلى الفتحة الخاصة به. عند القيام بذلك ، يتم نشر البيانات على كل مؤشر ترابط عامل كحدث عادي في حلقة الحدث.
  • يمكن أن تقرأ سلاسل رسائل العمال من فتحة TLS الخاصة بهم واسترداد أي بيانات خيطية محلية متاحة هناك.

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

يستخدم Envoy هذا بطريقتين مختلفتين:

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

خيوط تحديث الكتلة

في هذا القسم ، سوف أصف كيفية استخدام TLS (التخزين المحلي للخيط) لإدارة مجموعة. تتضمن إدارة الكتلة xDS و / أو معالجة DNS API وفحص الصحة.
[ترجمة] نموذج المبعوث الخيوط

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

  1. مدير الكتلة هو مكون داخل Envoy يدير جميع عمليات التجميع المعروفة ، وواجهة برمجة تطبيقات CDS (خدمة اكتشاف الكتلة) وواجهة برمجة تطبيقات SDS (خدمة اكتشاف نقطة النهاية) و DNS وفحوصات خارجية نشطة. وهي مسؤولة عن إنشاء عرض "متسق في نهاية المطاف" لكل عنقود منبع ، والذي يتضمن المضيفين المكتشفين بالإضافة إلى الحالة الصحية.
  2. يجري المدقق الصحي فحصًا صحيًا نشطًا ويبلغ مدير المجموعة بتغييرات الحالة الصحية.
  3. يتم تنفيذ CDS (خدمة اكتشاف الكتلة) / SDS (خدمة الاكتشاف السرية) / EDS (خدمة اكتشاف نقطة النهاية) / DNS لتحديد عضوية الكتلة. يتم إرجاع تغيير الحالة إلى مدير الكتلة.
  4. يقوم كل مؤشر ترابط عامل بتنفيذ حلقة حدث باستمرار.
  5. عندما يقرر مدير الكتلة أن حالة الكتلة قد تغيرت ، فإنه ينشئ لقطة للقراءة فقط جديدة من الكتلة ويرسلها إلى كل مؤشر ترابط عامل.
  6. خلال فترة السكون التالية ، سيقوم مؤشر ترابط العامل بتحديث اللقطة في فتحة TLS المخصصة.
  7. أثناء حدث I / O الذي يحتاج إلى تحديد المضيف لتحميل رصيد التحميل ، سيقوم موازن التحميل بالاستعلام عن فتحة TLS (التخزين المحلي لمؤشر الترابط) للحصول على معلومات حول المضيف. هذا لا يتطلب أقفال. لاحظ أيضًا أن TLS يمكنها أيضًا إطلاق أحداث عند التحديث بحيث يمكن لموازن التحميل والمكونات الأخرى إعادة حساب ذاكرة التخزين المؤقت وهياكل البيانات وما إلى ذلك. هذا خارج نطاق هذا المنشور ، ولكن يتم استخدامه في أماكن مختلفة في الكود.

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

الأنظمة الفرعية الأخرى التي تستخدم TLS

يتم استخدام TLS (التخزين المحلي للخيط) و RCU (قراءة تحديث النسخ) على نطاق واسع في Envoy.

أمثلة على الاستخدام:

  • آلية تغيير الوظائف أثناء التنفيذ: يتم تقييم القائمة الحالية للميزات الممكّنة في السلسلة الرئيسية. يتم بعد ذلك تزويد كل خيط عامل بلقطة للقراءة فقط باستخدام دلالات RCU.
  • استبدال جداول التوجيه: بالنسبة لجداول المسارات التي توفرها RDS (خدمة اكتشاف المسار) ، يتم إنشاء جداول التوجيه في الخيط الرئيسي. سيتم بعد ذلك توفير لقطة للقراءة فقط لكل مؤشر ترابط عامل باستخدام دلالات RCU (قراءة تحديث النسخ). هذا يجعل تغيير جداول التوجيه فعالاً ذريًا.
  • التخزين المؤقت لرأس HTTP: كما اتضح ، فإن حساب رأس HTTP لكل طلب (عند 25K + RPS لكل نواة) مكلف للغاية. يحسب Envoy مركزيًا رأسًا كل نصف ثانية تقريبًا ويوفره لكل عامل عبر TLS و RCU.

هناك حالات أخرى ، ولكن يجب أن توفر الأمثلة السابقة فهمًا جيدًا لما يتم استخدام TLS من أجله.

عيوب الأداء المعروفة

بينما يعمل Envoy جيدًا بشكل عام ، إلا أن هناك عددًا قليلاً من المجالات البارزة التي تتطلب الانتباه عند استخدامها بتزامن وإنتاجية عالية جدًا:

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

خاتمة

تم تصميم نموذج Envoy للترابط لسهولة البرمجة والتوازي الهائل على حساب الذاكرة والوصلات التي قد تكون مهدرة إذا لم يتم تكوينها بشكل صحيح. هذا النموذج يسمح لها بأداء جيد للغاية في عدد كبير جدا من الخيوط والإنتاجية.
كما ذكرت بإيجاز على Twitter ، يمكن أيضًا تشغيل التصميم أعلى مكدس شبكات وضع المستخدم كامل الميزات مثل DPDK (Data Plane Development Kit) ، مما قد يؤدي إلى معالجة الخوادم العادية لملايين الطلبات في الثانية مع معالجة L7 الكاملة . سيكون من الممتع للغاية رؤية ما سيتم بناؤه في السنوات القليلة المقبلة.
تعليق أخير سريع: لقد سُئلت عدة مرات عن سبب اختيارنا لـ ++ C لـ Envoy. لا يزال السبب هو أنها لا تزال اللغة الوحيدة المستخدمة على نطاق واسع لبناء البنية الموصوفة في هذا المنشور. من المؤكد أن C ++ ليست مناسبة لجميع أو حتى العديد من المشاريع ، ولكن في حالات استخدام معينة ، فهي لا تزال الأداة الوحيدة لإنجاز المهمة.

روابط للتعليمات البرمجية

روابط الملفات ذات الواجهات وتنفيذ الرؤوس التي تمت مناقشتها في هذا المنشور:

المصدر: www.habr.com

إضافة تعليق