ضبط مهلات اتصال Nginx للتعامل مع ارتفاعات الزيارات دون فقدان الطلبات

مجموعة من توجيهات المهلة الزمنية في nginx تحدد ما إذا كان السيرفر سيتعامل بسلاسة مع ارتفاع الزيارات أو سيبدأ بفقدان طلبات حقيقية. إليك كيفية ضبطها بشكل صحيح.

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

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

لماذا المهلات الزمنية أهم مما تتصور

يضطر nginx لاتخاذ قرارات باستمرار: كم من الوقت ينتظر العميل لإرسال طلب كامل، وكم من الوقت يبقي الاتصال مفتوحاً بين الطلبات، وكم من الوقت ينتظر الخادم الخلفي (PHP-FPM، أو تطبيق Node، أو أي شيء آخر) للرد. كل واحد من هذه القرارات تحكمه قيمة مهلة زمنية، ومعظم القيم الافتراضية اختيرت لحالة استخدام عامة، وليس لنمط الزيارات الخاص بك.

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

التوجيهات الأساسية للمهلات الزمنية

إليك أهمها خلال ارتفاعات الزيارات:

  • client_header_timeout - المدة التي ينتظرها nginx حتى يرسل العميل رأس الطلب كاملاً
  • client_body_timeout - المدة التي ينتظرها لجسم الطلب (مهمة للزيارات الكثيفة بطلبات POST مثل صفحات الدفع أو إرسال النماذج)
  • keepalive_timeout - المدة التي يبقى فيها الاتصال الخامل مفتوحاً بانتظار طلب آخر
  • proxy_connect_timeout - المدة التي ينتظرها nginx لإقامة اتصال مع تطبيقك الخلفي
  • proxy_read_timeout - المدة التي ينتظرها حتى يرسل الخادم الخلفي البيانات
  • send_timeout - المدة التي ينتظرها nginx بين عمليات الكتابة الناجحة للعميل

إعداد بداية معقول لمعظم تطبيقات الويب تحت زيارات متوسطة إلى عالية يبدو هكذا:

http { client_header_timeout 15s; client_body_timeout 15s; keepalive_timeout 30s; send_timeout 15s; location / { proxy_connect_timeout 5s; proxy_read_timeout 30s; proxy_send_timeout 30s; } }

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

Keepalive: السلاح ذو الحدين

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

بالنسبة لمعظم المواقع، النقطة المثالية تتراوح بين 15 و30 ثانية. مسارات الدفع في المتاجر الإلكترونية التي تحتوي على استدعاءات AJAX متتالية كثيرة قد تستفيد من نافذة أطول قليلاً. أما المدونة البسيطة أو موقع تسويقي فنادراً ما يحتاج أكثر من 15 ثانية.

تحقق أيضاً من إعدادات keepalive الخاصة بالخادم الخلفي بشكل منفصل. إذا كان nginx يوجّه الطلبات إلى PHP-FPM أو تطبيق Node، فأنت تريد اتصالات دائمة مع الخادم الخلفي أيضاً:

upstream backend { server 127.0.0.1:8000; keepalive 32; } server { location / { proxy_pass http://backend; proxy_http_version 1.1; proxy_set_header Connection ""; } }

هذا وحده يمكن أن يقلل بشكل ملموس من زمن TTFB خلال فترات التزامن العالي لأن nginx لن يعيد فتح اتصالات مع خادم التطبيق باستمرار. لقد تناولنا موضوع إعادة استخدام الاتصالات بشكل أوسع في كيف تقلل اتصالات Keep-Alive من العبء الذي يبطئ كل طلب، إذا أردت التعمق في التفاصيل التقنية.

اتصالات Workers وقائمة الانتظار (Backlog)

المهلات الزمنية وحدها لن تنقذك إذا لم يستطع nginx قبول اتصالات جديدة بسرعة كافية من الأساس. هناك إعدادان يعملان جنباً إلى جنب مع ضبط المهلات:

  • worker_connections - الحد الأقصى للاتصالات المتزامنة لكل عملية worker
  • listen ... backlog= - عدد الاتصالات المعلّقة التي سيضعها نظام التشغيل في قائمة الانتظار قبل رفض اتصالات جديدة
events { worker_connections 4096; multi_accept on; } server { listen 443 ssl backlog=2048; }

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

تحديد المعدل دون معاقبة المستخدمين الحقيقيين

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

limit_req_zone $binary_remote_addr zone=general:10m rate=20r/s; location / { limit_req zone=general burst=40 nodelay; }

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

مراقبة المقاييس الصحيحة

لا يمكنك الضبط بشكل أعمى. قبل تغيير أي شيء في بيئة الإنتاج، احصل على خط أساس من سجلات nginx:

  • زمن استجابة الخادم الخلفي ($upstream_response_time في تنسيق السجل الخاص بك)
  • زمن الطلب ($request_time)
  • عدد أخطاء 502/504 خلال نافذة زمنية متحركة
  • الاتصالات النشطة من stub_status

أضف هذا إلى إعدادات nginx للحصول على إحصائيات اتصال مباشرة:

location /nginx_status { stub_status; allow 127.0.0.1; deny all; }

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

قائمة واقعية لضبط الإعدادات

  • اضبط client_body_timeout وclient_header_timeout على 10-15 ثانية، وليس القيمة الافتراضية 60 ثانية
  • حافظ على proxy_connect_timeout قصيرة (3-5 ثوانٍ) لأن بطء الاتصال عادة ما يشير إلى مشكلة حقيقية
  • امنح proxy_read_timeout متسعاً كافياً لأبطأ نقطة نهاية (endpoint) مشروعة لديك، وليس لأسرعها
  • فعّل keepalive للخادم الخلفي حتى يُعاد استخدام اتصالاتك معه تحت الحمل
  • حدد حجم قائمة backlog بما يتناسب مع موجات الاتصالات المتزامنة المتوقعة
  • استخدم تحديد المعدل بقيمة burst سخية، وليس حائطاً صلباً

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

الخلاصة

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