مفهوم MPM در Apache و انتخاب آن
MPM یا Multi-Processing Module بخشی از هسته آپاچی است که تعیین میکند وبسرور چگونه اتصالهای شبکه را بپذیرد و هر درخواست را به کدام پردازه یا نخ بسپارد. در هر لحظه فقط یک MPM میتواند فعال باشد و انتخاب بین prefork، worker و event مستقیماً روی مصرف رم و تعداد کاربر همزمانی که سرور تحمل میکند اثر میگذارد؛ روی سرورهای امروزی که PHP با PHP-FPM اجرا میشود، event گزینه درست و متداول است.
MPM دقیقاً چه کاری انجام میدهد
آپاچی معماری ماژولار دارد، اما MPM یک ماژول معمولی مثل mod_rewrite نیست؛ لایهای است که به پورت گوش میدهد، اتصال ورودی را میپذیرد و آن را به یک واحد پردازشی تحویل میدهد. چون هر سه MPM دقیقاً همین یک وظیفه را به روشهای متفاوت انجام میدهند، فعال بودن همزمان دو تای آنها بیمعنی است. به همین دلیل هم تنظیمات کارایی آپاچی مثل MaxRequestWorkers یا ThreadsPerChild به MPM فعال وابستهاند و با تعویض MPM باید بازبینی شوند.
سه MPM آپاچی و تفاوت واقعی آنها
prefork
در این حالت آپاچی چند پردازه فرزند مستقل میسازد و هر پردازه در هر لحظه فقط یک اتصال را مدیریت میکند. هیچ نخی در کار نیست، بنابراین کتابخانههایی که thread-safe نیستند بدون دردسر کار میکنند. بهای این امنیت، مصرف حافظه است: هر بازدیدکننده همزمان یعنی یک پردازه کامل آپاچی در رم. روی یک سرور مجازی ابری با رم محدود، همین موضوع خیلی زودتر از آنچه تصور میکنید به سقف میخورد.
worker
مدل ترکیبی است: چند پردازه فرزند و داخل هر پردازه چند نخ. هر نخ یک اتصال را مدیریت میکند، پس برای پذیرش همان تعداد کاربر به پردازههای بهمراتب کمتری نیاز است و مصرف رم پایین میآید. ایراد باقیمانده این است که نخ تا وقتی اتصال keep-alive باز است مشغول میماند، حتی اگر کاربر هیچ درخواستی نفرستد.
event
همان ساختار worker است با یک تفاوت مهم: نخ جداگانهای اتصالهای بیکار و در حالت keep-alive را نگه میدارد و نخهای کارگر را آزاد میکند تا سراغ درخواستهای جدید بروند. نتیجه این است که با همان مقدار رم، تعداد اتصال همزمان بسیار بیشتری را پاسخ میدهید. از آپاچی ۲.۴ به بعد این MPM حالت آزمایشی ندارد و انتخاب پیشفرض بیشتر توزیعهای امروزی است.
| MPM | مدل | مصرف رم | مناسب برای |
prefork |
یک پردازه برای هر اتصال | بالا | وقتی mod_php یا ماژول غیر thread-safe دارید |
worker |
پردازه + نخ | متوسط | ترافیک بالا بدون mod_php |
event |
پردازه + نخ + مدیریت جدا keep-alive | پایین | حالت پیشنهادی همراه PHP-FPM |
هرچه از prefork به سمت event بروید، برای همان تعداد اتصال همزمان رم کمتری لازم است؛ تنها چیزی که شما را به prefork زنجیر میکند، اجرای PHP به شکل mod_php است.
چطور بفهمیم کدام MPM فعال است
روی سرورهای مبتنی بر RHEL، CentOS، AlmaLinux و همچنین سرورهای cPanel:
httpd -V | grep -i "Server MPM"httpd -M | grep mpm
روی دبیان و اوبونتو:
apache2ctl -V | grep -i "Server MPM"apache2ctl -M | grep mpm
خروجی خطی مثل Server MPM: event یا mpm_prefork_module (shared) به شما میگوید کدام حالت فعال است. اگر همین حالا PHP را هم بررسی میکنید، دستور php -i | grep "Server API" نشان میدهد PHP بهصورت Apache 2.0 Handler اجرا میشود یا FPM/FastCGI؛ ریشه این نامها به استاندارد قدیمی CGI برمیگردد که همین امروز هم پایه اجرای اسکریپت روی وبسرور است. انتخاب خود وبسرور هم مسئله جداگانهای است و آپاچی را در مقایسه وبسرورهای Apache، LiteSpeed، Nginx و IIS کنار رقبایش گذاشتهایم.
اگر سرویس شما هاست اشتراکی است این دستورها در دسترس شما نیستند؛ MPM تنظیمی در سطح کل سرور است و از کنترلپنل کاربر تغییر نمیکند. انتخاب و تعویض MPM فقط روی سروری معنا دارد که دسترسی روت آن در اختیار خودتان باشد.
چرا mod_php شما را به prefork زنجیر میکند
وقتی PHP به شکل ماژول داخلی آپاچی (mod_php) اجرا شود، مفسر PHP داخل خود پردازه آپاچی زندگی میکند. بسیاری از افزونههای PHP و کتابخانههای سیستمی که به آنها لینک میشوند thread-safe نیستند و اجرایشان داخل یک MPM نخمحور میتواند به خطاهای تصادفی و کرش منجر شود؛ به همین دلیل mod_php فقط با prefork توصیه میشود.
PHP-FPM این گره را باز میکند. در این مدل PHP در یک سرویس کاملاً جدا اجرا میشود و آپاچی از طریق mod_proxy_fcgi درخواست را به آن میسپارد. حالا آپاچی دیگر درگیر thread-safe بودن PHP نیست و میتوانید سراغ event بروید؛ ضمن اینکه تعداد پردازههای PHP را مستقل از اتصالهای وب تنظیم میکنید.
تغییر MPM در عمل
پیش از هر تغییری از فایل پیکربندی نسخه پشتیبان بگیرید و بدانید که ریاستارت آپاچی قطعی کوتاه سرویس دارد؛ این کار را در ساعت کمترافیک انجام دهید.
روی دبیان و اوبونتو، ابتدا اگر mod_php فعال است آن را غیرفعال و PHP-FPM را جایگزین کنید (شماره نسخه را با نسخه نصبشده خودتان عوض کنید):
a2dismod php8.1a2enmod proxy_fcgi setenvifa2enconf php8.1-fpma2dismod mpm_preforka2enmod mpm_eventapache2ctl configtestsystemctl restart apache2
روی RHEL و مشتقاتش دستور a2enmod وجود ندارد و انتخاب MPM داخل فایل /etc/httpd/conf.modules.d/00-mpm.conf انجام میشود: خط LoadModule mpm_prefork_module modules/mod_mpm_prefork.so را کامنت کنید و خط LoadModule mpm_event_module modules/mod_mpm_event.so را از کامنت خارج کنید، سپس apachectl configtest و در صورت سالم بودن خروجی systemctl restart httpd.
روی سرورهای cPanel این کار از مسیر WHM » EasyApache 4 انجام میشود و هندلر PHP هم در MultiPHP Manager روی php-fpm تنظیم میشود؛ اینجا دستکاری مستقیم فایلها توصیه نمیشود چون در بازسازی بعدی بازنویسی میشوند. اگر ترجیح میدهید این مراحل را خودتان جلو نبرید، در سرور مجازی مدیریتشده ایران نصب و پیکربندی کنترلپنل و رفع اشکال نرمافزارهای نصبشده بر عهده تیم پشتیبانی است.
تنظیم MPM بر اساس رمی که واقعاً دارید
انتخاب MPM نصف کار است؛ نصف دیگر این است که سقف کارگرها را متناسب با حافظه سرور ببندید. ابتدا میانگین حافظه هر پردازه آپاچی را اندازه بگیرید:
ps -ylC httpd --sort:rss | awk 'NR>1 {s+=$8; n++} END {print s/n/1024" MB"}'
روی دبیان و اوبونتو بهجای httpd از apache2 استفاده کنید. حالا حافظهای که میخواهید به آپاچی اختصاص دهید (یعنی رم کل منهای سهم دیتابیس، PHP-FPM و سیستمعامل) را بر این میانگین تقسیم کنید تا سقف منطقی MaxRequestWorkers به دست بیاید.
در prefork این عدد مستقیماً یعنی تعداد پردازهها. در worker و event باید مضربی از ThreadsPerChild باشد و ServerLimit هم دستکم برابر حاصل تقسیم MaxRequestWorkers بر ThreadsPerChild تنظیم شود، وگرنه آپاچی مقدار شما را نادیده میگیرد و در لاگ هشدار میدهد. یک پیکربندی نمونه برای event:
StartServers 3ServerLimit 16ThreadsPerChild 25MaxRequestWorkers 400MinSpareThreads 75MaxSpareThreads 250MaxConnectionsPerChild 10000
هشدار مهم: بالا بردن MaxRequestWorkers بدون رم کافی سرور را سریعتر از دست میدهد، نه سریعتر میکند. وقتی حافظه تمام شود سیستم به swap میافتد و در ادامه OOM Killer سرویسها را میبندد؛ همین حالت روی دایرکتادمین به شکل مصرف رم بالای آپاچی خودش را نشان میدهد. اگر با تنظیم درست هم به سقف میخورید، مشکل پیکربندی نیست و باید منابع را بالا ببرید؛ برای سایتهای پربازدید و دیتابیسهای سنگین، اجاره سرور اختصاصی کنترل کامل روی سختافزار و پیکربندی را در اختیار شما میگذارد.
اشتباههای رایج
- قرار دادن دستورهای مخصوص یک MPM بیرون از بلوک درست. مقادیر
ThreadsPerChildباید داخل<IfModule mpm_event_module>باشند؛ اگر آنها را زیر بلوکpreforkبنویسید آپاچی یا نادیده میگیرد یا با خطا بالا نمیآید. - استفاده از نام قدیمی دستورها. از آپاچی ۲.۴ نام
MaxClientsبهMaxRequestWorkersتغییر کرده است و مخلوط کردن این دو یکی از دلایل متداول خطایInvalid command MaxRequestWorkersاست. - ریاستارت بدون تست. همیشه قبل از
systemctl restartدستورapachectl configtestرا بزنید تا سرویس با فایل خراب پایین نماند. - فراموش کردن
KeepAliveTimeout. رویpreforkمقدار بالای این پارامتر پردازهها را بیدلیل اشغال نگه میدارد؛ رویeventفشار آن بهمراتب کمتر است.
جمعبندی
تا وقتی mod_php دارید ناچار به prefork هستید؛ بهمحض جدا کردن PHP، گزینه درست برای اکثر سایتها event است و worker عملاً فقط برای سازگاری با نصبهای قدیمی معنا دارد. بعد از تعویض حتماً مقادیر کارگرها را با رم واقعی سرور هماهنگ کنید و رفتار حافظه را در ساعتهای اوج ترافیک زیر نظر بگیرید؛ عددی که روی کاغذ درست به نظر میرسد را فقط بار واقعی تأیید یا رد میکند.