عنوان:

‫ NGINX برای توسعه‌دهندگان ASP.NET Core - قسمت چهارم - بهینه‌سازی عملکرد


نویسنده: وحید نصیری
تاریخ: ۱۴۰۴/۰۳/۳۰ ۰۸:۰۵
آدرس: www.dntips.ir
پس از اینکه اپلیکیشن خود را به صورت استاندارد و امن راه‌اندازی کردیم، وقت آن است که عملکرد آن را به سطح بالاتری ببریم. در این بخش، روی سه تکنیک کلیدی برای افزایش سرعت، مقیاس‌پذیری و پایداری اپلیکیشن تمرکز می‌کنیم.

بخش ۴: بهینه‌سازی عملکرد (Performance Optimization)
یک اپلیکیشن خوب، فقط اپلیکیشنی نیست که کار می‌کند؛ بلکه باید سریع، پاسخگو و قادر به مدیریت ترافیک بالا باشد. NGINX ابزارهای قدرتمندی برای رسیدن به این اهداف در اختیار ما قرار می‌دهد.

۱. Load Balancing (توزیع بار)
چرا به توزیع بار نیاز داریم؟
یک نمونه (Instance) از اپلیکیشن شما تنها می‌تواند تعداد محدودی از درخواست‌ها را به صورت همزمان پردازش کند. توزیع بار به دو دلیل اصلی یک تکنیک حیاتی است:
  • مقیاس‌پذیری (Scalability): به‌جای ارتقای سخت‌افزار سرور (Vertical Scaling)، می‌توانید چندین نمونه از اپلیکیشن خود را روی یک یا چند سرور اجرا کنید (Horizontal Scaling). NGINX ترافیک ورودی را بین این نمونه‌ها تقسیم کرده و ظرفیت پاسخگویی شما را چند برابر می‌کند.
  • پایداری و در دسترس بودن بالا (High Availability): اگر یکی از نمونه‌های اپلیکیشن شما به هر دلیلی از کار بیفتد، Load Balancer به صورت خودکار ترافیک را به سمت نمونه‌های سالم هدایت می‌کند. این یعنی کاربران شما تقریباً هیچ‌گاه با قطعی سرویس مواجه نخواهند شد (Zero-Downtime).

چگونه آن را پیاده‌سازی کنیم؟

۱. اجرای چند نمونه از اپلیکیشن:
ابتدا باید اپلیکیشن ASP.NET Core خود را روی پورت‌های مختلف اجرا کنید. برای مثال، سه نمونه روی پورت‌های 5000، 5001 و 5002. ساده‌ترین راه برای این کار، ایجاد چند کپی از فایل systemd با تغییر در خط ExecStart و شماره پورت است.

۲. تعریف یک گروه سرور با upstream:
در NGINX، ما یک "استخر" یا گروه از سرورهای پشتیبان را با استفاده از دستور upstream تعریف می‌کنیم. این بلوک باید در سطح http فایل nginx.conf یا یک فایل مجزا در conf.d قرار گیرد.
فایل /etc/nginx/nginx.conf را باز کرده و داخل بلوک http کد زیر را اضافه کنید:
http {
    # ... سایر تنظیمات http

    upstream my_aspnet_app_backend {
        # least_conn; # <-- می‌توانید الگوریتم را مشخص کنید
        server localhost:5000;
        server localhost:5001;
        server localhost:5002;
    }

    # ...
}

۳. استفاده از upstream در کانفیگ سایت:
حالا فایل کانفیگ سایت خود (my-aspnet-app.conf) را ویرایش کرده و به جای آدرس ثابت، نام upstream را در proxy_pass قرار دهید.
# در فایل /etc/nginx/sites-available/my-aspnet-app.conf

server {
    # ...
    location @proxy {
        proxy_pass http://my_aspnet_app_backend; # <--- این خط تغییر می‌کند
        # ... سایر تنظیمات پراکسی
    }
}
الگوریتم‌های توزیع بار:
  • round_robin (پیش‌فرض): درخواست‌ها به نوبت بین سرورها تقسیم می‌شوند.
  • least_conn: درخواست به سروری ارسال می‌شود که کمترین تعداد ارتباط فعال را دارد. برای درخواست‌های زمان‌بر مناسب است.
  • ip_hash: تضمین می‌کند که تمام درخواست‌های یک کاربر (بر اساس IP) همیشه به یک سرور خاص ارسال شود. این برای اپلیکیشن‌هایی که از Session داخل حافظه (In-Memory) استفاده می‌کنند، ضروری است. برای فعال‌سازی کافی است خط ip_hash; را به ابتدای بلوک upstream اضافه کنید.

فراموش نکنید که پس از تغییرات، کانفیگ را تست و NGINX را reload کنید: sudo nginx -t && sudo systemctl reload nginx.

۲. کش کردن محتوا (Caching)
کش کردن یکی از مؤثرترین راه‌ها برای افزایش سرعت سایت است. برای صفحاتی که محتوای آن‌ها به ندرت تغییر می‌کند (مانند پست‌های وبلاگ، لیست محصولات یا API های عمومی)، NGINX می‌تواند پاسخ را از اپلیکیشن شما دریافت کرده و در حافظه یا دیسک خود ذخیره کند. در درخواست‌های بعدی، NGINX پاسخ را مستقیماً از کش خود و بدون درگیر کردن اپلیکیشن ASP.NET Core شما به کاربر تحویل می‌دهد.

مراحل پیاده‌سازی:
۱. تعریف یک فضای کش:
ابتدا باید یک مسیر و یک ناحیه حافظه برای کش تعریف کنیم. این کار معمولاً یک بار و در بلوک http فایل nginx.conf انجام می‌شود.
# در فایل /etc/nginx/nginx.conf داخل بلوک http

proxy_cache_path /var/cache/nginx levels=1:2 keys_zone=my_app_cache:10m inactive=60m max_size=1g;
  • keys_zone=my_app_cache:10m: یک ناحیه حافظه اشتراکی به نام my_app_cache و با حجم ۱۰ مگابایت برای نگهداری کلیدهای کش ایجاد می‌کند.
  • inactive=60m: اگر یک آیتم کش شده به مدت ۶۰ دقیقه استفاده نشود، حذف خواهد شد.
  • max_size=1g: حداکثر حجم کش روی دیسک را ۱ گیگابایت تعیین می‌کند.

۲. اعمال کش در location مورد نظر:
حالا در فایل کانفیگ سایت خود، مشخص می‌کنیم که کدام درخواست‌ها باید کش شوند. برای مثال، فرض کنید می‌خواهیم خروجی یک API عمومی را کش کنیم:
# در فایل /etc/nginx/sites-available/my-aspnet-app.conf

server {
    # ...
    # برای دیباگ کردن، این هدر وضعیت کش را نشان می‌دهد (HIT, MISS, EXPIRED)
    add_header X-Proxy-Cache $upstream_cache_status;

    location /api/public/products {
        proxy_cache my_app_cache;
        proxy_cache_valid 200 10m;  # پاسخ‌های موفق (200) را ۱۰ دقیقه کش کن
        proxy_cache_valid 404 1m;   # پاسخ‌های "پیدا نشد" (404) را ۱ دقیقه کش کن

        # اگر سرور پشتیبان از دسترس خارج بود، از نسخه تاریخ مصرف گذشته کش استفاده کن
        proxy_cache_use_stale error timeout http_500 http_502 http_503 http_504;

        proxy_pass http://my_aspnet_app_backend;
        # ... سایر تنظیمات پراکسی
    }
    
    # سایر درخواست‌ها بدون کش به پراکسی ارسال می‌شوند
    location / {
        proxy_pass http://my_aspnet_app_backend;
        # ... سایر تنظیمات پراکسی
    }
}

۳. فشرده‌سازی (Compression)
فشرده‌سازی حجم فایل‌های متنی (مانند HTML, CSS, JS, JSON) را قبل از ارسال به مرورگر کاربر کاهش می‌دهد. این کار باعث کاهش زمان دانلود و بهبود تجربه کاربری، به خصوص در اینترنت‌های کندتر می‌شود.

نحوه فعال‌سازی:
فشرده‌سازی معمولاً به صورت سراسری برای تمام سایت‌ها فعال می‌شود. بهترین مکان برای این تنظیمات، فایل nginx.conf داخل بلوک http است.
# در فایل /etc/nginx/nginx.conf داخل بلوک http

##
# Gzip Settings
##
gzip on;
gzip_disable "msie6"; # برای مرورگرهای بسیار قدیمی IE6 غیرفعال شود

gzip_vary on;
gzip_proxied any;
gzip_comp_level 6; # سطح فشرده‌سازی بین ۱ تا ۹ (۶ یک تعادل خوب است)
gzip_buffers 16 8k;
gzip_http_version 1.1;
# فقط فایل‌های متنی را فشرده کن، نه تصاویر یا فایل‌های فشرده دیگر
gzip_types text/plain text/css application/json application/javascript text/xml application/xml application/xml+rss text/javascript;
  • gzip on;: فشرده‌سازی Gzip را فعال می‌کند.
  • gzip_types: لیست انواع فایل‌هایی (MIME Type) که باید فشرده شوند را مشخص می‌کند. بسیار مهم است که فرمت‌های از قبل فشرده شده مانند jpg یا png را در این لیست قرار ندهید.
نکته:Brotli یک الگوریتم فشرده‌سازی جدیدتر و معمولاً کارآمدتر از Gzip است. برای فعال‌سازی آن ممکن است نیاز به نصب یک ماژول اضافی در NGINX داشته باشید (sudo apt install libnginx-mod-brotli).

پس از اعمال تنظیمات، NGINX را reload کنید. حالا می‌توانید با استفاده از ابزارهای توسعه‌دهنده مرورگر (DevTools)، در تب Network ببینید که هدر Content-Encoding: gzip به پاسخ‌های شما اضافه شده است.

با پیاده‌سازی این سه تکنیک، اپلیکیشن شما نه تنها سریع‌تر اجرا می‌شود، بلکه برای مدیریت حجم بالای کاربران و مقاومت در برابر خطاها نیز آماده‌تر خواهد بود.