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

بشكل عام ، كل شيء في nginx عبارة عن وحدات تم كتابتها من قبل شخص ما. لذلك ، فإن كتابة الوحدات النمطية تحت nginx ليست ممكنة فحسب ، ولكنها ضرورية أيضًا. عندما يكون من الضروري القيام بذلك ولماذا ، فإن
Vasily Soshnikov (
dedokOne ) سوف يخبرنا على سبيل المثال في العديد من الحالات.
دعنا نتحدث عن الأسباب التي تشجع على كتابة الوحدات النمطية في C والبنية الأساسية لـ nginx وتشريح وحدات HTTP ووحدات C و NJS و Lua و nginx.conf. من المهم أن تعرف ليس فقط لأولئك الذين يتطورون تحت nginx ، ولكن أيضًا لأولئك الذين يستخدمون nginx-configs أو Lua أو لغة أخرى داخل nginx.
ملاحظة: يستند المقال إلى تقرير من فاسيلي سوشنيكوف. يجري باستمرار تحديث التقرير وتحديثه. المعلومات الواردة في المادة تقنية تمامًا ومن أجل الحصول على أقصى استفادة منها ، يحتاج القراء إلى خبرة في العمل باستخدام شفرة nginx بمستوى متوسط وما فوق.لفترة وجيزة حول إنجن إكس
كل ما تستخدمه مع nginx هي وحدات . كل توجيه في تكوين nginx هو وحدة نمطية منفصلة ، تمت كتابتها بعناية بواسطة زملاء من مجتمع nginx.
التوجيهات في nginx.conf هي أيضًا وحدات تعمل على حل مشكلة معينة. لذلك ، في وحدات nginx هي كل شيء. add_header، proxy_pass ، أي توجيه - هذه هي الوحدات النمطية أو مجموعات الوحدات النمطية التي تعمل وفقًا لقواعد معينة.
Nginx هو إطار يحتوي على: Network & File I / O ، الذاكرة المشتركة ، التكوين والبرمجة النصية. هذه طبقة ضخمة من المكتبات منخفضة المستوى ، والتي يمكنك من خلالها فعل أي شيء للعمل مع محركات أقراص الشبكة.
Nginx سريع ومستقر ، لكنه معقد . يجب عليك كتابة مثل هذا الرمز حتى لا تفقد هذه الصفات من nginx. غير مستقر nginx على الإنتاج عملاء غير راضين ، وكل ما يلي من هذا.
لماذا إنشاء وحدات خاصة بك
تحويل بروتوكول HTTP إلى بروتوكول آخر. هذا هو السبب الرئيسي الذي يحفز في كثير من الأحيان إنشاء وحدة نمطية معينة.
على سبيل المثال ، تقوم الوحدة النمطية memcached_pass بتحويل HTTP إلى بروتوكول آخر ، ويمكنك العمل مع أنظمة خارجية أخرى. تسمح لك الوحدة النمطية proxy_pass أيضًا بالتحويل ، على الرغم من HTTP (s) إلى HTTP (s). مثال جيد آخر هو fastcgi_pass.
هذه كلها توجيهات للنموذج: "انتقل إلى مثل هذه الواجهة الخلفية ، حيث لا HTTP (ولكن في حالة proxy_pass HTTP)."
إدراج المحتوى الديناميكي: تجاوز AdBlock ، إدخال الإعلان. على سبيل المثال ، لدينا خلفية ويجب تعديل المحتوى الذي يأتي منها. على سبيل المثال ، AdBlock ، الذي يحلل شفرة إدراج الإعلان ، ونحن بحاجة إلى التعامل معها - لضبطها بطريقة أو بأخرى.
هناك شيء آخر يجب عليك فعله غالبًا لتضمين المحتوى هو مشكلة التخزين المؤقت لـ HLS. عندما يتم تخزين المعلمات داخل HLS ، يمكن لمستخدمين الحصول على نفس الجلسة أو نفس المعلمات. من هناك ، يمكنك قص أو إضافة بعض المعلمات عندما تحتاج إلى تتبع شيء ما.
جمع البيانات Clickstream من الإنترنت / متر المحمول. حالة شعبية في ممارستي. غالبًا ما يتم ذلك على nginx ، ولكن ليس على access.log ، ولكن قليلاً أكثر ذكاءً.
تحويل جميع أنواع المحتوى. على سبيل المثال ، تسمح لك وحدة rtmp لـ بالعمل ليس فقط مع rtmp ، ولكن أيضًا مع HLS. هذه الوحدة يمكن أن تفعل الكثير مع محتوى الفيديو.
نقطة التخويل العامة: SEP أو Api Gateway. هذا هو الحال عندما يعمل nginx كجزء من البنية التحتية: يرخص ويجمع المقاييس ويرسل البيانات إلى المراقبة و ClickStream. تعمل Nginx هنا كمحور للبنية التحتية - نقطة دخول واحدة للواجهات الخلفية.
إثراء طلبات تتبعهم اللاحقة. الأنظمة الحديثة معقدة للغاية ، مع عدة أنواع من الخلفية التي تشكل فرقًا مختلفة. كقاعدة عامة ، يصعب ظهورها لأول مرة ، وأحيانًا يكون من الصعب فهم من أين جاء الطلب ومن أين تم إرسال الطلب. لتبسيط تصحيح الأخطاء ، تستخدم بعض الشركات الكبيرة تقنية صعبة - فهي تضيف بيانات معينة إلى الطلبات. لن يراها المستخدم ، ولكن من هذه البيانات ، من السهل تتبع مسار الطلب داخل النظام. وهذا ما يسمى
تتبع .
S3-الوكيل. هذا العام ، أرى غالبًا الأشخاص الذين يعملون مع أشياءهم من خلال s3. ولكن ليس من الضروري القيام بذلك على وحدات C ، والبنية التحتية كافية في nginx كذلك. لحل بعض هذه المشاكل ، يمكنك استخدام Lua ، يتم حل شيء ما على NJS. لكن في بعض الأحيان يكون من الضروري كتابة وحدات في C.
متى هو الوقت المناسب لإنشاء وحدات
هناك معياران لفهم أن الوقت قد حان.
تعميم الوظيفة. عندما تفهم أن شخصًا آخر يحتاج إلى منتجك ، فيجب عليك تهريبه إلى "المصدر المفتوح" وإنشاء وظائف معممة ونشره والسماح باستخدامه.
حل مشاكل العمل. عندما يحدد النشاط التجاري هذه المتطلبات التي لا يمكن تلبيتها إلا عن طريق كتابة الوحدة الخاصة به لـ nginx. على سبيل المثال ، الإدراج / التغيير الديناميكي للمحتوى ، يمكن إجراء مجموعة ClickStream على Lua ، ولكن على الأرجح لن يعمل بشكل طبيعي.
العمارة Nginx
لقد كتبت رمز nginx لفترة طويلة. تسعة من وحداتي تدور في الإنتاج ، إحداها في المصدر المفتوح ، وفي الإنتاج للعديدين. لذلك ، لدي خبرة وفهم.
Nginx هي دمية تعشش فيها كل شيء مبني حول النواة.
لذلك أنا أفهم nginx.
الأساسية هي مغلفة على epoll.
Epoll هي طريقة تسمح لك بالعمل بشكل غير متزامن مع أي ملفات وصفية ، وليس فقط مآخذ توصيل ، لأن الواصف ليس مجرد مأخذ توصيل.
أعلى النواة هي upstreams و HTTP و script. من خلال البرمجة النصية ، أعني nginx.conf ، وليس NJS. علاوة على التدفقات الأولية ، HTTP ، والبرمجة النصية ، تم بناء وحدات HTTP بالفعل ، والتي سنتحدث عنها.

مثال كلاسيكي على upstreams و HTTP خوادم upstream - توجيهات داخل التكوين. مثال على الوحدات النمطية لـ HTTP هو add_header. مثال على البرمجة النصية هو ملف التكوين نفسه. يحتوي الملف على الوحدات النمطية التي يتكون منها nginx ؛ يتم تفسيرها بطريقة ما وتتيح لك القيام بشيء ما كمسؤول أو كمستخدم لديك.
لن نفكر في النواة وسنتحدث بإيجاز شديد عن التدفقات العليا ، لأنه كون منفصل داخل nginx. القصة عنهم تستحق العديد من المقالات.
تشريح وحدات HTTP
حتى لو لم تكتب كود C داخل nginx ، لكن استخدمته ، تذكر القاعدة الرئيسية.
في nginx ، يطيع كل شيء نمط سلسلة المسؤولية - COR.
لا أعرف كيفية ترجمة هذا إلى اللغة الروسية ، لكنني سوف أصف المنطق. يمر طلبك عبر مجموعة من وحدات السلسلة المكونة ، بدءًا من الموقع. كل من هذه الوحدات ترجع نتيجة. إذا كانت النتيجة سيئة ، يتم مقاطعة السلسلة.

عند تطوير الوحدات النمطية أو استخدام بعض التوجيهات في NJS و Lua ، لا تنسَ أن الكود قد يعطل تنفيذ هذه السلسلة.
الأقرب إلى
سلسلة المسؤولية هو سطر من كود Bash:
grep -RI pool nginx | awk -F":" '{print $1}' | sort -u | wc -l
في التعليمات البرمجية ، يكون كل شيء بسيطًا جدًا: إذا سقط AWK في منتصف السطر ، ففرز ولن يتم تنفيذ الأوامر التالية. تعمل وحدة nginx بشكل مشابه ، لكن الحقيقة في nginx ويمكنك الالتفاف حول ذلك - أعد تشغيل الكود. لكن يجب أن تكون مستعدًا للتعطل والتشغيل ، تمامًا مثل الوحدات النمطية التي تستخدمها في التكوين ، ولكن ليس حقيقة أن هذا هو الحال.
أنواع وحدات HTTP
HTTP و nginx عبارة عن مجموعة من المراحل المختلفة.
- معالجة المرحلة - معالجات المرحلة .
- مرشحات - مرشحات الجسم / الرؤوس . هذا التصفية هو الرؤوس أو نصوص الطلب.
- الوكلاء . وحدات البروكسي النموذجية هي proxy_pass ، fastcgi_pass ، memcached_pass.
- الوحدات النمطية لموازنة الحمل المحددة - موازنات التحميل . هذا هو أكثر أنواع الوحدات غير الملتوية ، حيث لا يتم تطويرها كثيرًا. مثال على ذلك هو وحدة Ketama CHash ، التي تتيح لك إجراء تجزئة متناسقة داخل nginx لتوزيع الطلبات على الواجهات الخلفية.
سأقول عن كل من هذه الأنواع والغرض منها.
معالجات المرحلة
تخيل أن لدينا عدة مراحل ، بدءًا من مرحلة الوصول. هناك عدة وحدات في كل مرحلة. على سبيل المثال ، تنقسم مرحلة ACCESS إلى اتصال ، وطلب إلى nginx ، والتحقق من ترخيص المستخدم. كل وحدة هي خلية في السلسلة. يمكن أن يكون هناك عدد لا حصر له من هذه الوحدات في المرحلة.

آخر معالج نهائي هو مرحلة المحتوى الذي يتم فيه تسليم المحتوى عند الطلب.
الطريقة دائمًا هي: طلب - سلسلة من المعالجات - إخراج المحتوى.
المراحل المتاحة لمطوري الوحدات النمطية من
مصادر NGINX :
typedef enum { NGX_HTTP_POST_READ_PHASE = 0, NGX_HTTP_SERVER_REWRITE_PHASE, NGX_HTTP_FIND_CONFIG_PHASE, NGX_HTTP_REWRITE_PHASE, NGX_HTTP_POST_REWRITE_PHASE, NGX_HTTP_PREACCESS_PHASE, NGX_HTTP_ACESS_PHASE, NGX_HTTP_POST_ACESS_PHASE, NGX_HTTP_PRECONTENT_PHASE, NGX_HTTP_CONTENT_PHASE, NGX_HTTP_LOG_PHASE, } ngx_http_phases;
يمكن الكتابة فوق المراحل ، أضف معالجك الخاص. ليست هناك حاجة إلى كل منهم في الحياة الحقيقية ، إذا لم تكن مطور لب nginx core. لذلك ، لن أتحدث عن كل مرحلة ، ولكن فقط عن المرحلة الرئيسية التي استخدمتها.
العنصر الرئيسي هو
ACCESS_PHASE. من المفيد بشكل خاص إضافة تفويضك إلى nginx - للتحقق من تنفيذ الطلب من حيث الوصول.
المراحل المهمة التالية التي أستغلها غالبًا هي مراحل السكون والمحتوى. يسمح
لك PRECONTENT_PHASE بجمع مقاييس حول المحتوى على وشك إرسالها كرد على العميل. يتيح
لك CONTENT_PHASE إنشاء محتوى فريد خاص بك استنادًا إلى شيء ما.
المرحلة الأخيرة التي
أستخدمها غالبًا هي مرحلة التسجيل
LOG_PHASE. بالمناسبة ، التوجيه ACCESS_LOG يعمل فيه. تتضمن مرحلة التسجيل أشد القيود التي تدفعني إلى الجنون: لا يمكنك استخدام طلب فرعي وعمومًا لا يمكنك استخدام أي طلب. لقد تركت المحتوى للمستخدم بالفعل ، ولن يتم تنفيذ أي معالجات ، أو معلمين ، وأي طلب فرعي.
ساوضح لماذا هو مزعج. دعنا نقول عندما تريد عبور nginx و Kafka في مرحلة التسجيل. في هذه المرحلة ، تم إكمال كل شيء بالفعل: هناك حجم محسوب للمحتوى والحالة وجميع البيانات ، لكن لا يمكنك القيام بذلك. انهم لا يعملون هناك. عليك أن تكتب على مآخذ عارية في مرحلة التسجيل لإرسال البيانات إلى كافكا.
مرشحات الجسم / الرؤوس
هناك نوعان من المرشحات: مرشحات الجسم ومرشحات الرؤوس.
مثال على
مرشح الجسم هو وحدة مرشح gzip. لماذا هي مرشحات الجسم اللازمة؟ تخيل أن لديك وكيلًا معينًا ، وتريد تحويل المحتوى بطريقة أو بأخرى أو تحليله. في هذه الحالة ، يجب عليك استخدام مرشح الجسم.
إنه يعمل كالتالي: تأتيك العديد من القطع ، وتفعل شيئًا ما بها ، وتلقي نظرة على المحتويات ، وتجميعها ، إلخ. لكن مرشح لديه أيضا قيود كبيرة. على سبيل المثال ، إذا قررت تغيير النص الأساسي - لإدراج نص الاستجابة أو قصه ، تذكر أنه سيتم استبدال سمات HTTP ، على سبيل المثال ، موجز ويب للمحتوى. يمكن أن يؤدي ذلك إلى تأثيرات غريبة إذا لم تقم بتطبيق القيود وتعكس بشكل صحيح في التعليمات البرمجية الخاصة بك.
مثال على
مرشح الرأس هو add_header الذي استخدمه الجميع. الخوارزمية تعمل كما هو الحال في مرشح الجسم. يتم إعداد استجابة للعميل ، ويسمح لك عامل التصفية add_header بالقيام بشيء ما هناك: إضافة رأس ، حذف رأس ، استبدال رأس ، إرسال طلب فرعي.
بالمناسبة ، في تصفية النص الأساسي وفي طلبات الترشيح الفرعية المتاحة في رأس الصفحة ، يمكنك إرسال التعريفات الداخلية إلى موقع إضافي هناك.
الوكيل
هذا هو أكثر أنواع الوحدات النمطية تعقيدًا وإثارة للجدل التي تسمح لك بإرسال طلبات وكيل إلى أنظمة خارجية ، على سبيل المثال ،
تحويل HTTP إلى بروتوكول آخر . أمثلة: proxy_pass ، redis_pass ، tnt_pass.
Proxy هي واجهة اقترحها مطورو nginx الأساسيون لجعل كتابة وحدات الوكيل أسهل. إذا تم ذلك بالطريقة الكلاسيكية ، فسيتم تنفيذ معالجات PHASES البديلة هذه ، المرشحات ، الموازنات. ومع ذلك ، إذا كان البروتوكول الذي تريد تحويل HTTP إليه مختلفًا تمامًا عن الكلاسيكيات ، فستبدأ المشاكل الكبيرة. واجهة برمجة تطبيقات الوكيل التي يقدمها nginx هي ببساطة غير مناسبة - يجب عليك اختراع وحدة الوكيل هذه من البداية.
مثال جيد على هذه الوحدة هو postgres_pass. لأنها تتيح nginx التواصل مع بوستجرس. لا تستخدم الوحدة النمطية الواجهة التي تم تطويرها في nginx على الإطلاق - لها مسارها الخاص.
تذكر الوكيل ، ولكن من الأفضل عدم الكتابة. لكتابة البروكسي ، يجب أن تتعلم كلمة nginx عن ظهر قلب - إنها طويلة وصعبة للغاية.
تحميل موازنات
مهمة موازنات التحميل بسيطة للغاية - للعمل في وضع جولة روبن. تخيل أن لديك قسمًا أوليًا ، بعض الخوادم فيه ، تحدد الأوزان وطرق الموازنة. هذا هو موازن التحميل النموذجي.
هذا الوضع غير مناسب دائمًا. لذلك ، تم تطوير وحدة Ketama CHash ، حيث يكون من الممكن مشروطًا الوصول إلى طلب تجزئة ثابت لبعض الخوادم. في بعض الأحيان تكون مريحة. يقدم Nginx Lua balancer_by_lua. على Lua ، يمكنك كتابة أي موازن بشكل عام.
وحدات C
القادم سيكون رأيي شخصي تماما على تطوير C- وحدات. لتبدأ - قواعدي الذاتية.
تبدأ الوحدة النمطية بتوجيهات nginx.conf. حتى لو كنت تقوم بتصنيع وحدة C لن يتم تشغيلها إلا من قِبل شركتك ، فكر دائمًا في التوجيهات. ابدأ في تصميم الوحدة معهم ، لأن هذا هو ما سيتواصل معه مسؤول النظام. هذا مهم - قم بتنسيق جميع الفروق الدقيقة معه أو مع الشخص الذي سيقوم بتشغيل وحدة C الخاصة بك. إن NGINX منتج معروف ، وتطيع توجيهاته قوانين معينة يعرفها مسؤولو النظام. لذلك ، فكر دائمًا في الأمر.
استخدام نمط رمز nginx. تخيل أن الوحدة الخاصة بك سيتم دعمها من قبل شخص آخر. إذا كان بالفعل على دراية بـ nginx ونمط الكود الخاص به ، فسيكون من الأسهل عليه قراءة وفهم الكود.
في الآونة الأخيرة ، طلب مني صديق جيد من ألمانيا مساعدته في التعامل مع خلل داخل كود nginx الخاص به. لا أعرف عن نمط الشفرة الذي كتبه ، لكنني لم أستطع حتى قراءة الكود بشكل طبيعي.
استخدام تجمع الذاكرة الصحيح. احرص دائمًا على وضع ذلك في الاعتبار ، حتى لو كان لديك الكثير من الخبرة مع nginx. خطأ نموذجي لمطور وحدة C مبتدئ لـ nginx هو الحصول على مجموعة خاطئة.
خلفية صغيرة: يستخدم nginx عمومًا أيديولوجية المُخصصين الضعفاء. يمكنك استخدام malloc هناك ، ولكن غير مستحسن. لديه ألواح خاصة به ، مخصص الذاكرة الخاصة به ، تحتاج إلى استخدامه. وفقًا لذلك ، يحتوي كل كائن على رابط إلى التجمع الخاص به ، ويجب استخدام هذا التجمع. يتمثل الخطأ المبتدئ النموذجي في استخدام اتصال تجمع في عامل تصفية الرأس ، وليس طلب تجمع. هذا يعني أنه إذا كان لدينا اتصال مستمر ، فسوف ينتفخ المسبح حتى نفاد الذاكرة أو تحدث آثار جانبية أخرى. لذلك ، من المهم.
علاوة على ذلك ، من الصعب للغاية ظهور مثل هذه الأخطاء. Valgrind ("سيفهم syshniks") لا يعمل مع تخصيص لوح - سيظهر صورة غريبة.
لا تستخدم حظر الإدخال / الإخراج خطأ نموذجي لأولئك الذين يرغبون في تطبيق شيء خارجي أسرع هو استخدام حظر الإدخال / الإخراج وحظر المقابس. لا يمكنك القيام بذلك أبدًا في nginx - فهناك العديد من العمليات ، لكن كل عملية تستخدم مؤشر ترابط واحد.
يمكنك عمل خيوط متعددة ، ولكن كقاعدة عامة ، هذا يزيد الأمر سوءًا. إذا كنت تستخدم حظر الإدخال / الإخراج في مثل هذه البنية ، فسيكون الجميع في انتظار قطعة الحظر هذه.
سوف فك ما قلته أعلاه.
تبدأ الوحدة النمطية بتوجيهات nginx.conf
حدد الصفيف الذي يجب أن تعيشه التوجيهات: رئيسي ، خادم ، HTTP ، موقع ، موقع إذا.
حاول تجنب الموقع إذا - كقاعدة عامة ، يؤدي ذلك إلى استخدام غريب للغاية لتكوين nginx.
جميع التوجيهات في nginx تعيش في سياقات مختلفة وفي نطاقات مختلفة. يمكن أن يعمل توجيه add_header على مستوى HTTP وعلى مستوى الموقع وعلى مستوى الموقع إذا. وعادة ما يوصف هذا في الوثائق.
فهم على أي المستويات يمكن أن يعمل التوجيه الخاص بك ، حيث يتم تنفيذ التوجيه: PHASE Handler ، مرشح الجسم / الرأس.
هذا مهم لأنه في nginx يتم تجميد التكوين. حسب الاصطلاح ، عندما تكتب add_header في مكان ما أعلاه ، يتم تنعيم هذه القيمة في add_header السفلية ، والتي لديك بالفعل في الموقع. وفقا لذلك ، سوف تضيف رأسين. وهذا ينطبق على أي توجيه.
إذا قمت بتحديد منفذ مضيف ما ، فقم بالعكس - مجموعة مآخذ التوصيل. يجب الإشارة إلى هذا مرة واحدة.
بشكل عام ، أود منع أي دمج - أنت فقط لا تحتاج إليها. لذلك ، يجب أن تحدد دائمًا بوضوح صفيفات nginx من التكوين الذي تعيش فيه التوجيهات أو مجموعة التوجيهات.
مثال جيد:
location /my_location/ { add_header “My-Header” “my value”; }
هنا يتم إضافة add_header ببساطة إلى الموقع. يمكن أن يكون add_header نفسه في مكان ما أعلاه ، وسيتم تلويث كل شيء بكل بساطة. هذا هو سلوك موثق ومفهوم.
فكر فيما قد يعوق تنفيذ التوجيه.
تخيل أنك تقوم بتطوير مرشح الجسم. كما قلت أعلاه ، يضع nginx الوحدة النمطية الخاصة بك في سلسلة مشتركة ، وليس لديك أي ضمان بأن وحدة gzip لم تدخل السلسلة أمام مرشح جسمك في وقت الترجمة. في هذه الحالة ، إذا قام شخص ما بتشغيل وحدة gzip ، فسيتم إرسال البيانات إلى وحدتك من أجل gzip. هذا يهدد بأنه لا يمكنك فعل أي شيء بالمحتوى. يمكنك إعادة gzip ، على سبيل المثال ، ولكن هذا تهكم من وجهة نظر وحدة المعالجة المركزية.
تنطبق نفس القواعد على جميع معالجات المرحلة - لا يوجد أي ضمان من سيتم استدعاؤه من قبل ومن بعده. لذلك ، احترم الشخص الذي سيتم استدعاؤه ، وتذكر أن بعض gzip أو أي شيء آخر قد يطير إليك بشكل غير متوقع.
نمط رمز Nginx
عند إنشاء المنتج ، تذكر أن شخصًا ما سوف يدعمه. لا تنسى عن رمز نمط nginx.
قبل كتابة الوحدة النمطية nginx ، تعرف على المصدر:
الأول والثاني .
إذا كنت تتناول تطوير وحدات nginx في المستقبل ، فستكون مدركًا جيدًا لمصادر nginx. سوف تحبهم لأنه لا
يوجد وثائق . سوف تتعلم بنية دليل nginx جيدًا ، وتعلم استخدام Grep ، وربما Sed ، عندما تحتاج إلى نقل بعض القطع من nginx إلى الوحدات النمطية الخاصة بك.
تجمع الذاكرة
يجب استخدام التجمعات بشكل صحيح.
على سبيل المثال ، "r-> connection-> pool! = R-> pool". لا يمكنك بأي حال من الأحوال استخدام تكوين تجمع الذاكرة عند معالجة الطلبات ، وسوف تنتفخ حتى تتم إعادة تشغيل nginx.
فهم عمر الكائن. لنفترض أن إعادة تشغيل الطلب لها بالضبط عمر خط الأنابيب هذا. في هذا المجمع ، يمكنك وضع الكثير من الأشياء وإفساح المجال. يمكن أن يعيش الاتصال نظريًا إلى أجل غير مسمى - من الأفضل وضع شيء مهم حقًا فيه.
حاول ألا تستخدم أدوات تخصيص خارجية ، على سبيل المثال ، malloc / free . هذا له تأثير سيء على تجزئة الذاكرة. إذا كنت تعمل مع كميات كبيرة من البيانات وتستخدم الكثير من malloc ، فإن nginx يبطئ بشكل جيد.
لمحبي Valgrind هناك اختراق يسمح لك بتصحيح تجمعات nginx باستخدام Valgrind. هذا مهم إذا كان لديك الكثير من كود C على nginx ، لأنه حتى مطور متمرس في العمل مع الذاكرة يمكن أن يرتكب خطأ.
حظر الإدخال / الإخراج
كل شيء بسيط هنا - لا تستخدم حظر الإدخال / الإخراج.
وإلا ، فستكون هناك على الأقل مشكلات في الاتصالات المباشرة ، ولكن كحد أقصى ، سيعمل كل شيء لفترة طويلة جدًا.
أعرف الحالة عندما استخدم شخص Quora داخل nginx في وضع الحظر (لا تسأل عن السبب). أدى هذا إلى حقيقة أن الاتصالات المحافظة على الحياة تخلت عن نشاطها وانقضت مهلتها طوال الوقت. من الأفضل عدم القيام بذلك - كل شيء سوف يعمل لفترة طويلة ، غير كفء وسيتعين عليك على الفور تحريف مليون مهلة ، لأن nginx سيبدأ مهلة في العديد من الأشياء.
ولكن هناك بديل لوحدات C - NJS ولوا.
عندما لا تحتاج إلى تطوير C- وحدات
هذه السنة كانت لدي أول تجربة في العمل على NJS ، ولدي انطباع شخصي عن ذلك ، وأدركت حتى ما كان مفقودًا ، بحيث كان كل شيء على ما يرام. أود أيضًا أن أتحدث عن تجربتي في العمل مع Lua ضمن nginx ، علاوة على ذلك ، مشاركة المشاكل الموجودة في Lua.
أساسيات Lua / LuaJit
Nginx لا يستخدم Lua ، لكن LuaJit. ولكن هذا ليس لوا ، لأن لوا قد طورت بالفعل نسختين ، و LuaJit عالق في مكان ما في الماضي.
المؤلف عمليا لا يطور LuaJit - يعيش في الغالب في الشوك. الشوكة الأكثر حداثة هي
LuaJit2 . هذا يضيف مواقف غريبة في نفس OpenResty.
جامع القمامة يحتاج إلى عناية . لا تستطيع LuaJit التغلب على هذه المشكلة - ما عليك سوى الخروج ببعض الحلول. مع وجود حمولة ضخمة ، عندما يكون هناك الكثير من عمليات جمع البيانات المهملة التي يتم الاحتفاظ بها مرئية على العميل مع وجود أخطاء في المخطط والأخطاء 500. هناك العديد من الطرق للتعامل مع أداة تجميع البيانات المهملة في Lua ، لن أركز عليها هنا. هناك الكثير من المعلومات حول هذا الأمر على الإنترنت.
تنفيذ سلسلة يؤدي إلى مشاكل الأداء . هذا هو مجرد شر LuaJit ، وفي لوا تم إصلاحه. تنفيذ السلاسل في LuaJit يتحدى ببساطة أي منطق. تبطئ الخطوط في أعنف الطرق ، والتي ترتبط بالتنفيذ الداخلي.
عدم القدرة على استخدام العديد من المكتبات الجاهزة . يتم حظر Lua في البداية ، لذا تستخدم معظم المكتبات في Lua و LuaJit حظر I / O. بسبب عدم حظر nginx ، من المستحيل استخدام مكتبات جاهزة داخل nginx تستخدم أي حظر إدخال / إخراج. هذا سوف تبطئ nginx.
تتشابه أسباب استخدام LuaJit مع أسباب استخدام الوحدات النمطية:
- نماذج من وحدات معقدة.
- حسابات HMAC ، SHA للتراخيص ؛
- موازين
- التطبيقات الصغيرة: معالجات الرأس وقواعد عمليات إعادة التوجيه ؛
- متغيرات الحوسبة لـ nginx.conf.
أين الأفضل عدم استخدام LuaJit؟
القاعدة الرئيسية: لا تقم بمعالجة هيئة ضخمة على لوا - هذا لا يعمل.
لا تعمل معالجات المحتوى على Lua أيضًا . محاولة لتقليل المنطق إلى عدد قليل من
if
. سوف يعمل موازن بسيط ، ولكن الشريط الجانبي في Lua سيعمل بشكل سيء للغاية.
سوف الذاكرة المشتركة أو جامع القمامة تأتي. لا تستخدم الذاكرة المشتركة مع Lua - Garbage Collector بسرعة وبضمان إخراج المخ كاملة إلى الإنتاج.
لا تستخدم coroutines مع الكثير من المركبات المحافظة على الحياة. يولد Coroutines المزيد من القمامة داخل LuaJit Garbage Collector ، وهو أمر سيء.
إذا كنت تستخدم LuaJit بالفعل ، فتذكر:
- حول مراقبة الذاكرة.
- على رصد وتحسين عمل جامع القمامة ؛
- حول كيفية عمل Garbage Collector ، إذا كنت قد كتبت تطبيقًا معقدًا لـ LuaJit ، لأنه يتعين عليك إضافة شيء جديد.
NJS
عندما كنت في NGINX Conf ، أقنعوني أنه سيكون من الرائع عدم كتابة التعليمات البرمجية في C. اعتقدت أنه كان علي المحاولة ، وهذا ما حصلت عليه.
إذن. إنه يعمل ، الكود بسيط ، لا يؤثر على السرعة - كل شيء رائع.
نموذجي الصغير الذي بدأت به هو 10 سطور من الكود. لكن هذه الأسطر العشرة تفعل التخويل مع s3.
حساب المتغيرات ل nginx.conf. يمكن حساب العديد من المتغيرات باستخدام NJS. داخل nginx ، هذا رائع. يوجد مثل هذه الميزة في Lua ، ولكن يوجد أداة تجميع البيانات المهملة ، لذا فهي ليست رائعة.
ومع ذلك ، ليس كل شيء جيد جدا. لفعل أشياء رائعة حقًا على NJS ، فقد فاته بعض الأشياء.
الذاكرة المشتركة . لقد قمت بترقيع الذاكرة المشتركة ، هذا شوكة خاصة بي ، لذلك يكفي الآن.
مرشحات دعم المزيد من المراحل . في NJS ، لا يوجد سوى مرحلة المحتوى والمتغيرات ، ومرشح الرأس مفقود للغاية. عليك أن تكتب عكازات لإضافة الكثير من الرؤوس. لا يوجد مرشح نص كافٍ للمنطق المعقد أو العمل مع المحتوى.
معلومات حول كيفية مراقبتها وملفها الشخصي . أنا أعرف الآن كيف ، ولكن كان علي أن أدرس المصدر. لا توجد معلومات أو أدوات كافية حول التشكيل الجانبي الصحيح. إذا كان كذلك ، فإنه مخفي حيث لا يمكن العثور عليها. في نفس الوقت ، لا توجد معلومات كافية حول
أين يمكنني استخدام NJS وأين لا يمكنني ذلك؟
وحدات C. كان لدي الرغبة في توسيع NJS.
خاتمة
لماذا إنشاء الوحدات الخاصة بك؟ لحل المشاكل العامة والتجارية.
متى أحتاج إلى تنفيذ الوحدات النمطية في C؟ إذا لم تكن هناك خيارات أخرى. على سبيل المثال ، عبء ثقيل أو إدخال محتوى أو توفير أساسي على الأجهزة. ثم يجب أن يتم ذلك مضمون في C. في معظم الحالات ، تكون Lua أو NJS مناسبة. ولكن يجب أن تفكر دائما في المستقبل.
وعلى لوا؟ عندما لا يمكنك الكتابة في C. على سبيل المثال ، لا تحتاج إلى تحويل نص الطلب باستخدام RPS ضخمة. ينمو عدد العملاء لديك ، في مرحلة ما ستتوقف عن التعامل - فكر في الأمر.
NJS؟ عندما سئمت LuaJit تمامًا من جامع القمامة والأوتار. على سبيل المثال ، أنشأ التخويل العديد من كائنات Garbage على Lua ، لكن هذا لم يكن حرجًا. ومع ذلك ، فقد انعكس هذا في الرصد ومزعج. الآن توقف عن الظهور في المراقبة الخاصة بي ، وأصبح كل شيء جيدًا.
في HighLoad ++ 2019 ، سوف يواصل Vasily Soshnikov موضوع وحدات nginx ويتحدث أكثر عن NJS ، دون نسيان المقارنة مع LuaJit و C.
اطلع على القائمة الكاملة للتقارير على الموقع ، وأراك يومي 7 و 8 نوفمبر في أكبر مؤتمر لمطوري الأنظمة المحملة للغاية. تابع أفكارنا الجديدة في النشرة الإخبارية وقناة البرق .