آشنایی با روش کار PHP-FPM و مهمترین تنظیمات آن

آشنایی با روش کار PHP-FPM و مهمترین تنظیمات آن

در این مقاله می خواهیم به معرفی کانفیگ های مهم و تاثیر گذار FPM بپردازیم و بگویم برای اینکه وب سرور شما به خوبی بتواند جواب درخواست های مختلفی که به سمتش می آید را بدهد، باید چه تنظیماتی داشته باشد. این تنظیمات بسته به نوع رفتار برنامه ی شما می تواند متفاوت باشد. مثلا برنامه ای دارای حجم بالای درخواست است یا برنامه ای دیگر تعداد درخواست های منظم و مشخصی را دریافت می کند و تنظیمات باید براساس آن بهینه شود.

اگر با خود FPM و این که چه شد که FPM بوجود آمد،می خواهید بیشتر آشنا شوید، به شما سه مقاله ی مهم و خواندنی از سکان آکادمی را معرفی می کنم که به معرفی کامل FPM و نصب آن می پردازند:

1-پردازش موازی در آپاچی

2-چگونه به PHP-FPM رسیدیم؟

3-راه اندازی PHP-FPM روی وب سرور آپاچی

ولی اگر بخواهم به صورت خیلی مختصر معرفی کنم، FPM یک مدیر پردازش های fastcgi (factCgi Process manager) هست. حالا fastcgi  چیست؟ 

در نظر بگیرید وب سرور ما آپاچی است و می خواهد به ازای هر درخواستی که به سمتش می آید یک پردازش را ایجاد کند، تا بتواند به آن درخواست پاسخ دهد.

اسکریپت‌های PHP هم قبل از اینکه سخت‌افزار پردازشگر اصلی وب سرور بتواند آن را درک کند، نیاز به کامپایل دارند. در حالت های قدیمی و در واقع در حالت پیش فرض وب سرور ها -مثل افزونه های mod_php که برای آپاچی در نظر می گرفتند- اسکریپت PHP کاملا در هم تنیده با وب سرور بود و در واقع مسئول کامپایل کردن اسکریپت های PHP خود وب سرور بود. هرچند روشی بالغ و پایداری به شمار می رفت ولی ضعف های جدی داشت.

 همین که به ازای هر درخواست یک پردازش ایجاد شود و بعد از بین برود و برای درخواست بعدی یک پردازش جدید ایجاد شود، سرباری بر روی سیستم هست. در عین حال جلوی پردازش موازی نیز گرفته می شود.

 با کمک factcgi می توان یک پردازش پایدار واحد ایجاد کرد و از طریق آن، تعداد زیادی درخواست را پاسخ داد. همچنین پردازش های موازی را نیز می توان در یک یا چند اتصال تا حد خوبی پشتیبانی کرد به طوری که پردازش ها (یا بهتر بگوییم پردازش گرها) در fastcgi مقیاس پذیر باشند.

اتفاقی که در وب سروری که دارای factcgi هست می افتد، این است که درخواستی که به وب سرور، برای مثال Apache بر روی اتصال TCP می آید با یک اتصال UNIX Socket به factCgi ارسال می شود و بعد از پردازش و آماده سازی پاسخ (مثلا پردازش و آماده سازی پاسخ توسط اسکریپت های PHP) در همان اتصال به وب سرور برگردانده می شود و آن اتصال بسته می شود. ولی پردازش ها همچنان باقی می مانند.

خیلی از مدیرهای وب سایت ها و برنامه نویسان به این قضیه پی بردند که جداسازی برنامه های کاربردی وب از وب سرور در FastCGI نسبت به مفسرهای دیگر (mod_perl، mod_php و غیره) مزایای خیلی خوبی دارد. مثلا می توان سرور و برنامه های کاربردی را به صورت جداگانه ری استارت کرد (به خصوص در وب سایت های به شدت شلوغ) و یا اینکه برای هر لایه، جداگانه سیاست های امنیتی در نظر گرفت. 

PHP-FPM چه کار می کند ؟

قبل از اینکه وارد بحث کانفیگ ها شویم باید اول بدانیم php-fpm چگونه کار می کند. وقتی ما از php-fpm به عنوان مدیر پردازش های factCGI که اسکریپت های PHP را کامپایل و هندل می کند در سرور خود استفاده می کنیم، در واقع داریم از یک سرویس مجزا برای پردازش های PHP استفاده می کنیم که کارها را پیش ببرد. php-fpm خودش یک پردازش اصلی یا master process هست – با این اصطلاح جلوتر یا در حین استفاده از php-fpm بیشتر آشنا می شوید– که این پردازش شامل تعدادی استخر (pool) از پردازش های کاری است که به آنها (worker processes) می گویند. 

وقتی که وب سرور یک درخواست ورودی برای یک اسکریپت php دارد، وب سرور با استفاده از یک پروکسی، یک اتصال FactCGI برای انتقال درخواست به سرویس PHP-FPM می زند. php-fpm به درخواست های بر روی پورت های شبکه یا سوکت های یونیکس گوش می دهد تا این درخواست ها را دریافت کند. 

به محض دریافت یک اتصال پروکسی شده از سمت وب سرور، یکی از worker های آزاد درخواست را قبول می کند و شروع به اجرای کد php می کند و بعد از آن پاسخ را به سمت وب سرور بر می گرداند. وقتی کار worker  به پایان رسید، سرویس این worker را آزاد می کند تا بتواند منتظر درخواست های بعدی باشد. 

در php-fpm می توانیم چند pool تعریف کنیم و برای هر کدام کانفیگ های مجزایی در نظر بگیریم و درخواست های مختلف به مسیر های مختلف را به pool های مختلفی متصل کنیم. هرچند در این مقاله ما تمرکز خودمان را بر روی تنظیمات یک pool قرار می دهیم. 

تنظیمات FPM

برای اینکه با کانفیگ FPM آشنا بشویم اجازه دهید اول با دستوری که وضعیت سرویس FPM را نشان می دهد شروع کنیم. در این دستور ویژگی ها و کانفیگ های اصلی این سرویس را مشخص می کند. فرض می کنیم شما با استفاده از مقاله ی نصبfpm  بر روی لینوکس، یک وب سرور apache  به همراه یک php-fpm بر روی لینوکس خود دارید. پس دستور زیر را وارد کنید:

systemctl status php7.3-fpm

اگر سرویس به خوبی در حال فعالیت باشد با نتیجه ی زیر رو برو می شوید: 

در تصویر بالا جدای از اینکه متوجه می شوید این سرویس چند دقیقه هست که فعال است و الان در چه وضعیتی به سر می برد، می بینید PID یا همان آی دی main process یا همان master process  که بالاتر به آن پرداختیم، را عنوان کرده است. این همان پردازشی هست که بقیه ی worker  ها را مدیریت می کند. اما اگر دقت کنید خواهید دید که یک سری پردازش دیگر هم وجود دارد که در عکس بالا تعداد آنها ده تا هست. این ها در واقع همان worker هایی هستند که به آن اشاره کردیم و به آنها Child  های FPM هم گفته می شود. این child ها می توانند وضعیت های مختلفی داشته باشند که با توجه به عکس بالا 0  تا active هستند و 10 تا idle  که در واقع منتظر و بیکار نشستند تا درخواستی از راه برسد.

همه ی اینها درون یک pool قرار دارند که بوسیله ی آن master process مدیریت می شوند. آدرس این pool و کانفیگ های آن نیز در تصویر بالا مشخص می باشد: 

/etc/php/7.3/fpm/php-fpm.conf

از این به بعد ما با این آدرس کار داریم . درون این آدرس و فایل هایی که درون این آدرس به آن اشاره شده است همه ی کانفیگ هایی که میخواهیم درباره ی آن توضیح بدیم وجود دارد . 

شاید برای شما سؤال شده باشد که این Idle  وActive  که در تصویر بالا وجود داشت، چه هستند. چرا باید ده تا idle داشته باشیم و همیشه اینها را منتظر نگه داریم که رم ما را درگیر کنند و اینکه اصلا این عدد ها را کجا و بر چه اساسی انتخاب می کنیم.

برای بررسی کانفیگ ها با من همراه شوید. 

فایل زیر را باز کنید (دقت کنید بسته به اینکه چه نسخه ای از php را استفاده می کنید مسیر ممکن است متفاوت باشد. در اینجا ما مسیر php7.3 را پیش میرویم)

/etc/php/7.3/fpm/php-fpm.conf

درباره ی کانفیگ هایی که در این فایل هست کامل توضیح می دهیم ولی به انتهای این فایل که بروید آدرسی به شما نشان داده است که pool  های پیش فرض FPM می باشد. 

خب برای ادامه باید به مسیری که در بالا مشخص شده است برویم و کانفیگ های pool را بررسی و مشخص کنیم. 

/etc/php/7.3/fpm/pool.d/www.conf

بهتر است شما را با چند تا از مهمترین کانفیگ های php-fpm  که در این آدرس وجود دارد، آشنا کنم:

در این فایل کانفیگ های زیر را در کنار بقیه کانفیگ ها می بینید. درباره ی این کانفیگ ها توضیح می دهیم.

[php-fpm-pool-settings] 
pm = dynamic 
pm.max_children = 25 
pm.start_servers = 8 
pm.min_spare_servers = 6 
pm.max_spare_servers = 10 

pm

این کانفیگ مشخص می کند کهprocess manager  چگونهchildren  ها را می سازد و مدیریت می کند. این کانفیگ می تواند سه حالت داشته باشد که بر اساس نوع رفتاری که با وب سرور شما می شود و نیازمندی خودتان باید آنها را مشخص کنید به صورتی که مصرف مموری شما به صورت بهینه صورت بگیرد.

حالت های مختلف process manager

pm = static 

در این حالت وقتی که سرویس php-fpm شروع به کار می کند تعداد مشخصیchildren  می سازد و همیشه آنها را آماده ی کار نگه میدارد. این حالت بهترین کارایی را برایfpm  دارد چون همیشه تعداد مشخصیchildren  آماده ی کار دارد. ولی اشکالی که این حالت دارد این است که همیشه حتی زمانی که فشاری بر روی سرور نیست و نیازی به این همه process children نیست، مموری درگیر است و رفتار وب سرور داینامیک بر اساس میزان درخواست ها نیست. children ها هم همیشه فعال هستند و قسمتی از پردازنده و مموری را مصرف می کنند.

در این حالت فقط کافی است که pm.max_children را مشخص کنیم و به تعدادی که مشخص کرده ایمworker  یا process children ساخته می شود.

pm = onDemand 

در این حالت به میزان نیازی که سرور داردchildren  ساخته می شود و پردازش را انجام می دهد. ولی به هر حال برای این حالت هم ما pm.max_children مشخص می کنیم که از این تعداد بیشتر هم نشود. از طرفی pm.process_idle_timeout هم مشخص میکنیم که اگر child آزاد بود و کاری نداشت و به تایم اوت رسید، توسط pm کشته (kill) شود. 

این حالت هم برای سرور هایی خوب است که رفتار خاصی دارند. مثلا تعداد ریکوئست هایی که به سمت آن می آید خیلی به ندرت می باشد و نیاز نیست همیشه تعدادی worker فعال آماده داشته باشد. ولی از این بابت که تقریبا برای هر ریکوئست یکchild  جدید ساخته می شود و بعد هم کشته می شود و این، نرخ ساختن و از بین بردنChild  ها را زیاد می کند. به همین دلیل در این حالت سربار ساخت و از بین بردن Child  وجود دارد. 

pm = Dynamic

در این حالت بر اساس مشخصه هایی که در کانفیگ های دیگر مشخص می کنیم و بر اساس میزان درخواست های وارد شده، children ها به صورت داینامیک ساخته می شوند و یا از بین می رود و بر این اساس می توان از منابع سرور به صورت بهینه استفاده کرد. در بیشتر سرور هایی که به شدت مشغول هستند و رفتارهای مختلفی را از خود نشان می دهند و یا اینکه نرخ درخواست های کاربر در طول روز متفاوت می باشد، از این حالت استفاده می کنند. 

برای این حالت باید کانفیگ های زیر را مشخص کنیم:

pm.max_children: بیشترین تعداد children که این سرویس می تواند بسازد. 

pm.start_servers: در ابتدای شروع کار سرویس چند children ساخته شود.

pm.min_spare_servers: چند children همیشه به صورت آزاد و آماده ی پذیرفتن درخواست وجود داشته باشد. اگر سرویس به وضعیتی برسد که تعداد این نوع children ها از این عدد کمتر بشود. pm شروع می کند تعدادی child می سازد تا به این عدد برسد. 

pm.max_spare_servers: تعداد children های آزاد که آماده ی پذیرفتن درخواست هستند از این عدد بیشتر نشود. اگر وضعیت سرویس به جایی برسد که تعداد این نوع children  ها از این عدد بیشتر شود، pm شروع می کند و تعدادی ازchildren  ها را می کشد تا به این عدد برسد.

بررسی لاگ FPM

برای اینکه بهتر با این کانفیگ های پیچیده آشنا شویم، پیشنهاد می کنم وقتی که سرویس فعال است، سری به لاگ FPM بزنیم و ببینیم در حالتdebug  سرویس چگونه رفتار می کند. برای اینکه وارد این مسیر می شویم: 

/etc/php/7.3/fpm/php-fpm.conf

و در قسمتی کهlog level  را مشخص می کند به صورت زیر در حالت debug قرار می دهیم: 

; Log level
; Possible Values: alert, error, warning, notice, debug
; Default Value: notice
log_level = debug

و بعد از آن لاگ php-fpm را با دستور زیر می بینیم: 

tail -f /var/log/php7.3-fpm.log

در اینجا من قسمتی از لاگ را که به دلیل دریافت ریکوئست های مختلف سرور هست قرار می دهم. کانفیگ pm را بر رویdynamic  قرار دادم و کانفیگ هایی که در بالا گفته شد را مشخص کردم: 

DEBUG: pid 5013, fpm_pctl_perform_idle_server_maintenance(), line 378: [pool www] currently 1 active children, 8 spare children, 9 running children. Spawning rate 1
DEBUG: pid 5013, fpm_pctl_perform_idle_server_maintenance(), line 378: [pool www] currently 2 active children, 7 spare children, 9 running children. Spawning rate 1
DEBUG: pid 5013, fpm_pctl_perform_idle_server_maintenance(), line 378: [pool www] currently 3 active children, 6 spare children, 9 running children. Spawning rate 1
DEBUG: pid 5013, fpm_pctl_perform_idle_server_maintenance(), line 378: [pool www] currently 5 active children, 4 spare children, 9 running children. Spawning rate 1
DEBUG: pid 5013, fpm_children_make(), line 428: [pool www] child 5846 started
DEBUG: pid 5013, fpm_pctl_perform_idle_server_maintenance(), line 427: [pool www] 1 child(ren) have been created dynamically
DEBUG: pid 5013, fpm_pctl_perform_idle_server_maintenance(), line 378: [pool www] currently 5 active children, 5 spare children, 10 running children. Spawning rate 2
DEBUG: pid 5013, fpm_children_make(), line 428: [pool www] child 5848 started
DEBUG: pid 5013, fpm_pctl_perform_idle_server_maintenance(), line 427: [pool www] 1 child(ren) have been created dynamically
DEBUG: pid 5013, fpm_pctl_perform_idle_server_maintenance(), line 378: [pool www] currently 5 active children, 6 spare children, 11 running children. Spawning rate 4
DEBUG: pid 5013, fpm_pctl_perform_idle_server_maintenance(), line 378: [pool www] currently 5 active children, 6 spare children, 11 running children. Spawning rate 1
DEBUG: pid 5013, fpm_pctl_perform_idle_server_maintenance(), line 378: [pool www] currently 5 active children, 6 spare children, 11 running children. Spawning rate 1

در تصویر بالا می بینید pm سعی داشته است که حداقل 6 پروسس فرزند را که حالت آزاد یا همان spare داشته باشند را نگه دارد به خاطر همین به محض اینکه سرور سرش شلوغ می شود و چند تا از spare ها شروع به کار می کنند و به حالت active می روند، pm  شروع به ساخت چند child  جدید می کند که به حداقل pm.min_spare_servers که 6 بوده برسد. 

DEBUG: pid 5013, fpm_pctl_perform_idle_server_maintenance(), line 378: [pool www] currently 3 active children, 10 spare children, 13 running children. Spawning rate 1
DEBUG: pid 5013, fpm_pctl_perform_idle_server_maintenance(), line 378: [pool www] currently 3 active children, 10 spare children, 13 running children. Spawning rate 1
DEBUG: pid 5013, fpm_pctl_perform_idle_server_maintenance(), line 378: [pool www] currently 2 active children, 11 spare children, 13 running children. Spawning rate 1
DEBUG: pid 5013, fpm_got_signal(), line 82: received SIGCHLD
DEBUG: pid 5013, fpm_event_loop(), line 435: event module triggered 1 events
DEBUG: pid 5013, fpm_children_bury(), line 261: [pool www] child 5015 has been killed by the process management after 2092.897533 seconds from start
DEBUG: pid 5013, fpm_pctl_perform_idle_server_maintenance(), line 378: [pool www] currently 2 active children, 10 spare children, 12 running children. Spawning rate 1
DEBUG: pid 5013, fpm_pctl_perform_idle_server_maintenance(), line 378: [pool www] currently 2 active children, 10 spare children, 12 running children. Spawning rate 1

در ادامه سرور سرش خلوت می شود وchildren  ها وقتی کارشان تمام می شود از حالتactive  به حالتspare  می روند. خب در لاگ بالا به جایی می رسد که تعدادspare  ها بیشتر از عدد pm.max_spare_servers می شود، پسpm  با ارسال سیگنالی سنگدلانه یکی از spare  ها را می کشد تا به عدد 10 برسد.

شاید سئوال بپرسید چرا باید یک تعدادی Spare همیشه داشته باشیم. دلیل این کار برای pm به این خاطر هست که ساختن و کشتن children سربار زمانی دارد. وقتی که سرور یک دفعه سرش شلوغ می شود به جای اینکه درگیر ساختنchild  جدید باشد که پاسخ بدهد، همیشه تعدادی آماده ی کار داشته باشد که به پردازش ها برسند، تا pm  کمی وقت داشته باشد که اگر نیاز شد تعدادchildren  ها را بیشتر کند. 

مثلا لاگ زیر را ببینید: 

DEBUG: pid 32638, fpm_children_make(), line 428: [pool www] child 32319 started
DEBUG: pid 32638, fpm_children_make(), line 428: [pool www] child 32320 started
DEBUG: pid 32638, fpm_children_make(), line 428: [pool www] child 32321 started
DEBUG: pid 32638, fpm_children_make(), line 428: [pool www] child 32322 started
DEBUG: pid 32638, fpm_pctl_perform_idle_server_maintenance(), line 427: [pool www] 4 child(ren) have been created dynamically
DEBUG: pid 32638, fpm_pctl_perform_idle_server_maintenance(), line 378: [pool www] currently 13 active children, 4 spare children, 17 running children. Spawning rate 8
WARNING: pid 32638, fpm_pctl_perform_idle_server_maintenance(), line 399: [pool www] seems busy (you may need to increase pm.start_servers, or pm.min/max_spare_servers), spawning 8 children, there are $
DEBUG: pid 32638, fpm_children_make(), line 428: [pool www] child 32327 started
DEBUG: pid 32638, fpm_children_make(), line 428: [pool www] child 32328 started

در این لاگ مشخص شده که سرور ناگهانی مشغول شده است و pm مجبور شده چندChildren  پشت سر هم بسازد. خب شاید به دلیل این است که عدد حداقل spare ها را خیلی کم گرفتیم. به همین دلیل php-fpm این هشدار را می دهد که به نظر میرسد که نیاز هست که pm.start_servers , pm.min/max_spare_servers را افزایش دهید که از این اتفاق ها نیافتد.

مواقعی هم هست که شما در لاگ ها به این هشدار بر میخورید: 

DEBUG: pid 32638, fpm_pctl_perform_idle_server_maintenance(), line 378: [pool www] currently 20 active children, 7 spare children, 25 running children. Spawning rate 1
DEBUG: pid 32638, fpm_pctl_perform_idle_server_maintenance(), line 378: [pool www] currently 24 active children, 5 spare children, 25 running children. Spawning rate 1
WARNING: pid 32638, fpm_pctl_perform_idle_server_maintenance(), line 391: [pool www] server reached pm.max_children setting (30), consider raising it
DEBUG: pid 32638, fpm_pctl_perform_idle_server_maintenance(), line 378: [pool www] currently 23 active children, 6 spare children, 25 running children. Spawning rate 1
DEBUG: pid 32638, fpm_pctl_perform_idle_server_maintenance(), line 378: [pool www] currently 23 active children, 6 spare children, 25 running children. Spawning rate 1

در این لاگ می گوید که شما دارید به حد نهایت children ها که 25 بود نزدیک می شوید و ممکن است اگر فشار بر روی سرور بیشتر شود دیگر قادر به پاسخگویی نباشد و به همین خاطر هشدار می دهد که شاید نیاز باشد شما دستی به کانفیگ ها بکشید و عدد  pm.max_childrenرا بیشتر کنید.

تا اینجا با مهمترین کانفیگ های php-fpm آشنا شدید و می دانید که دقیقا هر کانفیگ چه کاربردی دارد. در مقاله ی بعد به این می پردازیم که بر اساس منابع سرور شما بهترین حالت برای کانفیگ های سرویسphp-fpm  شما چه می تواند باشد. 

منابع : 

https://www.cyberciti.biz/tips/rhel-centos-fedora-apache2-fastcgi-php-configuration.html

https://10up.github.io/Engineering-Best-Practices/systems/

پیشنهادات بیشتر سکان بلاگ برای شما