مقدمه: چرا کنترل از دست اپلیکیشن من خارج شده است؟
در اینجا میخواهیم در مورد یکی از تجربیات مشترک و گیجکننده صحبت کنیم: شما یک پردازش سنگین در اپلیکیشن ASP.NET Core خود دارید (مثلاً ساخت یک گزارش پیچیده که ۲ دقیقه طول میکشد). Timeout مربوط به HttpClient یا Command پایگاه داده را روی ۳ دقیقه تنظیم کردهاید، اما بعد از ۶۰ ثانیه، کاربر با خطای زشت 504 Gateway Time-out مواجه میشود. انگار که NGINX به تنظیمات اپلیکیشن شما هیچ اهمیتی نمیدهد! خبر خوب این است که حق با شماست. NGINX واقعاً اهمیتی نمیدهد! چرا؟
برای درک این موضوع، از یک مثال ساده استفاده میکنیم: مسابقه دو امدادی با یک داور سختگیر.
- دونده اول (کلاینت/مرورگر): چوب امدادی (درخواست) را به داور میدهد.
- داور (NGINX): یک داور بسیار سختگیر با یک کرونومتر در دست. او چوب را از دونده اول میگیرد و به دونده دوم میدهد.
- دونده دوم (اپلیکیشن ASP.NET Core): این دونده باید قبل از دویدن، یک معمای پیچیده را حل کند (پردازش سنگین دیتابیسی).
مشکل اینجاست: داور (NGINX) کرونومتر خود را روی ۶۰ ثانیه تنظیم کرده است. دونده دوم (اپلیکیشن شما) برای حل معما به ۹۰ ثانیه زمان نیاز دارد. سر ۶۰ ثانیه، داور سوت را به صدا درمیآورد، تیم را بازنده اعلام میکند (خطای 504) و دونده اول را به خانه میفرستد، در حالی که دونده دوم همچنان در حال حل کردن معما بود!
نتیجه کلیدی: وقتی NGINX به عنوان Reverse Proxy عمل میکند، دو لایه Timeout وجود دارد: یکی مخصوص خود NGINX و دیگری متعلق به اپلیکیشن شما. کرونومتر NGINX همیشه اولویت دارد. اگر NGINX تسلیم شود، دیگر مهم نیست که اپلیکیشن شما هنوز در حال کار است.
بخش اول: کالبدشکافی Time-out ها در NGINX
NGINX چندین نوع کرونومتر (Timeout) برای مراحل مختلف ارتباط دارد. برای حل مشکل 504، ما باید با ۳ مورد اصلی آشنا شویم:
proxy_connect_timeout (زمان برای دست دادن)
- توضیح: حداکثر زمانی که NGINX منتظر میماند تا بتواند یک ارتباط اولیه (مثل یک دست دادن) با اپلیکیشن پشتیبان (Kestrel) برقرار کند.
- مقدار پیشفرض: معمولاً ۶۰ ثانیه.
- آیا مشکلساز است؟ به ندرت. این ارتباط چون داخلی (localhost) است، تقریباً همیشه آنی برقرار میشود. معمولاً نیازی به تغییر این مقدار نیست.
proxy_send_timeout (زمان برای ارسال کامل درخواست)
- توضیح: حداکثر زمانی که NGINX برای ارسال کامل درخواست کلاینت به اپلیکیشن پشتیبان صرف میکند.
- مقدار پیشفرض: ۶۰ ثانیه.
- آیا مشکلساز است؟ برای درخواستهای عادی خیر، اما برای آپلود فایلهای بسیار حجیم روی یک شبکه داخلی کند، ممکن است مشکلساز شود.
proxy_read_timeout (مهمترین و مقصر اصلی!)
- توضیح: حداکثر زمانی که NGINX منتظر میماند تا اپلیکیشن پشتیبان، کامل پاسخ خود را به NGINX برگرداند.
- مقدار پیشفرض:۶۰ ثانیه.
- آیا مشکلساز است؟بله! این همان کرونومتر داور در مثال ماست. اگر پردازش شما در ASP.NET Core (مثلاً کوئری دیتابیس، ساخت گزارش، پردازش تصویر) بیش از ۶۰ ثانیه طول بکشد، این Timeout فعال شده و NGINX ارتباط را قطع میکند و خطای
504 را برمیگرداند.
راه حل عملی:
شما باید مقادیر Timeout در NGINX را طوری تنظیم کنید که بیشتر از حداکثر زمان مورد نیاز اپلیکیشن شما باشد.
مثال پیکربندی:
این تنظیمات را میتوانید در بلوک http, server یا location فایل کانفیگ خود قرار دهید.
# افزایش زمانهای پیشفرض برای پردازشهای طولانی مدت
proxy_connect_timeout 60s;
proxy_send_timeout 180s; # ۳ دقیقه
proxy_read_timeout 180s; # ۳ دقیقه
با این تنظیمات، شما به داور (NGINX) میگویید که کرونومتر خود را روی ۳ دقیقه تنظیم کند و به دونده دوم (اپلیکیشن شما) زمان کافی برای حل معما میدهد.
بخش دوم: مدیریت آپلود فایلهای حجیم
آپلود فایلهای حجیم یک چالش دوگانه است: هم حجم درخواست (Request Body) زیاد است و هم ممکن است به خاطر سرعت پایین اینترنت کاربر، زمان زیادی طول بکشد. NGINX برای محافظت از سرور، به صورت پیشفرض محدودیتهای سختگیرانهای برای این دو مورد دارد.
client_max_body_size (محدودیت حجم آپلود)
- توضیح: این دستور حداکثر حجم مجاز برای بدنه درخواست (Request Body) را تعیین میکند. این شامل تمام دادههای POST شده، از جمله فایلهای آپلودی میشود.
- مقدار پیشفرض: یک مقدار بسیار کم، معمولاً فقط
1M (یک مگابایت)! - اثر: اگر کاربری سعی کند فایلی با حجم بیشتر از ۱ مگابایت آپلود کند، NGINX بلافاصله درخواست را رد کرده و خطای
413 Request Entity Too Large را برمیگرداند. این درخواست هرگز به اپلیکیشن ASP.NET Core شما نمیرسد.
client_body_timeout (محدودیت زمان ارسال)
- توضیح: حداکثر زمانی که NGINX منتظر میماند تا کلاینت بدنه درخواست خود را به طور کامل ارسال کند.
- مقدار پیشفرض: ۶۰ ثانیه.
- اثر: اگر کاربری اینترنت کندی داشته باشد و آپلود فایل ۱۰ مگابایتی او بیش از ۶۰ ثانیه طول بکشد، NGINX ارتباط را قطع میکند.
راه حل عملی:
این مقادیر را در بلوک http یا server افزایش دهید.
مثال پیکربندی:
# تنظیم در سطح http یا server
client_max_body_size 100M; # اجازه آپلود فایل تا سقف ۱۰۰ مگابایت
client_body_timeout 180s; # ۳ دقیقه زمان برای تکمیل آپلود توسط کاربر
نکته: میتوانید مقدار client_max_body_size را روی 0 تنظیم کنید تا محدودیت به طور کامل برداشته شود، اما این کار از نظر امنیتی توصیه نمیشود زیرا سرور شما را در برابر حملات ساده DoS آسیبپذیر میکند.
جمعبندی: یک مثال کامل
فرض کنید یک API برای آپلود ویدئو (/api/upload-video) و یک API دیگر برای ساخت گزارش (/api/generate-report) دارید.
server {
listen 80;
server_name myapp.com;
# تنظیمات عمومی برای کل سرور
client_max_body_size 200M; # حداکثر حجم آپلود برای کل سایت: ۲۰۰ مگابایت
# Location برای پردازشهای بسیار طولانی
location /api/generate-report {
proxy_pass http://my_aspnet_backend;
# به این API خاص ۵ دقیقه زمان میدهیم
proxy_read_timeout 300s;
proxy_connect_timeout 60s;
proxy_send_timeout 180s;
# ... سایر هدرهای پراکسی
}
# Location عمومی برای بقیه بخشهای سایت
location / {
proxy_pass http://my_aspnet_backend;
# استفاده از تایماوتهای عمومیتر
proxy_read_timeout 180s;
proxy_send_timeout 180s;
# ... سایر هدرهای پراکسی
}
}با این دانش، شما میتوانید به طور کامل رفتار NGINX را در مواجهه با پردازشهای طولانی مدت و آپلودهای حجیم کنترل کرده و از بروز خطاهای ناخواسته برای کاربران خود جلوگیری کنید.