Scalability and Architecture Trade-Offs

مقیاس‌پذیری فقط یکی از ویژگی‌های کیفی یا الزامات غیرکاربردی هست که زبان مشترکِ حوزه‌ی معماری نرم‌افزاره. یکی از پیچیدگی‌های همیشگی معماری نرم‌افزار، لزومِ موازنه‌ی بین ویژگی‌های کیفی مختلفه. به عبارت ساده، یه طراحی که یه ویژگی کیفی رو در اولویت قرار میده، ممکنه روی بقیه ویژگی‌ها تأثیر مثبت یا منفی بذاره. مثلا، شاید بخوایم موقع اتفاق افتادن رویدادهای خاص تو سرویس‌هامون، پیام‌های لاگ (log) بنویسیم تا بتونیم عیب‌یابی و پشتیبانی از کد رو انجام بدیم. اما باید مراقب باشیم که چه تعداد رویداد رو ثبت می‌کنیم، چون لاگ‌گرفتن سربار ایجاد می‌کنه و روی عملکرد و هزینه تأثیر منفی می‌گذاره.

معمارهای نرم‌افزار باتجربه، دائما طراحی‌هاشون رو برای برآورده کردن ویژگی‌های کیفی با اولویت بالا، تنظیم می‌کنن، در حالی که تأثیر منفی روی بقیه ویژگی‌های کیفی رو به حداقل می‌رسونن. مقیاس‌پذیری هم همین‌طوره. وقتی روی قابلیت مقیاس‌پذیری یه سیستم تمرکز می‌کنیم، باید با دقت در نظر بگیریم که طراحی ما چطور روی سایر ویژگی‌های مطلوب مثل عملکرد، دسترسی‌پذیری، امنیت و قابلیت مدیریت (که اغلب نادیده گرفته میشه) تأثیر می‌گذاره.

عملکرد (Performance)

یه راه ساده برای فکر کردن به تفاوت بین عملکرد و مقیاس‌پذیری وجود داره. وقتی هدفمون عملکرده، سعی می‌کنیم معیارهای خاصی رو برای درخواست‌های مجزا برآورده کنیم. این می‌تونه میانگین زمان پاسخ کمتر از ۲ ثانیه باشه، یا یه هدف عملکردی در بدترین حالت، مثل درصد نود و نهم زمان پاسخ کمتر از ۳ ثانیه. بهبود عملکرد به طور کلی برای مقیاس‌پذیری مفیده. اگه عملکرد درخواست‌های مجزا رو بهبود بدیم، ظرفیت بیشتری تو سیستم ایجاد می‌کنیم که به مقیاس‌پذیری کمک می‌کنه چون می‌تونیم از ظرفیت خالی برای پردازش درخواست‌های بیشتری استفاده کنیم. با این حال، همیشه به این سادگی نیست. ممکنه بتونیم زمان پاسخ رو به روش‌های مختلفی کاهش بدیم. شاید کد رو با دقت بهینه کنیم، مثلا کپی غیرضروری اشیاء رو حذف کنیم، از یه کتابخونه سریال‌سازی JSON سریع‌تر استفاده کنیم، یا حتی کل کد رو با یه زبان برنامه‌نویسی سریع‌تر بازنویسی کنیم. این رویکردها بدون افزایش استفاده از منابع، عملکرد رو بهینه می‌کنن. یه رویکرد جایگزین اینه که با نگه داشتن وضعیت (state) پرکاربرد تو حافظه به جای نوشتن تو دیتابیس برای هر درخواست، درخواست‌های مجزا رو بهینه کنیم. حذف یه دسترسی به دیتابیس تقریبا همیشه همه چیز رو سریع‌تر می‌کنه. اما اگه سیستم ما مقدار زیادی از وضعیت رو برای دوره‌های طولانی تو حافظه نگه داره، شاید (و تو یه سیستم با بارگذاری سنگین، قطعا) مجبور بشیم تعداد درخواست‌هایی که سیستم می‌تونه مدیریت کنه رو با دقت مدیریت کنیم. این احتمالا مقیاس‌پذیری رو کاهش میده چون رویکرد بهینه‌سازی ما برای درخواست‌های مجزا، از منابع بیشتری (تو این مورد، حافظه) نسبت به راه‌حل اولیه استفاده می‌کنه، و در نتیجه ظرفیت سیستم رو کم می‌کنه.

دسترسی‌پذیری (Availability)

در دسترس بودن و مقیاس‌پذیری عموما شرکای خیلی سازگاری هستن. با مقیاس‌گذاری سیستم‌هامون از طریق تکرار منابع، چندتا نمونه از سرویس‌ها رو ایجاد می‌کنیم که می‌تونن برای رسیدگی به درخواست‌های هر کاربری استفاده بشن. اگه یکی از نمونه‌هامون خراب بشه، بقیه در دسترس باقی می‌مونن. سیستم فقط به دلیل یه منبع خراب و در دسترس‌نبودن، با کاهش ظرفیت مواجه میشه. همین طرز فکر برای تکرار لینک‌های شبکه، روترهای شبکه، دیسک‌ها و تقریبا هر منبعی تو یه سیستم کامپیوتری صدق می‌کنه.موضوع با مقیاس‌پذیری و در دسترس بودن، زمانی که با وضعیت (state) سروکار داریم، پیچیده می‌شه. به یه دیتابیس فکر کنین. اگه سرور دیتابیس تنهامون بیش از حد بارگذاری بشه، می‌تونیم اون رو تکرار کنیم و درخواست‌ها رو به هر کدوم از نمونه‌ها ارسال کنیم. این همچنین در دسترس‌بودن رو هم افزایش می‌ده چون می‌تونیم خرابی یه نمونه رو تحمل کنیم. این طرح اگه دیتابیس‌هامون فقط خواندنی باشن، عالی کار می‌کنه. اما به محض اینکه یه نمونه رو به‌روز می‌کنیم، باید به نحوی بفهمیم که چطور و چه زمانی نمونه دیگه رو به‌روزرسانی کنیم. اینجاست که مسئله‌ی سازگاری نسخه‌های تکرارشده (replica consistency) سروکلش پیدا می‌شه.در واقع، هر وقت وضعیت (state) برای مقیاس‌پذیری و در دسترس بودن تکرار میشه، با سازگاری سروکار خواهیم داشت.

امنيت

امنيت یه موضوع پیچیده و کاملا تکنیکیه، هیچکس نمی‌خواد از یه سیستم ناامن استفاده کنه، و سیستم‌هایی که هک می‌شن و اطلاعات کاربر رو به خطر می‌ندازن، باعث استعفای مدیران ارشد فنی (CTO) و تو موارد حاد، شکست شرکت‌ها می‌شن. اصول اولیه‌ی یه سیستم امن، احراز هویت (authentication)، مجوزدهی (authorization)، و درستی داده‌ها (integrity) هستن. لازمه که مطمئن بشیم داده‌ها تو حین انتقال روی شبکه‌ها قابل رهگیری نیستن، و داده‌های ساکن (persistent store) توسط هرکسی که اجازه دسترسی به اون داده‌ها رو نداره، قابل دسترسی نباشه. اساسا، من نمی‌خوام کسی شماره‌ی کارت اعتباری منو موقعی که بین سیستم‌ها رد و بدل می‌شه یا تو دیتابیس یه شرکت ذخیره می‌شه، ببینه. بنابراین، امنیت یه ویژگی کیفی ضروری برای هر سیستمیه که با اینترنت سروکار داره. از هزینه‌های ساختن سیستم‌های امن نمی‌شه اجتناب کرد، پس بیایید به طور خلاصه بررسی کنیم که چطور این هزینه‌ها روی عملکرد و مقیاس‌پذیری تأثیر می‌ذارن. تو سطح شبکه، سیستم‌ها به طور معمول از پروتکل امنيت لايه‌ی انتقال (TLS) استفاده می‌کنن، که روی TCP/IP اجرا می‌شه . TLS با استفاده از رمزنگاری نامتقارن، احراز هویت و درستی داده‌ها رو فراهم می‌کنه. برای برقراری یه اتصال امن، هزینه‌ی عملکردی وجود داره چون هر دو طرف باید کلید تولید و رد و بدل کنن. برقراری اتصال TLS همچنین شامل رد و بدل گواهینامه‌ها برای تأیید هویت سرور (و به صورت اختیاری کلاینت)، و انتخاب یه الگوریتم برای بررسی اینکه داده‌ها تو حین انتقال دستکاری نشده‌ باشند، می‌شه. بعد از اینکه یه اتصال برقرار شد، داده‌های در حال انتقال با استفاده از رمزنگاری متقارن رمزگذاری می‌شن، که با توجه به اینکه پردازنده‌های مدرن سخت‌افزار اختصاصی رمزنگاری دارن، افت عملکرد ناچیزی داره. برقراری اتصال معمولا نیازمند دو تبادل پیام بین کلاینت و سرور هست و به همین خاطر نسبتا کنده‌ست. تا حد امکان با استفاده‌ی مجدد از اتصالات، می‌شه این سربارهای عملکردی رو به حداقل رسوند.

برای محافظت از داده‌های ساکن ، موتورهای پایگاه داده محبوب مثل SQL Server و Oracle، قابلیت‌هایی مثل رمزنگاری شفاف داده‌ها (TDE) رو دارن که رمزنگاری کارآمد در سطح فایل رو ارائه میدن. مکانیزم‌های رمزنگاری با دانه‌بندی ریزتر، تا سطح فیلد، به طور فزاینده‌ای تو صنایع تحت نظارت مثل امور مالی مورد نیاز هستن. ارائه‌دهندگان ابری هم قابلیت‌های مختلفی رو ارائه میدن تا اطمینان حاصل کنن که داده‌های ذخیره‌شده تو انبارهای داده ابری، امن باشن. سربارهای داده‌های امن در حالت ساکن، هزینه‌هایی هستن که برای دستیابی به امنیت باید متحمل شد - مطالعات نشون میدن که این سربارها تو محدوده‌ی ۵ تا ۱۰ درصد قرار دارن.

یه دیدگاه دیگه روی امنیت، سه‌گانه‌ی CIA هست که مخفف محرمانگی (confidentiality)، درستی داده‌ها (integrity) و در دسترس بودن (availability) هست. دوتا مورد اول همون چیزیه که بالاتر توضیح دادم. در دسترس بودن به توانایی یه سیستم برای عملکرد قابل اعتماد تحت حمله‌ی دشمنان اشاره داره. چنین حملاتی ممکنه تلاش‌هایی برای سوءاستفاده از یه ضعف تو طراحی سیستم برای از کار انداختن سیستم باشن. یه حمله‌ی دیگه، حمله‌ی کلاسیک منع سرویس توزیع‌شده (DDoS) هست، که در اون یه دشمن کنترل انبوهی از سیستم‌ها و دستگاه‌ها رو به دست میاره و سیل عظیمی از درخواست‌ها رو هماهنگ می‌کنه که به طور موثری یه سیستم رو از دسترس خارج می کنه.

به طور کلی، امنیت و مقیاس‌پذیری مخالف هم هستن. امنیت به طور اجباری باعث کاهش عملکرد می‌شه. هر چقدر یه سیستم لایه‌های امنیتی بیشتری داشته باشه، بار بیشتری روی عملکرد و در نتیجه‌ی اون، مقیاس‌پذیری گذاشته می‌شه بنابراین برای رسیدن به الزامات عملکرد و مقیاس‌پذیری یه سیستم، به منابع قدرتمندتر و گرون‌تری نیاز هست.

مدیریت‌پذیری (Manageability)

همراه با توزیع شدن بیشتر و پیچیده‌تر شدن تعاملات سیستم‌هایی که می‌سازیم، مدیریت اون‌ها اهمیت بیشتری پیدا می‌کنه. لازمه که مطمئن بشیم هر کامپوننت همونجوری که انتظار می‌ره داره کار می‌کنه، و عملکرد همچنان داره به انتظارات پاسخ می‌ده.پلتفرم‌ها و تکنولوژی‌هایی که برای ساخت سیستم‌هامون استفاده می‌کنیم، انبوهی از ابزارهای مانیتورینگ مبتنی بر استاندارد رو ارائه می‌دن که می‌شه برای این اهداف ازشون استفاده کرد. داشبوردهای مانیتورینگ رو می‌شه برای بررسی سلامت و رفتار مداوم هر کامپوننت سیستم به کار برد. این داشبوردها که با ابزارهای قابل تنظیم و متن‌باز مثل Grafana ساخته می‌شن، می‌تونن معیارهای سیستم رو نمایش بدن و زمان وقوع اتفاقات یا رسیدن به آستانه‌های مختلف که نیاز به توجه اپراتور دارن، هشدار بدن. اصطلاحی که برای این قابلیت مانیتورینگ پیشرفته به کار می‌ره، قابلیت مشاهده (observability) هست.

سرویس های مختلفی مثل MBeans جاوا، CloudWatch آمازون وب‌سرویس و AppMetrics پایتون وجود داره که مهندسان می‌تونن ازشون برای گرفتن معیارهای سفارشی برای سیستم‌هاشون استفاده کنن ، یه مثال معمولی، زمان پاسخ به درخواست‌هاست. با استفاده از این APIها، داشبوردهای مانیتورینگ می‌تونن به گونه‌ای تنظیم بشن که نمودارها و گراف‌های زنده‌ای رو ارائه بدن که دید عمیقی نسبت به رفتار سیستم داشته باشن. چنین دیدهایی برای اطمینان از عملیات مداوم و برجسته کردن بخش‌هایی از سیستم که ممکنه نیاز به بهینه‌سازی یا تکرار داشته باشن، ارزشمند هستن.

مقیاس‌گذاری یه سیستم به طور قطع به معنای اضافه کردن کامپوننت‌های جدید سیستمی - سخت‌افزاری و نرم‌افزاری - هست. با افزایش تعداد کامپوننت‌ها، بخش‌های در حال اجرای بیشتری برای مانیتورینگ و مدیریت کردن داریم. این کار هیچ‌وقت بدون هزینه نیست. این موضوع پیچیدگی رو به مدیریت عملیات سیستم اضافه می‌کنه و از نظر کدی که برای مانیتورینگ لازمه توسعه داده بشه و تکامل پلتفرم قابلیت مشاهده، به هزینه‌ها اضافه می‌کنه.

تنها راه برای کنترل کردن هزینه‌ها و پیچیدگی‌های مدیریت‌پذیری با مقیاس‌پذیری، اتوماسیونه. این جاییه که دنیای DevOps وارد صحنه می‌شه. DevOps مجموعه‌ای از بهروش هاو ابزارهایی هست که توسعه‌ی نرم‌افزار و عملیات سیستم رو با هم ترکیب می‌کنه. DevOps دوره‌ی توسعه‌ی قابلیت‌های جدید رو کاهش می‌ده و تست مداوم، استقرار، مدیریت، ارتقا و مانیتورینگ سیستم رو به صورت خودکار انجام می‌ده. این بخش جدایی‌ناپذیر از هر سیستم مقیاس‌پذیر موفق هست.