توسعهدهندگان شاهد این هستند که اپلیکیشن تازه لانچشدهشان درست زمانی که چند هزار کاربر روی «go» کلیک میکنند، متوقف میشود؛ و این کندی بهندرت ناشی از باگ در کد است – بلکه نبرد بین CPU و RAM سرور برای فضای موجود است. این گلوگاه خود را به صورت بارگذاری طولانیتر صفحات، تایماوتها (time-outs) یا کرشهای کامل نشان میدهد و به تجربه کاربری، درآمد و اعتماد به برند آسیب میزند.
چرا سروری که در محیط آزمایشگاهی خوب کار میکرد، در محیط عملیاتی از کار میافتد
در طول مرحله توسعه، یک توسعهدهنده تنها چند درخواست ارسال میکند، بنابراین منابع سرور بیشتر اوقات بیکار هستند. وقتی اپلیکیشن وارد مرحله عملیاتی (live) میشود، هر بازدیدکننده درخواستی ایجاد میکند که به دو جزء اصلی نیاز دارد:
- CPU (واحد پردازش مرکزی) – پردازندهای که هر حلقه، تابع و محاسباتی را اجرا میکند. آن را مانند سرآشپزی تصور کنید که فقط میتواند تعداد محدودی غذا را در آن واحد آماده کند. یک سفارش بلافاصله سرو میشود؛ صد سفارش به این معناست که سرآشپز همچنان با همان سرعت کار میکند، اما مشتریان مدت بیشتری منتظر میمانند.
- RAM (حافظه دسترسی تصادفی) – حافظه موقت برای دادههایی که CPU هنگام مدیریت یک درخواست به آنها نیاز دارد. مانند میزی است که سرآشپز مواد اولیه هر غذا را روی آن میگذارد. اگر میز پر باشد، سرآشپز باید تا زمانی که فضا خالی نشود، پذیرش سفارشهای جدید را متوقف کند.
وقتی هزاران کاربر بهطور همزمان وارد میشوند، هر درخواست سهم خود را از زمان CPU و بخشی از RAM را اشغال میکند. مجموعه محدود هر دو منبع در میان درخواستها تقسیم میشود و صف طولانی میگردد. خودِ سرور کندتر نشده است؛ بلکه زمان انتظار برای هر درخواست افزایش یافته است.
وسوسهی «فقط یک دستگاه بزرگتر بخریم»
اولین واکنش رایج، ارتقای دستگاه است – روشی که vertical scaling (مقیاسپذیری عمودی) نامیده میشود. اضافه کردن هستههای CPU بیشتر یا RAM بیشتر، ظرفیت را بهبود میبخشد: تغییر از ۴ هسته به ۱۶ هسته، یا از ۸ گیگابایت به ۶۴ گیگابایت، میتواند بدون تغییر در کد، حجم بالای ترافیک را جذب کند.
با این حال، مقیاسپذیری عمودی با یک سقف سخت روبرو میشود:
- محدودیتهای فیزیکی – هر مادربرد تنها میتواند تعداد مشخصی هسته و مقدار محدودی حافظه را میزبانی کند.
- بازدهی نزولی – هزینه هر هسته یا گیگابایت اضافی بیشتر از قبلی است، در حالی که میزان بهبود عملکرد کاهش مییابد.
- نقطه شکست واحد (Single point of failure) – اگر سرور بیش از حد بزرگ از کار بیفتد، کل سرویس از دسترس خارج میشود.
به دلیل همین محدودیتها، غولهای صنعت – از پلتفرمهای استریم و موتورهای جستجو گرفته تا سایتهای تجارت الکترونیک – از استفاده از یک ماشین غولپیکر واحد فاصله گرفتهاند.
جایگزین: پخش کردن بار روی چندین دستگاه کوچکتر
به جای ساختن یک برج بلندتر، اپراتورها سرورهای بیشتری با اندازه متوسط اضافه میکنند و اجازه میدهند ترافیک بین آنها تقسیم شود. این رویکرد horizontal scaling (مقیاسپذیری افقی) باعث میشود هر ماشین در یک محدوده عملکردی مطلوب باقی بماند و از منحنی هزینهی تصاعدی در ارتقاهای عمودی جلوگیری میکند.
هماهنگ کردن چندین ماشین به یک load balancer (توازنکننده بار) نیاز دارد – یک نرمافزار یا سختافزار که هر درخواست ورودی را دریافت کرده و آن را به سروری که بیشترین ظرفیت خالی را دارد، ارسال میکند. این توازنکننده پیچیدگی را از مشتری پنهان میکند؛ از دید کاربر، سایت همچنان مانند یک نقطه اتصال واحد به نظر میرسد.
مقیاسپذیری افقی همچنین تابآوری (resilience) ایجاد میکند. اگر یک گره (node) کرش کند، توازنکننده بهسادگی ترافیک را به سمت گرههای سالم باقیمانده هدایت میکند و سرویس را زنده نگه میدارد.
نکاتی که هنگام اضافه کردن ماشینها باید رعایت کنید
- طراحی بدون وضعیت (Stateless design) – درخواستها نباید به دادههایی که فقط در حافظه یک سرور خاص ذخیره شدهاند وابسته باشند؛ در غیر این صورت، ممکن است کاربر به گرهای هدایت شود که فاقد بافت (context) مورد نیاز است. استفاده از کشهای مشترک یا پایگاههای داده این مشکل را حل میکند.
- بررسی سلامت (Health checks) – توازنکننده بار باید بتواند سریعاً یک سرور در حال خرابی را شناسایی کرده و ارسال ترافیک به آن را متوقف کند.
- سیاستهای مقیاسپذیری خودکار (Auto-scaling policies) – بسیاری از پلتفرمهای ابری به شما اجازه میدهند آستانههایی (مانند میزان استفاده از CPU یا تأخیر درخواست) تعریف کنید که بهطور خودکار نمونهها (instances) را راهاندازی یا خاموش کنند تا هزینهها با میزان تقاضا همسو بماند.
نکته مقابل: مقیاسپذیری عمودی از بین نرفته است
برای تیمهای کوچک یا اپلیکیشنهایی با ترافیک کم، یک سرور قدرتمندِ واحد میتواند سادهترین و ارزانترین راهکار باشد. اگر جهش ترافیک قابل پیشبینی باشد (مثلاً یک عرضه محصول برنامهریزی شده)، یک ارتقای عمودی موقت ممکن است عملیتر از تهیه مجموعهای از نمونههای جدید باشد.
کلید کار در این است که تشخیص دهید چه زمانی ترفند «دستگاه بزرگتر» دیگر ارزش متناسب با هزینه را ارائه نمیدهد و از آن زمان برنامهریزی برای توزیع بار را شروع کنید.
نتیجهگیری
کند شدن سرور پس از راهاندازی معمولاً ناشی از مسئله رقابت بر سر منابع است، نه یک نقص در کد. چرخههای CPU و اسلاتهای RAM محدود هستند و وقتی درخواستهای زیادی بهطور همزمان میرسند، در صف قرار میگیرند که این امر باعث طولانی شدن زمان پاسخگویی میشود. مقیاسپذیری عمودی مقداری ظرفیت اضافی برای شما فراهم میکند، اما بهزودی با محدودیتهای فیزیکی و اقتصادی مواجه میشود. مقیاسپذیری افقی — یعنی اضافه کردن سرورهای معمولیتر در پشت یک متعادلکننده بار — با افزایش ترافیک، مسیری ارزانتر و منعطفتر را ارائه میدهد. لحظهای که متوجه طولانی شدن صف شدید، زمان آن فرا رسیده است که ارزیابی کنید آیا چند هسته بیشتر کافی خواهد بود یا باید شروع به توزیع بار بین چندین ماشین کنید.
منبع: مقاله dev.to با عنوان “Why Servers Slow Down – CPU, RAM and the hidden cost of every request.”
