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

خادم ويب للأداء وقابل للتمديد مع تعرج و python

نشر في 2025-03-22
تصفح:598

مقدمة

] أود أيضًا أن أفكر في نفسي كمطور للأنظمة ، والذي يعني ، من خلال تعريف أندرو كيلي ، مطورًا مهتمًا بفهم الأنظمة التي يعملون معها تمامًا. في هذه المدونة ، أشارككم أفكاري حول حل المشكلة التالية:

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

تحذير رئيسي - سيكون هناك

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

A performant and extensible Web Server with Zig and Python

وما هي القطع التي سنقوم بتجميعها من؟

    واجهة رسمية تشعر بها ، ولكن إذا كنت تريد الحد الأدنى من التبعيات - فهناك متعرج في شكل WASM HTMX.
  • خادم ويب متعرج ، مدمج عن كثب مع Linux kernel. هذا هو الجزء الأداء ، الذي سأركز عليه في هذه المدونة.
  • خلفية بيثون ، متكاملة مع التعرج. هذا هو الجزء المعقد.
  • التكامل مع أنظمة التنفيذ المتينة مثل الزمن وقابل للتدفق. هذا يساعد الموثوقية ، ولن تتم مناقشته في المدونة.
مع أدواتنا التي تقرر ، لنبدأ!

هل مبالغ فيها على أي حال؟

Zig ليس لديه دعم مستوى اللغة لـ Coroutines :( و Coroutines هو ما تم تصميمه مع كل خادم ويب للأداء. لذا ، فهل هناك فائدة من المحاولة؟

تمسك ، على ، دعنا أولاً نضع قبعة مبرمج الأنظمة الخاصة بنا. Coroutines ليست رصاصة فضية ، لا شيء. ما هي الفوائد والعيوب الفعلية المعنية؟

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

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

A performant and extensible Web Server with Zig and Python

The Go Runtime ، على سبيل المثال ، تعدد الإرسال goroutines على مؤشرات ترابط OS. تشارك المواضيع في جدول الصفحة ، وكذلك الموارد الأخرى المملوكة لعملية. إذا قدمنا ​​عزل وحدة المعالجة المركزية والتقارب مع المزيج - سيتم تشغيل مؤشرات الترابط بشكل مستمر على نوى وحدة المعالجة المركزية الخاصة بها ، فستبقى جميع هياكل بيانات نظام التشغيل في الذاكرة دون حاجة إلى تبديلها ، وسيقوم جدولة مساحة المستخدمين بتخصيص وقت وحدة المعالجة المركزية إلى Goroutines بدقة ، لأنه يستخدم تعاونية متعددة الطراز. هل المنافسة ممكنة؟

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

هل يمكننا التعاون مع النواة؟

سأزعم أن التجريد "الحقيقي" على مستوى OS لوحدة تنفيذ مستقلة ليس حتى مؤشر ترابط - إنه في الواقع عملية نظام التشغيل. في الواقع ، فإن التمييز هنا ليس واضحًا - كل ما يميز المواضيع والعمليات هو قيم PID و TID المختلفة. بالنسبة إلى واصفات الملفات ، يتم تحديد الذاكرة الافتراضية ، معالجات الإشارات ، الموارد المتعقبة - ما إذا كانت هذه منفصلة للطفل محددة في الوسيطات إلى Syscall "clone". وبالتالي ، سأستخدم مصطلح "العملية" ليعني مؤشر ترابط التنفيذ الذي يمتلك موارد النظام الخاصة به - في المقام الأول وقت وحدة المعالجة المركزية ، والذاكرة ، واصفات الملف المفتوحة.

A performant and extensible Web Server with Zig and Python

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

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

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

نموذج التزامن

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

A performant and extensible Web Server with Zig and Python

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

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

تعدد المهام التعاونية مع Linux

هناك Syscall Sched_yield ، والذي يسمح للمعلومات بالتخلي عن وحدة المعالجة المركزية عندما أكمل جزءًا من عمله. يبدو تعاونا تماما. هل يمكن أن يكون هناك طريقة لطلب شريحة زمنية بحجم معين أيضًا؟ في الواقع ، هناك - مع جدولة جدولة Sched_Deadline. هذه سياسة في الوقت الفعلي ، مما يعني أنه بالنسبة لشريحة وقت وحدة المعالجة المركزية المطلوبة ، يعمل الخيط دون انقطاع. ولكن إذا تم تجاوز الشريحة - يتم تشغيل الاستبقاء ، ويتم تبديل موضوعك وتجاهله. وإذا تم الإقلاع عن الشريحة - يمكن للمعلومات الاتصال Sched_yield بالإشارة إلى النهاية المبكرة ، مما يسمح للتشغيل. هذا يبدو وكأنه أفضل ما في العالمين - نموذج تعاوني و preeMtive.

A performant and extensible Web Server with Zig and Python

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

تحقق كلا الاستراتيجيتين نفس نهاية نموذج coroutine -

من خلال التعاون مع kernel ، من الممكن تشغيل مهام التطبيق بأقل انقطاع .

بيثون كلغة نصية مضمنة

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

A performant and extensible Web Server with Zig and Python

Python ، من ناحية أخرى ، هي واحدة من أكثر اللغات ديناميكية هناك. الفصول الدراسية ، الكائنات - جميعها قواميس أسفل الغطاء ، ويمكن معالجتها في وقت التشغيل كما تريد. هذا له عقوبة أداء ، ولكنه يجعل نمذجة العمل مع الفصول والأشياء والعديد من الحيل الذكية عملية. تعادله هو عكس ذلك - هناك القليل من الحيل الذكية في تعرج ، مما يمنحك أقصى قدر من التحكم. هل يمكننا الجمع بين صلاحياتهم من خلال جعلهم يتداخلون؟

في الواقع ، يمكننا ، بسبب دعم كلاهما C abi. يمكن أن يكون لدينا مترجم Python يعمل من داخل عملية التعرج ، وليس كعملية منفصلة ، مما يقلل من النفقات العامة في تكلفة وقت التشغيل ورمز الغراء. يتيح لنا ذلك أيضًا الاستفادة من مخصصات Zig المخصصة داخل Python - تعيين ساحة لمعالجة الطلب الفردي ، وبالتالي تقليل إذا لم تقم بإزالة النفقات العامة لجامع القمامة ، ووضع غطاء ذاكرة. سيكون أحد القيود الرئيسية هو خيوط التفريخ في وقت تشغيل Cpython لجمع القمامة و IO ، لكنني لم أجد أي دليل على ذلك. يمكننا ربط Python في حلقة حدث مخصصة في Zig ، مع تتبع الذاكرة لكل كوروتين ، من خلال الاستفادة من حقل "السياق" في الملخص. الاحتمالات غير محدودة.

خاتمة

ناقشنا مزايا التزامن ، والتوازي ، وأشكال مختلفة من التكامل مع kernel OS. يفتقر الاستكشاف إلى المعايير والرمز ، والتي آمل أن يعوضها في جودة الأفكار المقدمة. هل جربت أي شيء مماثل؟ ما هي أفكارك؟ ردود الفعل مرحبًا :)

مزيد من القراءة

    https://linux.die.net/man/2/clone
  • https://man7.org/linux/man-pages/man7/sched.7.html
  • https://man7.org/linux/man-pages/man2/sched_yield.2.html
  • https://rigtorp.se/low-latency-guide/
  • ]
  • https://hadar.gr/2017/lightweight-goroutines
بيان الافراج يتم استنساخ هذه المقالة على: https://dev.to/brogrammerjohn/a-performant-and-extensible-web-server-sig-sig-and-python-4adl؟1 إذا كان هناك أي انتهاك ، فيرجى الاتصال بـ [email protected] لحذفها.
أحدث البرنامج التعليمي أكثر>

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

Copyright© 2022 湘ICP备2022001581号-3