برنامه نویسی با ملاحظات مقیاس پذیری

اصول برنامه‌سازی مقیاس‌پذیر دانشکده مهندسی کامپیوتر

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

تست‌های واحد (Unit Tests) برای اطمینان از عملکرد صحیح هر کامپوننت بسیار مهم هستند. هر کامپوننت باید دارای مستندات دقیقی باشد که نحوه استفاده، ورودی‌ها، خروجی‌ها و رفتارهای مورد انتظار را توضیح دهد. این مستندات به توسعه‌دهندگان دیگر کمک می‌کند تا به راحتی از کامپوننت‌ها استفاده کنند. توسعه‌دهندگان می‌توانند کتابخانه‌هایی از کامپوننت‌های عمومی و پرکاربرد ایجاد کنند که در پروژه‌های مختلف استفاده شوند. راه‌حل‌های رایج معمولاً شامل راه‌اندازی سرور، استفاده از فریمورک‌هایی مانند Flask یا FastAPI، و استقرار روی سرویس‌هایی مانند AWS Lambda یا Google Cloud Functions است.

در مقابل، روش‌های سنتی مانند استفاده از Flask/FastAPI و هاست‌های شخصی به دانش فنی بالا، مدیریت سرور و هزینه‌های نگهداری نیاز دارند. سرویس‌های بدون سرور (Serverless) مانند AWS Lambda و Google Cloud Functions این امکان را فراهم می‌کنند که کد پایتون خود را به عنوان API مستقر کنید، بدون نیاز به سرور دائمی. به این صورت، هر کلاس نقش خاص خود را دارد و به این ترتیب کدها به بخش‌های کوچکتر و قابل فهم تقسیم می‌شوند. این اصل، وابستگی ضعیف را تشویق می‌کند و قابلیت تست‌پذیری و نگهداری کد شما را بهبود می‌بخشد. زیرکلاس‌ها باید رفتار کلاس پایه را گسترش یا تخصصی کنند، نه اینکه از هدف اصلی آن منحرف شوند. تصور کنید یک ویجت پایه به نام MedicalHistoryTile داریم که یک ورودی سابقه پزشکی بیمار را با جزئیات کلی مانند تاریخ و عنوان نمایش می‌دهد.

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

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

مقیاس پذیری افقی یا Scale Out کردن روشی است که در آن به جای افزایش منابع بیشتر مثل Ram، تعداد گره ها (Nodes یا همان کامپیوترهای موجود) را افزایش می دهیم. برنامه‌نویسی رویه‌ای یک مدل برنامه‌نویسی است که در آن عملیات‌ها به‌صورت توالی‌وار و خطی انجام می‌شوند. در این رویکرد، کد به‌طور معمول از مجموعه‌ای از توابع یا رویه‌ها (procedures) که به ترتیب اجرا می‌شوند تشکیل شده است. به این معنی که در یک برنامه رویه‌ای، ابتدا داده‌ها تعریف می‌شوند، سپس عملیات یا توابع برای پردازش این داده‌ها در کد اعمال می‌شود. با Auto-Scaling سیستم این اجازه را دارد که به‌طور خودکار تعداد سرورهای فعال را بر اساس بار فعلی تنظیم کند.

از این طریق، قابلیت‌های کلاس والد به‌راحتی در کلاس‌های فرزند به ارث می‌رسد و این کار موجب کاهش تکرار کد و افزایش کارایی می‌شود. برنامه‌نویسی انعطاف‌پذیر به نوشتن کدی اطلاق می‌شود که به‌راحتی قابل تغییر، گسترش و نگهداری باشد. در این نوع برنامه‌نویسی، کدهای نوشته‌شده قابلیت انطباق با تغییرات آینده را دارند و معمولاً از پیچیدگی‌های زیاد و وابستگی‌های تنگاتنگ اجتناب می‌شود. انعطاف‌پذیری در برنامه‌نویسی به‌طور کلی به معنای قابلیت سیستم برای پذیرش ویژگی‌های جدید، تغییرات یا رفع اشکال‌ها بدون نیاز به تغییرات عمده در بخش‌های دیگر سیستم است. برنامه نویسی کامپوننت محور (Component-Based Programming یا CBP) یک پارادایم برنامه نویسی است که بر اساس استفاده از کامپوننت‌های مستقل و قابل استفاده مجدد برای ساخت نرم افزار ساخته شده است.

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

برای افزایش قدرت پردازش، تصمیم می گیرید یک کامپیوتر جدید با یک CPU 8 هسته ای 3.4 گیگاهرتزی بخرید تا جایگزین کامپیوتر قدیمی خودتان کنید. با استفاده از این الگوها، سیستم تو مقاوم‌تر، انعطاف‌پذیرتر و خودترمیم (Self-Healing)تر میشه و در صورت بروز هر خطا، بدون نیاز به دخالت زیاد، خودش به حالت پایدار برمی‌گرده.

اصل جایگزینی لیسکوف (Liskov Substitution Principle یا LSP) یکی دیگر از اصول پایه‌ای SOLID است. این ویجت از طریق لیست تکرار می‌شود و از متد buildDetailWidget هر شیء جزئیات برای ساخت رابط کاربری استفاده می‌کند. حالا ویجت PatientDetails می‌تونه لیستی از اشیاء PatientDetail رو بگیره و متد buildDetailWidget هر کدوم رو فراخوانی کنه تا جزئیات مربوطه نمایش داده بشه. یه کلاس دیگه به اسم MedicationHistoryDetail ایجاد می‌کنید که اون هم از PatientDetail ارث می‌بره و سابقه دارویی رو نمایش میده. ما همواره تلاش کرده­‌ایم که دروس را به طور کامل ضبط نماییم و در اختیار شما دوستان قرار دهیم. اما گاهی برخی ناهماهنگی ها سبب می شود که یک یا تعدادی از جلسات یک درس ضبط نشود.

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

به عنوان مثال، فرض کنید کدی دارید که همزمان هم اطلاعات را از دیتابیس دریافت می‌کند، هم پردازش‌های پیچیده را انجام می‌دهد و هم آن‌ها را در رابط کاربری به نمایش می‌گذارد. ارث یکی از قدرتمندترین ویژگی های OOP است و به توسعه دهندگان این امکان را می دهد تا کلاسهای جدیدی را ایجاد کنند که عملکردهای موجود را به ارث می برند. این دوره روش اجرای وراثت در پایتون ، ایجاد کلاسهای پایه و مشتق شده و روشهای نادیده گرفتن برای گسترش یا اصلاح عملکرد موجود را نشان می دهد. بدون یادگیری دیزاین پترن‌ها احتمالا کدهایی که می‌نویسید پیچیده و غیر بهینه هستند و روی کیفیت و کارایی پروژه‌های شما تاثیر منفی خواهند داشت. شرکت نکردن در دوره آموزش Design Patterns سبزلرن که برای برنامه‌نویسان طراحی شده باعث می‌شود تا مجبور شوید زمان بیشتری برای حل مشکلات کدنویسی هدر دهید! تبدیل شدن به یک برنامه‌نویس حرفه‌ای بدون یادگیری دیزاین پترن‌ها در این دوره تقریبا غیر ممکن است.

برای مقابله با این چالش، از ابزارهایی مانند قابلیت مشاهده‌پذیری (Observability) و ردیابی (Tracing) استفاده می‌کنیم که به ما اجازه می‌دهند وضعیت داخلی نرم‌افزار را بهتر درک کنیم. برای افزایش کارایی نرم‌افزار، استارتاپ‌ها می‌توانند از ترکیب پایگاه‌های داده NoSQL و پایگاه‌های داده سنتی (Relational Databases) استفاده کنند. این رویکرد ترکیبی به آن‌ها اجازه می‌دهد تا بخش‌های مختلف داده را به بهترین شکل ممکن مدیریت کنند. این استراتژی، کارایی نرم‌افزار را بهبود داده و امکان مقیاس‌پذیری آسان‌تر را فراهم می‌کند. آیا می‌دانید محصولات نرم‌افزاری چگونه با رشد استارتاپ‌ها گسترش می‌یابند؟ درک مفهوم مقیاس‌پذیری (Scaling) برای هر استارتاپی حیاتی است.

مقیاس‌پذیری به این معناست که پروژه به راحتی و با کیفیت مطلوب قابل افزایش حجم کار و تعداد کاربران می‌باشد. توی این سری مقالات، قراره به مفاهیم و اصولی بپردازیم که اگه به درستی پیاده‌سازی بشن، بهت کمک می‌کنن تا یک سیستم مقیاس‌پذیر (Scalable)، پایدار (Stable) و قابل اعتماد (Reliable) بسازی. وقتی در مورد طراحی سیستم‌های پیچیده صحبت می‌کنیم، مخصوصاً سیستم‌های توزیع‌شده (Distributed Systems)، نکته اصلی اینه که بدونیم سیستممون قراره در دنیای واقعی با چه چالش‌هایی روبرو بشه. این چالش‌ها می‌تونن از ترافیک‌های سنگین و ناگهانی گرفته تا خرابی‌های سخت‌افزاری، مشکلات شبکه، یا حتی قطع شدن کل دیتاسنتر باشن. اجزای یک برنامه میکروسرویس می‌توانند به‌صورت مستقل مقیاس‌پذیری شوند، بدون اینکه کل نرم‌افزار تحت فشار قرار گیرد.

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

استفاده از زیرساخت‌های بک‌اند موبایل به‌عنوان سرویس (MBaaS) مدیریت زیرساخت اپلیکیشن را آسان‌تر می‌کند. هر تابع یا متد باید حاوی یک وظیفه خاص باشد و تلاش کند که هیچگونه وابستگی غیرضروری به سایر بخش‌های سیستم نداشته باشد. توابع یا متدهایی که هرکدام تنها یک کار خاص انجام دهند، از نظر اصول مدولاریت ایده‌آل هستند. AllergyHistoryTile از MedicalHistoryTile ارث می‌برد و یک ویژگی allergen (ماده آلرژی‌زا) را اضافه می‌کند. از متد build کلاس پایه برای ساختار اصلی استفاده مجدد می‌کند و یک متد جدید getTrailingWidget را برای نمایش اطلاعات خاص آلرژی (رنگ قرمز برای تأکید) معرفی می‌کند.

کلاس‌های مشخصی مانند NetworkPatientRepository و LocalPatientRepository ایجاد کنید که PatientDataProvider را پیاده‌سازی می‌کنند. به عنوان مثال، ویجتی که اطلاعات بیمار را نمایش می‌دهد، فقط به PatientInfoService وابسته خواهد بود و نیازی به وابستگی به رابط‌های دیگر مانند AppointmentService یا BillingService نخواهد داشت. این اصل، ایجاد رابط‌های کوچکتر و خاص‌تر را به جای رابط‌های بزرگ و چندمنظوره ترویج می‌کند. در یک برنامه واقعی، ممکن است از یک راه‌حل مدیریت state (مانند Provider یا BLoC) برای مدیریت داده‌های بیمار و به‌روزرسانی پویای لیست جزئیات نمایش‌داده‌شده استفاده کنید. یه کلاس پایه انتزاعی به اسم PatientDetail با یه متد buildDetailWidget ایجاد می‌کنید که مسئول نمایش یه جزئیات خاصه. مثلاً یک کلاس برای مدیریت اطلاعات بیمار، یک کلاس برای انجام فرآیندهای پزشکی، یک کلاس برای تولید صورتحساب و یک کلاس برای مدیریت قرارهای ملاقات.

به عبارت دیگر، هر مدول تنها اطلاعات لازم برای تعامل با سایر قسمت‌ها را ارائه می‌دهد. این روش باعث می‌شود که اگر پیاده‌سازی داخلی یک بخش تغییر کند، تاثیری روی سایر قسمت‌ها نگذارد. این کلاس پایه ساختار اصلی یک tile سابقه پزشکی را با عنوان و تاریخ تعریف می‌کند. انتزاعی است زیرا متد build را به طور کامل پیاده‌سازی نمی‌کند و به زیرکلاس‌ها اجازه می‌دهد محتوا را سفارشی کنند. یکی از الگوهای طراحی اصول SOLID میباشد که در این مقاله به پیاده سازی آنها در فریمورک فلاتر و زبان برنامه نویسی دارت میپردازیم.

این امر باعث می‌شود تا برنامه‌نویسان بتوانند با استفاده از یک واسط یکسان (interface)، رفتارهای متفاوتی را پیاده‌سازی کنند و همچنین تعاملات پیچیده بین اجزاء سیستم را ساده‌تر کنند. در ادامه به بررسی سوالات متداول شما درباره کامپوننت در برنامه نویسی می‌پردازیم. از ابزارهای پروفایلینگ استفاده کنید تا بخش‌های کد که ممکن است باعث کاهش عملکرد شوند را شناسایی و بهینه کنید. توضیحات کامل در مورد تست‌هایی که برای کامپوننت‌ها نوشته شده‌اند، باعث می‌شود توسعه‌دهندگان بتوانند به راحتی کامپوننت‌ها را بررسی و تست کنند. این امکان باعث می‌شود که شناسایی و رفع اشکالات سریع‌تر انجام شود و زمان کلی تست کاهش یابد. اگرچه استفاده از کامپوننت در برنامه نویسی مزایای بسیاری دارد، اما معایب و چالش‌هایی نیز وجود دارد که باید به آنها توجه کرد.

سیستمی که عملکرد آن پس از افزودن سخت افزار، متناسب با ظرفیت اضافه شده، بهبود می یابد، سیستمی مقیاس پذیر گفته می شود. برای مثال، در سیستم پردازش پرداخت‌ها در یک فروشگاه آنلاین بزرگ، از اینترفیس‌ها می‌توانیم برای پردازش پرداخت از روش‌های مختلف استفاده کنیم. نحوه واکنش به هر نوع خطا، بستگی به نیازمندی‌های دسترسی‌پذیری (Availability) اپلیکیشنت داره. مثلاً اگه به دسترسی‌پذیری بالا نیاز داری، می‌تونی از چندین Availability Zone استفاده کنی یا برای سناریوهای بحرانی، سوییچ اتوماتیک به Region پشتیبان (Fail-over) داشته باشی. این کار از قطع شدن سرویس در بدترین شرایط هم جلوگیری می‌کنه، ولی به هزینه و پیچیدگی سیستم اضافه می‌کنه.

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


برنامه نویسی گو