كلاود
development

ضبط PHP-FPM للأداء: pm.max_children والذاكرة بالأرقام

كلاود|١٦ سبتمبر ٢٠٢٦|6 دقائق قراءة|١٤٦ مشاهدة
ضبط PHP-FPM للأداء: pm.max_children والذاكرة بالأرقام

أكثر أعطال 502 و504 على سيرفرات PHP سببها قيمة pm.max_children خاطئة، إما عالية تُنهك الذاكرة أو منخفضة تُكدّس الطلبات. المعادلة بسيطة: اقسم الذاكرة المتاحة على متوسط حجم العملية، واختر النمط المناسب لحركتك.

لو يطلع لك خطأ 502 Bad Gateway أو 504 Gateway Timeout على تطبيق PHP تحت الضغط، فالسبب الأكثر شيوعاً هو قيمة pm.max_children غير مضبوطة على ذاكرة سيرفرك الفعلية. المعادلة العملية واحدة: pm.max_children = (الذاكرة المتاحة لـPHP) ÷ (متوسط حجم عملية PHP الواحدة). الذاكرة المتاحة هي إجمالي الرام ناقص ما تحجزه لنظام التشغيل وقاعدة البيانات وخادم الويب. متوسط عملية Laravel أو WordPress حديث يتراوح من 40 إلى 100 ميغابايت، فلو عندك باقة نمو (4GB رام) وحجزت 1.5GB لبقية الخدمات، يتبقى 2.5GB أي 2560 ميغابايت، وبقسمتها على 60 ميغابايت للعملية تحصل على نحو 42 عاملاً كحد أقصى آمن.

القاعدة الثانية: اختر نمط مدير العمليات حسب حركتك. static لحركة ثابتة عالية، وdynamic للحالة العامة، وondemand للمواقع قليلة الزيارات أو عندما تستضيف عدة مواقع على سيرفر واحد. خطأ 502 يعني غالباً أن FPM سقط أو بلغ سقف العمّال، وخطأ 504 يعني أن السكربت تجاوز مهلة التنفيذ. الحل ليس رفع الأرقام عشوائياً، بل حسابها من الذاكرة ومراقبة سجلّ FPM ثم تعديلها على بيانات حقيقية. في الأسطر التالية نفصّل الحساب والأنماط وأدوات المراقبة والأخطاء الشائعة بأرقام على باقات كلاود.

لماذا pm.max_children هو الرقم الأهم

كل طلب PHP يخدمه عامل (worker) منفصل يستهلك ذاكرة. pm.max_children هو أقصى عدد عمّال يشتغلون في وقت واحد، فهو سقف التزامن وسقف استهلاك الذاكرة معاً. لو ضبطته عالياً جداً، عند الذروة يفتح FPM عمّالاً أكثر مما تحمله الرام، فيتدخّل قاتل الذاكرة (OOM Killer) ويقتل عمليات عشوائية، والنتيجة 502، ولو ضبطته منخفضاً جداً تصل الطلبات فيصطفّ ما زاد عن العدد في طابور، فيطول زمن الانتظار حتى يسقط الطلب بـ502 أو 504 مع رسالة في السجل: server reached pm.max_children.

الخطوة الأولى قبل أي حساب: اعرف حجم عملياتك الحقيقي على سيرفرك أنت، لا رقماً من مقال. نفّذ هذا الأمر أثناء حركة فعلية ليعطيك متوسط ذاكرة عامل FPM بالميغابايت:

ps --no-headers -o rss -C php-fpm | awk '{s+=$1} END {print s/NR/1024" MB"}'

لو طلع لك 55 ميغابايت مثلاً، اعتمده مع هامش أمان بسيط (60 ميغابايت). الفرق بين تطبيق نظيف وتطبيق محمّل بإضافات كبير: WordPress بإضافات ثقيلة قد يبلغ 120 ميغابايت للعملية، بينما API خفيف بـLaravel قد يكفيه 40 ميغابايت. لهذا لا يوجد رقم سحري واحد يصلح لكل السيرفرات، بل معادلة تطبّقها على حالتك أنت.

المعادلة بالأرقام على باقات كلاود

نطبّق المعادلة على باقات الخادم السحابي. نحجز ذاكرة لنظام التشغيل وقاعدة البيانات وخادم الويب، ثم نقسم الباقي على متوسط 60 ميغابايت للعملية:

الباقةالراممحجوز للنظام+DBمتاح لـPHPpm.max_children الآمن
أساس2 GB1 GB1024 MB~15
نمو4 GB1.5 GB2560 MB~42
أعمال8 GB2.5 GB5632 MB~90
مؤسسة16 GB4 GB12288 MB~200

هذه أسقف نظرية، ابدأ بأقل منها بنحو 20% ثم راقب. لو كانت قاعدة بياناتك ثقيلة (MySQL يستهلك رام كثيرة مع innodb_buffer_pool كبير)، ارفع المحجوز واخفض max_children. القاعدة الحاكمة: الأفضل طابور قصير على عمّال محسوبين من انهيار كامل بسبب نفاد الذاكرة. لو احتجت تزامناً أعلى من سقف باقتك، ترقية الرام أرخص وأأمن من المخاطرة: إضافة 2GB رام بـ600 ريال سنوياً ترفع سقف العمّال بنحو 33 عاملاً على متوسط 60 ميغابايت، وهذا فرق يحوّل سيرفراً يختنق عند الذروة إلى سيرفر يمرّرها بثبات.

static و dynamic و ondemand: أيّها تختار

الأنماط الثلاثة تتحكم في كيفية إنشاء العمّال وإبقائهم أحياء:

النمطالسلوكالأنسب لـالذاكرة
staticعدد ثابت من العمّال أحياء دائماًحركة عالية ثابتة، صفحة واحدة مزدحمةالأعلى استهلاكاً
dynamicيتوسّع بين start_servers وmax_spare حتى max_childrenالحالة العامة، حركة متغيّرةمتوسط
ondemandلا عمّال حتى يصل طلب، ثم يُقتلون بعد خمولمواقع قليلة الزيارات، عدة مواقع على سيرفرالأقل استهلاكاً

مع static تضبط pm.max_children فقط، وكل العمّال يشتغلون من لحظة الإقلاع، فلا يوجد تأخير بدء بارد لكن الذاكرة محجوزة دائماً. مع dynamic تضبط pm.start_servers وpm.min_spare_servers وpm.max_spare_servers إضافة إلى max_children، فيوازن بين الجاهزية والذاكرة. مع ondemand يبدأ FPM بصفر عمّال ويفتح عاملاً عند كل طلب جديد ثم يقتله بعد pm.process_idle_timeout، وهو مثالي لو تستضيف عدة تطبيقات على خادم واحد ولا تريد كل تطبيق يحجز رام وهو خامل. عيبه الوحيد تأخير البدء البارد لأول طلب بعد الخمول، وهو مقبول في المواقع قليلة الحركة.

ضبط dynamic بشكل متوازن

لباقة نمو (max_children = 42) قيم بداية معقولة: pm.start_servers = 10، pm.min_spare_servers = 8، pm.max_spare_servers = 20. القاعدة: start_servers بين min وmax، وmax_spare لا يتجاوز نصف max_children تقريباً حتى لا يبقى عدد كبير خاملاً يأكل الذاكرة. هذه قيم بداية تصقلها بالمراقبة، فلو رأيت العمّال الاحتياط يُستهلكون دائماً، ارفع start_servers قليلاً ليكون السيرفر جاهزاً للذروة.

مراقبة FPM: صفحة الحالة وسجل البطء

لا تضبط بالتخمين، بل بالقياس المستمر. فعّل pm.status_path في إعداد المجمع (pool) لتحصل على صفحة حالة حيّة تعرض العمّال النشطين والخاملين وطول الطابور وأعلى عدد عمّال وصلت إليه (max active processes) وكم مرة بلغت السقف (max children reached). لو رأيت listen queue يكبر باستمرار، فطلباتك تصطفّ وتحتاج رفع max_children أو ترقية الرام. فعّل أيضاً slowlog مع request_slowlog_timeout (مثلاً 5 ثوانٍ) ليسجّل FPM أثر أي طلب يتجاوز المهلة، فتعرف بالضبط أي دالة تبطئ تطبيقك بدل التخمين. مراقبة أسبوع واحد تكشف نمط حركتك الحقيقي: ذروتك، متوسط زمن الطلب، وهل مشكلتك في العمّال أم في بطء الكود أم في قاعدة البيانات.

opcache يضاعف الأداء بلا تكلفة

قبل رفع أي موارد، تأكد أن opcache مفعّل، فهو يخزّن الكود المترجَم في الذاكرة ويوفّر إعادة ترجمة كل ملف مع كل طلب. اضبط opcache.memory_consumption على 128 ميغابايت على الأقل لتطبيق متوسط، وopcache.max_accelerated_files على رقم أعلى من عدد ملفات مشروعك، فمشروع Laravel قد يتجاوز 10,000 ملف. opcache صحيح الضبط يرفع الإنتاجية بنسبة ملموسة على نفس العتاد، وهو أرخص تحسين ممكن لأنه بلا موارد إضافية.

أخطاء شائعة تسبب 502 و504

  • max_children أعلى من طاقة الرام: عند الذروة ينهار السيرفر بـOOM ويعطي 502. راقب dmesg بحثاً عن رسائل قتل الذاكرة.
  • max_children منخفض جداً: يظهر في سجل FPM سطر server reached pm.max_children، والطلبات تصطفّ وتسقط. الحل: احسبه من الذاكرة لا من الخوف.
  • تعارض المهل: لو كان request_terminate_timeout في FPM أقصر من fastcgi_read_timeout في Nginx، تحصل على 504 من سكربتات بطيئة. اجعل مهلة Nginx أطول قليلاً من مهلة FPM.
  • تسرّب ذاكرة تراكمي: بعض التطبيقات تكبر عملياتها مع الوقت. اضبط pm.max_requests = 500 ليعيد العامل تدوير نفسه بعد عدد طلبات، فيحرّر الذاكرة المتسرّبة.
  • socket مقابل TCP: استخدم Unix socket بين Nginx وFPM على نفس السيرفر، أسرع وأقل استهلاكاً من TCP على 127.0.0.1.
  • مجمّع واحد لكل المواقع: اعزل كل موقع في pool منفصل بمستخدم منفصل، فلا يسقط موقع بقية المواقع عند بلوغه سقفه.

خطوات عملية للضبط الصحيح

لتضبط PHP-FPM على سيرفرك بثقة، اتبع هذا الترتيب:

  • قِس متوسط حجم عملية FPM الحقيقي بالأمر أعلاه أثناء حركة فعلية.
  • احسب max_children = (الرام ناقص المحجوز) ÷ متوسط العملية، ثم اخصم 20% كهامش.
  • اختر النمط: static لموقع مزدحم واحد، dynamic للعام، ondemand لعدة مواقع.
  • اضبط pm.max_requests بين 300 و500 لمقاومة تسرّب الذاكرة، وفعّل opcache بذاكرة كافية.
  • واءم مهلة Nginx مع request_terminate_timeout في FPM.
  • فعّل صفحة الحالة وسجل البطء، وراقب أسبوعاً.
  • لو تكرر server reached pm.max_children دون نفاد رام، ارفع العدد قليلاً أو رقِّ الرام.

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

مشاركة