راهنمای جامع و تخصصی Load Balancer در ASP.NET Core
در دنیای مدرن توسعه نرمافزار، جایی که ترافیک بالای کاربران و در دسترس بودن (Availability) سیستمها حرف اول را میزند، استفاده از یک Load Balancer (متعادلکننده بار) نه تنها یک انتخاب، بلکه یک ضرورت است. اکوسیستم ASP.NET Core به دلیل ماهیت بیحال (Stateless) و سبک بودن، یکی از بهترین بسترها برای کار در معماریهای توزیعشده و پشت Load Balancerهاست.
در این مقاله، به بررسی دقیق نحوه تعامل ASP.NET Core با Load Balancerها، چالشهای پیشرو و بهترین روشهای پیادهسازی آن میپردازیم.
Load Balancer چیست؟
Load Balancer یک دستگاه نرمافزاری یا سختافزاری است که ترافیک شبکه ورودی را بین چندین سرور پشت خود توزیع میکند. هدف اصلی آن جلوگیری از بار اضافی بر روی یک سرور واحد، افزایش قابلیت اطمینان و بهبود زمان پاسخگویی است.
نکته: در معماریهای مبتنی بر مایکروسرویس و Cloud، Load Balancerها نقشی کلیدی در روتینگ درخواستها به نمونههای (Instances) سالم ایفا میکنند.
الگوریتمهای رایج توزیع بار
Load Balancerها از الگوریتمهای مختلفی برای توزیع ترافیک استفاده میکنند. چند مورد از معروفترینها عبارتند از:
- Round Robin: درخواستها به ترتیب و به صورت چرخشی بین سرورها ارسال میشوند. (سادهترین روش)
- Least Connections: درخواست جدید به سروری ارسال میشود که در حال حاضر کمترین تعداد کانکشن فعال را دارد.
- IP Hash: بر اساس آدرس IP کلاینت، یک سرور خاص انتخاب میشود. این روش برای حفظ Session Sticky کاربردی است.
- Least Response Time: درخواست به سروری ارسال میشود که ترکیبی از کمترین کانکشنها و سریعترین زمان پاسخ را دارد.
چالشهای ASP.NET Core پشت Load Balancer
با وجود اینکه ASP.NET Core برای محیطهای توزیعشده بهینهسازی شده است، اما قراردادن آن پشت یک Load Balancer بدون تنظیمات صحیح میتواند به مشکلاتی نظیر تغییر ناگهانی IP کاربر، از کار افتادن Anti-Forgery Tokens و خطاهای HTTPS منجر شود.
۱. مشکل IP و Scheme واقعی کاربر
وقتی درخواستی از سمت کلاینت به Load Balancer میرسد، Load Balancer آن را به سرور ASP.NET Core فوروارد میکند. در این حالت، آدرس IP که برنامه شما میبیند، IP خود Load Balancer است، نه IP واقعی کاربر. همچنین، اگر ارتباط بین کاربر و Load Balancer از طریق HTTPS باشد، اما ارتباط داخلی بین Load Balancer و سرور شما از طریق HTTP باشد، ASP.NET Core فکر میکند درخواستها ناامن (HTTP) هستند.
راهحل: استفاده از Forwarded Headers Middleware.
۲. مشکل Data Protection Keys
ASP.NET Core به طور پیشفرض کلیدهای رمزنگاری خود (برای Authentication Cookies، Anti-Forgery Tokens و غیره) را در حافظه محلی (In-Memory) ذخیره میکند. اگر کاربر یک درخواست را به سرور A بفرستد و درخواست بعدی به سرور B برسد، سرور B نمیتواند کوکی کاربر را رمزگشایی کند و کاربر لاگاوت خواهد شد.
راهحل: استفاده از یک منبع مشترک (Distributed Storage) مانند Redis یا SQL Server برای ذخیره کلیدهای Data Protection.
پیکربندی Forwarded Headers در ASP.NET Core
برای حل مشکل IP و Scheme واقعی، باید میانافزار UseForwardedHeaders را در فایل Program.cs پیکربندی کنید.
var builder = WebApplication.CreateBuilder(args);
// تنظیمات Forwarded Headers
builder.Services.Configure<ForwardedHeadersOptions>(options =>
{
options.ForwardedHeaders = ForwardedHeaders.XForwardedFor | ForwardedHeaders.XForwardedProto;
// در صورت استفاده از Load Balancerهای شناخته نشده، این خط را کامنت کنید
// options.KnownNetworks.Clear();
// options.KnownProxies.Clear();
});
var app = builder.Build();
// فعالسازی میانافزار قبل از سایر میانافزارها
app.UseForwardedHeaders();
app.UseAuthentication();
app.UseAuthorization();
app.MapControllers();
app.Run();
هشدار امنیتی: اگر شبکههای و پروکسیهای مجاز را به درستی در
KnownProxiesوKnownNetworksتنظیم نکنید، کاربران مخرب میتوانند با جعل هدرX-Forwarded-For، IP خود را مخفی کنند.
پیادهسازی Health Checks برای Load Balancer
یکی از ویژگیهای بارز Load Balancerهای مدرن، بررسی سلامت (Health Check) سرورهاست. اگر یک نمونه از ASP.NET Core از کار بیفتد، Load Balancer باید سریعاً ترافیک را از آن سرور دور کند.
ASP.NET Core کتابخانهای قدرتمند به نام Health Checks دارد.
// افزودن سرویس Health Checks
builder.Services.AddHealthChecks()
.AddSqlServer(builder.Configuration.GetConnectionString("DefaultConnection")!)
.AddRedis(builder.Configuration.GetConnectionString("RedisConnection")!);
var app = builder.Build();
// ایجاد Endpoint برای بررسی سلامت
app.MapHealthChecks("/health");
Load Balancer (مانند Nginx یا HAProxy) هر چند ثانیه یک درخواست به /health میفرستد. اگر پاسخ 200 OK دریافت کرد، سرور را سالم میداند. اگرTimeout یا خطای 5xx دریافت کرد، سرور را از چرخه ترافیک خارج میکند.
مدیریت Data Protection در وبفارم (Web Farm)
همانطور که اشاره شد، برای کارکرد صحیح برنامه در حالت Load Balanced، کلیدهای رمزنگاری باید بین تمام نمونهها به اشتراک گذاشته شوند.
builder.Services.AddDataProtection()
.PersistKeysToStackExchangeRedis(
ConnectionMultiplexer.Connect(builder.Configuration.GetConnectionString("RedisConnection")!),
"DataProtection-Keys")
.SetApplicationName("MyLoadBalancedApp"); // حتما نام اپلیکیشن در تمام سرورها یکسان باشد
با این تنظیمات، حتی اگر درخواست کاربر توسط الگوریتم Round Robin به سرورهای متفاوتی ارسال شود، هیچ مشکلی در احراز هویت و اعتبارسنجی فرمها پیش نخواهد آمد.
معرفی YARP (Yet Another Reverse Proxy)
مایکروسافت یک پروکسی معکوس و Load Balancer متنباز به نام YARP بر پایه ASP.NET Core توسعه داده است. YARP به شما اجازه میدهد تا یک پروکسی قدرتمند را دقیقاً با همان کدهای C# که میشناسید بسازید و آن را به عنوان API Gateway یا Load Balancer داخلی استفاده کنید.
پیادهسازی ساده YARP در ASP.NET Core
ابتدا پکیج زیر را نصب کنید:
dotnet add package Yarp.ReverseProxy
سپس در Program.cs:
var builder = WebApplication.CreateBuilder(args);
// افزودن YARP به سرویسها
builder.Services.AddReverseProxy()
.LoadFromConfig(builder.Configuration.GetSection("ReverseProxy"));
var app = builder.Build();
// فعالسازی مسیرهای پروکسی
app.MapReverseProxy();
app.Run();
و در appsettings.json (یا تنظیمات مشابه):
{
"ReverseProxy": {
"Routes": {
"route1": {
"ClusterId": "cluster1",
"Match": {
"Path": "/api/{**catch-all}"
}
}
},
"Clusters": {
"cluster1": {
"LoadBalancingPolicy": "RoundRobin",
"Destinations": {
"destination1": {
"Address": "http://localhost:5001/"
},
"destination2": {
"Address": "http://localhost:5002/"
}
}
}
}
}
}
بهترین روشها (Best Practices)
- استفاده از HTTPS Offloading: کار رمزگشایی SSL را به Load Balancer بسپارید تا منابع پردازشی سرورهای ASP.NET Core شما برای اجرای منطق کسبوکار آزاد شود. (با هدر
X-Forwarded-Protoاین کار مدیریت میشود). - طراحی Stateless: تا حد امکان از ذخیره Session در حافظه (In-Memory Session) پرهیز کنید. به جای آن از Distributed Cache مانند Redis استفاده کنید.
- زمانبندی Graceful Shutdown: هنگام خاموش کردن یک نمونه از ASP.NET Core، مطمئن شوید که Load Balancer ابتدا ترافیک ورودی جدید را قطع میکند و سپس منتظر اتمام درخواستهای در حال پردازش میماند.
- استفاده از cấuیا پیکربندی متمرکز: در محیطهای وبفارم، فایلهای فیزیکی پیکربندی (مانند
appsettings.json) باید بین تمام سرورها همگامسازی شوند یا از سرویسهایی مانند Azure App Configuration استفاده کنید.
جمعبندی
ASP.NET Core بستری فوقالعاده انعطافپذیر برای کار با Load Balancerهاست. با فعالسازی صحیح Forwarded Headers، استفاده از Health Checks و انتقال کلیدهای Data Protection به یک منبع مشترک، میتوانید سیستم خود را به صورت افقی (Horizontal Scaling) گسترش دهید و میلیونها درخواست را بدون افت کیفیت پردازش کنید. ابزارهایی مانند YARP نیز امکان ایجاد لایههای مدیریت ترافیک اختصاصی را در اکوسیستم داتنت فراهم کردهاند.