مرورگر میچرخد، بعد از چند ثانیه صفحهٔ سفید یا پیام 504 Gateway Timeout میآید. لاگ PHP هیچ خطایی ندارد، CPU سرور هم پایین است، ولی درخواست هیچوقت کامل نمیشود. این وضعیت تقریباً همیشه یک معنی دارد: یکی از حلقههای زنجیرهٔ درخواست، پیش از آنکه پاسخ آماده شود، بیحوصله شده و اتصال را بسته است. کار شما پیدا کردن همان حلقه است، نه بالا بردن کورکورانهٔ همهٔ تایماوتها.
خطای 504 از کدام لایه میآید
درخواست کاربر قبل از رسیدن به کد شما از چند واسطه میگذرد. هر کدام تایماوت مستقل خودش را دارد و هرکدام زودتر تمام شود، همان پیام را میسازد:
- مرورگر کاربر (تایماوت داخلی، معمولاً طولانی)
- CDN یا پروکسی جلویی مثل Cloudflare
- وبسرور جلویی (Nginx یا Apache) که نقش reverse proxy را بازی میکند
- مدیر پردازش PHP مثل PHP-FPM
- کد اپلیکیشن و در انتها دیتابیس یا یک API بیرونی
نکتهٔ کلیدی این است که پیام 504 فقط از لایههای واسط میآید. اگر خود PHP بمیرد یا حافظه تمام کند، خطای 500 یا 502 میبینید، نه 504. پس دیدن 504 یعنی کد شما احتمالاً زنده است، فقط دیر جواب میدهد.
تفاوت 504 با 502 و 524
502 یعنی واسط نتوانست به سرور بالادستی وصل شود؛ اتصال برقرار نشد. 504 یعنی اتصال برقرار شد ولی پاسخ در بازهٔ مجاز نرسید. Cloudflare عدد 524 را برای همین حالت خودش استفاده میکند و پیشفرضش 100 ثانیه است. اگر در لاگ Cloudflare عدد 524 میبینید و در لاگ Nginx هیچ ردی از درخواست نیست، یعنی درخواست حتی به سرور شما نرسیده یا پاسخ قبل از 100 ثانیه آماده نشده. این تفکیک، نصف کار عیبیابی است.
کدام تایماوت زودتر تمام میشود
ترتیب واقعی را باید از لاگها دربیاورید، ولی ترتیب پیشفرض رایج اینطور است:
| لایه | پارامتر | مقدار پیشفرض رایج |
|---|---|---|
| Cloudflare | تایماوت پروکسی | 100 ثانیه |
| Nginx (proxy) | proxy_read_timeout | 60 ثانیه |
| PHP-FPM | request_terminate_timeout | معمولاً بدون محدودیت یا 30 ثانیه |
| PHP | max_execution_time | 30 ثانیه |
| MySQL | wait_timeout | 28800 ثانیه |
با این جدول، اولین چیزی که میکشد PHP است: در ثانیهٔ 30 اسکریپت را میکشد و اگر خروجی تمیزی نداشته باشد، Nginx منتظر میماند تا 60 ثانیه و بعد 504 میدهد. یعنی کاربر 60 ثانیه صبر میکند تا خطایی ببیند که ریشهاش در ثانیهٔ 30 بوده. اینجا اشتباه میکنند: max_execution_time را به 300 میبرند، ولی proxy_read_timeout را دست نمیزنند. نتیجه این است که خطا عوض نمیشود، فقط دیرتر میآید و لاگ PHP پر میشود از Maximum execution time of 30 seconds exceeded که دیگر آنجا نیست.
پیدا کردن حلقهٔ گلوگاه با لاگ
قبل از هر تغییری، باید بفهمید درخواست کجا گیر کرده. سه جا را همزمان نگاه کنید:
tail -f /var/log/nginx/error.log
tail -f /var/log/php*-fpm.log
tail -f /var/log/nginx/access.log | grep " 504 "
در لاگ دسترسی Nginx، فیلد $request_time و $upstream_response_time را اضافه کنید. اگر upstream_response_time عددی نزدیک به تایماوت شماست، مشکل در PHP یا دیتابیس است. اگر خط تیره یا صفر است، درخواست حتی به upstream نرسیده و مشکل در خود Nginx یا شبکه است.
log_format timed '$remote_addr $request_time $upstream_response_time "$request" $status';
برای دیدن کندترین درخواستها، لاگ را بر اساس زمان مرتب کنید. یک اسکریپت ساده کافی است، ولی اگر نمیخواهید دستی وقت بگذارید، ابزارهای رایگان وبمستر برای بررسی سریع پاسخ سرور و هدرها کار را کوتاه میکند.
وقتی مشکل در دیتابیس است
شایعترین ریشهٔ 504 در سایتهای وردپرسی، یک کوئری بدون ایندکس است. با فعال کردن slow query log در MySQL بفهمید کدام کوئری بیشتر از یک ثانیه طول میکشد:
SET GLOBAL slow_query_log = 'ON';
SET GLOBAL long_query_time = 1;
اگر کوئریای با Rows_examined بالای چند صد هزار دیدید، مشکل تایماوت نیست؛ مشکل ایندکس است و هیچ تایماوتی حلش نمیکند. اینجا اشتباه میکنند: بهجای اضافه کردن ایندکس، تایماوت را بالا میبرند و سایت را برای همه کندتر میکنند، چون هر درخواست کند حالا منابع را بیشتر اشغال میکند.
تنظیم درست تایماوتها بهترتیب
قاعده ساده است: تایماوت هر لایه باید از لایهٔ داخلی بزرگتر باشد، وگرنه لایهٔ بیرونی زودتر میبُرد و پیام گمراهکننده میدهد. یک ترتیب منطقی:
- PHP:
max_execution_time = 120 - PHP-FPM:
request_terminate_timeout = 130 - Nginx:
proxy_read_timeout 150s;وfastcgi_read_timeout 150s; - Cloudflare: در پلنهای بالاتر قابل افزایش، ولی زیر 100 ثانیه نگه داشتن منطقیتر است
هزینهٔ این کار را هم بگویید: هر ثانیه تایماوت اضافه، یک پردازش PHP-FPM را بیشتر اشغال میکند. اگر pm.max_children را هم بالا نبرید، چند درخواست کند کافی است تا صف پر شود و سایت برای همه 504 بدهد. تایماوت طولانی بدون ظرفیت کافی، مشکل را از یک کاربر به همهٔ کاربران منتقل میکند.
مسیر پیشنهادی برای سایتهای پربازدید
اگر ترافیک بالاست، بهجای طولانی کردن تایماوت، کار سنگین را از مسیر درخواست بیرون ببرید. گزارشهای سنگین را با cron و صف پردازش (queue) اجرا کنید و نتیجه را در دیتابیس یا کش بگذارید. اینطور صفحهٔ کاربر همیشه سریع جواب میدهد و تایماوت هیچوقت به مرحلهٔ اجرا نمیرسد. برای سایتهایی که به منابع بیشتری نیاز دارند، سرور اختصاصی امکان تنظیم دقیقتر این پارامترها را میدهد، ولی روی هاست اشتراکی معمولاً فقط بخشی از این مقادیر در دسترس شماست.
وقتی هیچ تایماوتی مشکل را حل نمیکند
سه حالت را در عمل زیاد دیدهام که تنظیم تایماوت بیفایده است:
- یک API بیرونی کند که خودش تایماوت ندارد. اینجا باید در کد تایماوت بگذارید، نه در سرور.
- یک حلقهٔ بیپایان در کد که هرگز تمام نمیشود. تایماوت فقط آن را میکشد، ولی ریشه سر جایش است.
- مشکل شبکه بین سرور و دیتابیس خارجی. با
mtrیاtracerouteبررسی کنید.
در مورد اول، تایماوت کوتاه در کد بهترین کار است: اگر API در 5 ثانیه جواب نداد، مقدار کششده را نشان بدهید. کاربر یک صفحهٔ کمی قدیمی میبیند، ولی صفحهٔ خطا نمیبیند.
پرسشهای پرتکرار
چرا خطای 504 فقط برای بعضی کاربران میآید؟
چون معمولاً به مسیر شبکه یا کش بستگی دارد. کاربری که از کش CDN سرو میشود هیچوقت به سرور شما نمیرسد و خطا نمیبیند. کاربری که درخواستش cache miss میخورد، مسیر کامل را طی میکند و اگر آن لحظه سرور شلوغ باشد، 504 میگیرد. برای همین باید لاگ را بر اساس IP و مسیر درخواست فیلتر کنید، نه فقط تعداد کل خطاها را بشمارید.
آیا بالا بردن max_execution_time خطای 504 را حل میکند؟
فقط اگر واقعاً PHP زودتر از بقیه لایهها قطع شده باشد و لایههای بیرونی هم به همان نسبت تایماوت بزرگتری داشته باشند. اگر Cloudflare در 100 ثانیه قطع میکند، بردن max_execution_time به 300 فقط باعث میشود پردازش بیفایده ادامه پیدا کند و کاربر همچنان خطا ببیند. اول ترتیب تایماوتها را درست کنید، بعد مقدارها را بالا ببرید.
چطور بفهمم مشکل از سرور است یا از کد سایت؟
یک فایل ساده PHP با محتوای <?php echo "ok"; ?> بسازید و مستقیم درخواست بزنید. اگر این فایل سریع جواب داد ولی صفحهٔ اصلی 504 میدهد، مشکل در کد یا دیتابیس است. اگر همان فایل ساده هم کند بود، مسئله در وبسرور، PHP-FPM یا منابع سرور است. این تست دو دقیقهای، جهت عیبیابی را مشخص میکند.
نقش DNS در خطای 504 چیست؟
معمولاً هیچ. 504 یعنی اتصال TCP برقرار شده و پاسخ نرسیده؛ اگر DNS مشکل داشت، خطای اتصال میدیدید نه 504. تنها استثنا وقتی است که رکورد شما به یک پروکسی یا سرویس واسط اشاره کند که خودش کند است. برای اطمینان از درست بودن رکوردها و مسیر، بررسی DNS و شبکه را انجام دهید و بعد سراغ تایماوتها بروید.
قدم بعدی مشخص است: لاگ Nginx را با $upstream_response_time فعال کنید، یک بار خطا را بازتولید کنید و ببینید عدد کجا متوقف میشود. همان عدد، لایهٔ مقصر را به شما نشان میدهد. اگر بعد از این بررسی به هاستی نیاز داشتید که این پارامترها را در اختیارتان بگذارد، هاست لینوکس سرورنت این امکان را میدهد؛ ولی تا وقتی ریشه را پیدا نکردهاید، تغییر هاست فقط خطا را جابهجا میکند.
دیدگاهها ۰
هنوز دیدگاهی ثبت نشده؛ اولین نفر باشید!