System Design

توزیع بار تطبیقی برای میکروسرویس‌ها

استراتژی‌های توزیع بار ایستا اغلب در محیط‌های پویای میکروسرویس که نمونه‌ها بر اساس تقاضا مقیاس‌بندی می‌شوند، شکست می‌خورند. روش‌های سنتی مانند Round-Robin یا کمترین اتصال، عملکرد یکنواخت سرورها را فرض می‌کنند که در محیط‌های تولید نادر است. توزیع بار تطبیقی با پایش مداوم سلامت سرویس‌ها و تنظیم وزن ترافیک در زمان واقعی، این مشکل را حل می‌کند و از استفاده بهینه از منابع اطمینان حاصل کرده و تأخیر را به حداقل می‌رساند.

مشکل وزن‌دهی ایستا

در یک معماری میکروسرویس معمولی، ممکن است پنج نمونه از یک سرویس پرداخت داشته باشید. در ابتدا، همه نمونه‌ها سالم هستند. با این حال، ممکن است یکی از نمونه‌ها شروع به تجربه توقف‌های جمع‌آوری زباله (Garbage Collection)، مصرف بالای CPU یا نشت حافظه کند. یک توزیع‌دهنده بار ایستا به ارسال ترافیک به هر پنج نمونه ادامه می‌دهد، که باعث افزایش تأخیر برای کاربرانی می‌شود که به گره در حال مبارزه دسترسی پیدا می‌کنند. در نهایت، آن گره ممکن است کاملاً از کار بیفتد و اگر توزیع‌دهنده بار به سرعت کافی واکنش نشان ندهد، یک شکست زنجیره‌ای را فعال کند.

بررسی‌های سلامت در زمان واقعی

توزیع تطبیقی مؤثر با بررسی‌های سلامت دقیق آغاز می‌شود. به جای اتکا صرف به کدهای وضعیت HTTP، ما زمان پاسخ، نرخ خطا و متریک‌های اشباع را پایش می‌کنیم. ما سه وضعیت را برای هر نمونه سرویس تعریف می‌کنیم: سالم، تحت فشار و ناسالم. یک نمونه در صورتی تحت فشار (Degraded) در نظر گرفته می‌شود که زمان پاسخ آن از یک آستانه (مثلاً تأخیر در صدک ۹۵م دو برابر خط پایه) فراتر رود یا نرخ خطای آن از یک درصد خاص عبور کند.

الگوریتم وزن‌دهی پویا

پس از داشتن داده‌های سلامت، ما یک وزن پویا برای هر نمونه محاسبه می‌کنیم. وزن، احتمال دریافت درخواست بعدی توسط یک نمونه را تعیین می‌کند. یک مدل ساده از کاهش نمایی (Exponential Decay) در اینجا خوب کار می‌کند. ما یک "امتیاز عملکرد" برای هر نمونه حفظ می‌کنیم که با گذشت زمان کاهش می‌یابد. درخواست‌های موفق امتیاز را افزایش می‌دهند، در حالی که درخواست‌های کند یا ناموفق آن را کاهش می‌دهند. سپس وزن بر اساس این امتیازها نرمال‌سازی می‌شود.


function calculateDynamicWeight(instance, healthData) {
    const baselineLatency = 100; // ms
    const currentLatency = healthData.latency;
    const errorRate = healthData.errorRate;
    
    // جریمه برای تأخیر بالا
    const latencyPenalty = Math.max(0, (currentLatency - baselineLatency) / baselineLatency);
    
    // جریمه برای خطاها
    const errorPenalty = errorRate * 10;
    
    // امتیاز ترکیبی: کمتر بهتر است
    const totalPenalty = latencyPenalty + errorPenalty;
    
    // وزن‌ها معکوس متناسب با جریمه هستند
    // حداقل وزن اطمینان می‌بخشد که برخی ترافیک همچنان برای تست بازیابی جریان دارد
    return Math.max(0.1, 1.0 - totalPenalty);
}

استراتژی پیاده‌سازی

در عمل، این منطق اغلب درون یک سایدکار (Sidecar) شبکه سرویس (مانند Envoy یا Linkerd) یا یک توزیع‌دهنده بار متمرکز جاسازی می‌شود. توزیع تطبیقی مبتنی بر سایدکار تأخیر کمتری ارائه می‌دهد زیرا بررسی‌های سلامت محلی هستند. سایدکار عملکرد هر نمونه بالادستی را ردیابی می‌کند و تصمیمات مسیریابی را در عرض میلی‌ثانیه‌ها تنظیم می‌کند. برای مثال، اگر تأخیر نمونه A جهش کند، سایدکار روی سرویس کلاینت فوراً ترافیک را به نمونه B و C منتقل می‌کند، بدون اینکه منتظر واکنش یک ارکستراتور متمرکز بماند.

مدیریت نوسان (Flapping) و هیسترزیس

یک دام رایج "نوسان" (Flapping) است، جایی که یک نمونه به دلیل مشکلات گذرا به سرعت بین وضعیت‌های سالم و ناسالم جابه‌جا می‌شود. برای جلوگیری از این موضوع، ما هیسترزیس (Hysteresis) را معرفی می‌کنیم. یک نمونه باید برای مدت مشخصی (مثلاً ۳۰ ثانیه) سالم بماند تا وزن آن به طور کامل بازیابی شود. به طور مشابه، باید به طور مداوم برای یک دوره کوتاه (مثلاً ۵ ثانیه) شکست بخورد تا به عنوان ناسالم علامت‌گذاری شود. این هموارسازی از نوسان بی‌مورد ترافیک توسط توزیع‌دهنده بار جلوگیری می‌کند.


class InstanceTracker {
    constructor(instanceId) {
        this.instanceId = instanceId;
        this.state = 'HEALTHY';
        this.lastStateChange = Date.now();
        this.consecutiveFailures = 0;
        this.consecutiveSuccesses = 0;
    }

    recordResult(success, latencyMs) {
        if (success) {
            this.consecutiveSuccesses++;
            this.consecutiveFailures = 0;
            
            // هیسترزیس: نیاز به ۳۰ ثانیه موفقیت برای بازیابی از DEGRADED
            if (this.state === 'DEGRADED' && Date.now() - this.lastStateChange > 30000) {
                this.state = 'HEALTHY';
                this.lastStateChange = Date.now();
            }
        } else {
            this.consecutiveFailures++;
            this.consecutiveSuccesses = 0;
            
            // هیسترزیس: نیاز به ۵ شکست برای علامت‌گذاری UNHEALTHY
            if (this.consecutiveFailures >= 5) {
                this.state = 'UNHEALTHY';
                this.lastStateChange = Date.now();
            } else if (this.consecutiveFailures >= 2) {
                this.state = 'DEGRADED';
                this.lastStateChange = Date.now();
            }
        }
    }
}

نتیجه‌گیری

توزیع بار تطبیقی برای ساخت معماری‌های میکروسرویس مقاوم ضروری است. با گذر از پیکربندی‌های ایستا به مسیریابی آگاه از عملکرد در زمان واقعی، می‌توانید تجربه کاربری و قابلیت اطمینان سیستم را به طور قابل توجهی بهبود دهید. با پیاده‌سازی بررسی‌های سلامت پایه و وزن‌دهی مبتنی بر تأخیر شروع کنید، سپس آستانه‌ها و پارامترهای هیسترزیس خود را بر اساس ویژگی‌های خاص سرویس خود اصلاح کنید. به یاد داشته باشید، هدف فقط اجتناب از شکست نیست، بلکه تخریب ظریف (Graceful Degradation) تحت فشار است.

Share: