قبل از شروع به بحث در مورد سیستمهای توزیع شده، بهتر است ابتدا به سوالی اساسی که اساس بوجود آمدن سیستمهای توزیع شده است، پاسخ دهیم:
چرا یک سخت افزار به تنهایی پاسخگوی همه نیازهای ما نیست؟
همه میدانیم که در یک هستهی از پردازنده، چیزی بعنوان پردازش موازی وجود ندارد. هر هسته در هر لحظه میتواند یک پردازش را انجام دهد و این سرعت بالای در پردازش عملیات جاری و سوئیچ کردن بین دیگر عملیات است که حس موازی اجرا شدن آنها را به ما میدهد. یعنی در صورتیکه بخواهیم در یک سخت افزار با پردازندهی تک هستهای، برنامه نویسی موازی انجام بدهیم، در واقع هیچیک از عملیات ما بصورت موازی انجام نمیشوند. زمان پردازشی پردازنده، بر اساس تعداد عملیات و اولویت آنها، بین آنها تقسیم میشود. هر لحظه یکی از آنها اجرا میشود و با اتمام زمان اجرایش، نوبت به بعدی میرسد تا جاییکه تمام آنها به اتمام برسند. در این حالت پردازنده تک هستهای، برای 2 کار زمان صرف میکند؛ اول اجرای عملیات جاری و دوم سوئیچ کردن به عملیات بعدی.
حال در صورتیکه تعداد عملیاتی که باید در سیستم بصورت همزمان انجام شوند بیشتر شود، زمانیکه پردازنده باید برای سوئیچ صرف کند نیز بیشتر شده و در نتیجه زمان اجرای عملیات بیشتر میشود و آنها دیرتر به اتمام میرسند. با بیشتر شدن تعداد این عملیات، کار به جایی میرسد که دیگر پردازنده هیچ زمانی را برای پردازش یک عملیات ندارد و باید تمام وقت خود را با سوئیچ کردن بین آنها صرف کند. بله؛ ما سیستمی را طراحی کردهایم که شامل مجموعهای از عملیات است که هیچ یک اجرا نمیشوند!
راه حل چیست؟
ساده است. با افزایش تعداد هستههای پردازنده، سیستم ما قادر است تعداد عملیات بیشتری را بصورت همزمان انجام دهد که این عملیات به تعداد هستههای پردازنده، واقعا بصورت همزمان انجام میشوند. یعنی هر هسته در هر لحظه یک پردازش را میتواند بصورت جداگانه از سایر هستهها انجام دهد.
اینجا بود که نیازمندیهای ما باعث شدند سخت افزارها پیچیدهتر شوند. البته پیچیدگی بود که باعث تکامل آنها شد. تا اینجا برای انجام تعداد عملیات بیشتر میتوانیم سخت افزار را ارتقاء دهیم. همچنین در اینجا بود که مفهوم Parallel Systems تکامل پیدا کرد؛ سیستمهایی که توانایی اجرای همزمان چند عملیات را داشتند که همه آنها از یک حافظه، بصورت مشترک استفاده میکردند.
مشکل سیستمهای Parallel مشخص است. کارآیی این نوع سیستم، کاملا به سخت افزار و نوع پیاده سازی آنها وابسته است. یعنی در صورت نیاز به کارآیی بیشتر، تنها راه ارتقاء سخت افزار و بهینه کردن کدهاست. اما این روال را تا کجا میتوانیم انجام دهیم؟
برای روشن شدن مشکل بالا بیایید یک Web Application را بر روی یک سخت افزار اجرا کنیم. در یک Web Application یک Thread Pool شامل مجموعهای از Threadها میباشد که هر Thread وظیفه اجرای یک درخواست را بر عهده دارد. یعنی با دریافت یک درخواست، یک Thread از این مجموعه کم میشود و وظیفه پاسخ دهی به آن در خواست را بر عهده میگیرد. تعداد Thread هایی که در یک Thread Pool میباشند نیز وابسته به تعداد هستههای پردازنده میباشد. برای این تعداد بصورت پیشفرض مقداری در نظر گرفته میشود که بیشترین کارآیی را در یک هسته داشته باشد؛ مثلا در ASP.NET بصورت پیشفرض به ازای هر هستهی از CPU، تعداد 20 Thread به این مجموعه اضافه میشود. یعنی ما در یک پردازنده 2 هستهای تنها میتوانیم تعداد 40 درخواست را بصورت همزمان دریافت کنیم. در صورتیکه تعداد در خواستها در یک لحظه بیشتر از این تعداد باشد، تمام درخواستهای اضافی در صف دریافت قرار میگیرند تا یکی از این Threadها به درخواست خودش پاسخ دهد و به Thread Pool بازگردد و آماده اجرای درخواست بعدی باشد.
حال با فرض اینکه بصورت میانگین به هر درخواست در مدت 2 ثانیه پاسخ داده شود و در طول هر 2 ثانیه ما تعداد 200 درخواست جدید دریافت کنیم، یعنی در هر 2 ثانیه تعداد 160 درخواست در صف پردازش درخواست باقی میمانند. یعنی در مدت 10 ثانیه تعداد 800 درخواست پردازش نشده در این صف وجود دارند. در صورتیکه این روال ادامه پیدا کند، صف پردازش بزرگتر و بزرگتر میشود؛ تا جایی که دیگر حافظهای برای دریافت درخواستهای جدید نباشد. اینجاست که سیستم ما از دسترس خارج میشود. پس تصمیم میگیریم سخت افزار خود را ارتقاء دهیم و کدهای خود را نیز بهینه کنیم. مثلا جاییکه عملیات I/O را انجام میدهیم، برای استفادهی بهینه از Threadهای موجود، کدهای خود را بصورت Async اجرا کنیم.
تا حدودی مشکل ما فعلا حل شدهاست. بعد از مدتی بدلیل اضافه شدن نیازمندیهای جدید، تعدادکاربران فعال سیستم زیاد میشود و دوباره مشکل پوشش دادن تعداد بیشتر درخواست بوجود میآید. مجبوریم دوباره عملیات Scale-up یا Vertical scaling را انجام دهیم. بله؛ عملیاتی که ما در این سیستمها برای مقیاسپذیری انجام میدهیم، اصطلاحا Vertical scaling یا Scale-up نام دارد. یعنی با افزایش تعداد کاربران یا تعداد درخواست، کدها بهینهتر و سخت افزار ارتقاء پیدا میکند.
منبع :dotnettips.info
البته مثالی که ذکر شد به هیچ وجه با دنیای واقعی قابل مقایسه نیست. ممکن است شما سرویسی بسیار حیاتی را پیاده سازی کرده باشید که در شرایط خاص، هزاران یا میلیونها کاربر بصورت همزمان بخواهند درخواستهای خود را برای شما ارسال کنند. در این حالت شما دو راه دارید؛ اول اینکه مرتبا سرویس بسیار حیاتی خود را از دسترس خارج کنید و منتظر بمانید تا حجم تعداد درخواستهای کاربران کاهش یابد و یا مجبورید سخت افزار سرور خود را آنقدر ارتقاء دهید، تا این تعداد درخواست را بصورت همزمان پوشش دهد. واقعا هزینه تهیه کردن این سرور چقدر است؟
فرض کنید از سمت پایگاه داده نیز با مشکل روبرو شدهایم. حجم دادههای ما روبه افزایش است. فضای حافظهی آزاد تنها Server ی که دادههای ما را ذخیره میکند، رو به اتمام است. چاره چیست؟ آن را ارتقا دهیم؟ بله برای مدتی سرور را از دسترس خارج کرده و فضای آزاد را افزایش میدهیم. در این بین تمام سرویسهای ما که وابسته به این سرور هستند، از دسترس خارج میشوند. این کار برای دادههایی که ذاتا همیشه رو به افزایش هستند، چقدر باید تکرار شوند؟ چقدر باید هزینه کنیم تا این دادهها که تمام سرویسهای ما به آنها وابسته هستند، با مشکل مواجه نشوند، یا کارآیی بازیابی آنها پایین نیاید؟
حال بیاید از زاویه دیگری به ماجرا نگاه کنیم ما یک سرویس بسیار حساس با تعداد کاربران زیادی را داریم. از دسترس خارج شدن این سرویس برای ما بسیار هزینه بر است. اما تنها سرور بسیار قوی ما که برای آن هزینهی بسیار زیادی را پرداخت کردهایم، با مشکلی مواجه شده که ممکن است زمان زیادی برای رفع مشکل آن صرف شود. بله باز سرویس از دسترس خارج شده و ما با مشکل بسیار جدی مواجه شدهایم که ممکن است آیندهی سرویس بسیار مهم را به خطر بیاندازد. چاره چیست؟ مثلا تعدادی سرور مشابه سرور اصلی را خریداری کنیم و در صورتیکه سرور اصلی با مشکل جدی مواجه شد از آنها استفاده کنیم. بله هزینه چند برابر شد. فرض کنید به هر دلیل، برق قسمتی که شما این سرورها را نگهداری میکنید، قطع شد! چه راهکاری را دارید؟ واقعیتی که باید بپذیریم این است که چون ما یک سرور را برای اجرای Application خودمان داریم، در هرصورت اگر این سرور با مشکلی مواجه شود، تمام سرویسهای ما با خطری جدی مواجه میشوند و ما نیز در صورتیکه بخواهیم این چرخهی معیوب را ادامه دهیم، تنها هر بار صورت مسئله را پاک میکنیم. بهتر است روش جدیدی را برای این مشکل بیابیم.
اینجاست که مفهوم سیستمهای توزیع شده به کمک سیستمهای Parallel میآید و مفهوم Scale-up یا Vertical scaling با مفهموم Horizontal Scaling یا Scale-out ادغام میشود. در قسمت بعدی، با مفاهیم، خصوصیات و اصطلاحات موجود در این سیستمها آشنا میشویم.