مقیاس پذیری افقی (Horizontal Scaling) در اپلیکیشن های PHP – راهنمای پیشرفته


ارائه یک وبسایت یا اپلیکیشن در محیط production چالشهای خاص خود را دارد؛ اما وقتی در مسیر صحیحی صورت بگیرد، موفقیت بزرگی محسوب میشود. مشاهده این که تعداد بازدیدکنندهها بالا میرود، همواره حس خوبی ایجاد میکند. البته مواردی وجود دارند که ترافیک چنان افزایش مییابد که باعث از کار افتادن استک LAMP شما میشود. مهم نیست که این اتفاق چه ساعت یا چه روزی رخ بدهد، واقعیت این است که آفلاین شدن وبسایت یا اپلیکیشن هزینه بسیار بالایی دارد و در برخی موارد زیانهای جبرانناپذیری برای یک کسب و کار ایجاد میکند.
اما جای ترس نیست، چون روشهایی وجود دارند که بتوانیم اپلیکیشنهای PHP را پایدارتر و قابلاطمینانتر سازیم. اگر واژه مقیاسپذیری در ذهن شما چرخ میخورد، باید بگوییم که ایده درستی در ذهن خود دارید.
مقیاسپذیری چیست؟
مقیاسپذیری به طور خلاصه به توانایی یک سیستم برای مدیریت افزایش ترافیک یا رشد مراجعه کاربران، همزمان با حفظ کیفیت تجربه کاربری مطلوب گفته میشود. اساساً دو نوع مقیاسپذیری برای یک سیستم متصور است: مقیاسپذیری عمودی که مقیاسپذیری رو به بالا (scaling up) نیز نامیده میشود و مقیاسپذیری افقی که مقیاسپذیری رو به بیرون (scaling out) نامیده میشود.
مقیاسپذیری عمودی از طریق افزایش منابع سیستم مانند افزودن حافظه یا توان پردازشی بیشتر حاصل میآید. برای مثال تغییر نوع یک VPS نمونهای از مقیاسپذیری عمودی است. با این که این روش یک راهحل بیدرنگ محسوب میشود؛ اما باعث میشود که مشکلات واقعی که در اپلیکیشن شما وجود دارند پنهان شوند. تضمینی وجود ندارد که یک سرور با اندازه دو برابر، باعث شود که اپلیکیشن شما نیز دو برابر سریعتر عمل کند.
از سوی دیگر مقیاسپذیری افقی با افزودن سرورهای بیشتر به یک کلاستر موجود صورت میپذیرد. در ادامه معنی این روش را دقیقتر توضیح میدهیم.
مقیاسپذیری افقی چیست؟
یک کلاستر به گروهی از سرورها گفته میشود. یک سرور توزیع بار (load balancer) بار کاری را بین سرورهای موجود در کلاستر توزیع میکند. هر زمان یک وبسرور جدید میتواند به یک کلاستر موجود اضافه شود تا درخواستهای بیشتری که از سوی کاربران به سمت اپلیکیشن شما میآید را مدیریت کند، به این کار مقیاسپذیری افقی گفته میشود.
نمونهای از مقیاسپذیری افقی در نمودار زیر ارائه شده است:
سرور توزیع بار تنها یک مسئولیت دارد: تصمیمگیری در مورد این که کدام سرور در کلاستر، یک درخواست که پذیرفته شده است را دریافت خواهد کرد. این سرور اساساً مانند یک پراکسی معکوس (reverse proxy) عمل میکند و باعث میشود کل فرایند در نظر کاربر یکپارچه به نظر بیاید.
با این که مقیاسپذیری افقی معمولاً روشی پایدارتر و کارآمدتر برای مقیاسپذیری محسوب میشود اما به اندازه روش مقیاسپذیری عمودی ساده نیست.
چالشهای مقیاسپذیری افقی
به طور خلاصه، چالشهای عمده مقیاسپذیری اپلیکیشنهای وب این است که باید همه گرهها در یک کلاستر بهروز و همگام حفظ شوند. سناریوی زیر را در نظر بگیرید:
زمانی که کاربر A یک درخواست به mydomain.com ارسال میکند، سرور توزیع بار درخواستها را به سرور 1 ارسال میکند. از سوی دیگر درخواست کاربر B به سرور دیگری به نام سرور 2 در کلاستر ارسال میشود.
حال، سؤال این است که اگر کاربر A تغییری در اپلیکیشن ایجاد کند، مثلاً فایلی آپلود کند یا بخشی از محتوای اپلیکیشن را تغییر دهد، چه اتفاقی در پایگاه داده میافتد؟ چگونه میتوان انسجام محتوا را در میان همه گرههای کلاستر حفظ کرد؟ به علاوه PHP اطلاعات نشست (session) را به طور پیش فرض روی دیسک ذخیره میکند. اگر کاربر A وارد اپلیکیشن شود، با توجه به این که سرور توزیع بار ممکن است هر بار درخواست این کاربر را به سرور دیگری ارسال کند، چگونه میتوان کاربر را در مراجعات بعدی به خاطر نگه داشت؟
در ادامه روشهای فائق آمدن بر این مشکلات و آمادهسازی اپلیکیشن PHP موجود برای مقیاسپذیری افقی را توضیح میدهیم.
تجزیه، تجزیه، تجزیه
آمده سازی یک سیستم برای مقیاسپذیری شامل مقدار زیادی تجزیه است. زیرا در مقیاسپذیری ضروری است که تعداد زیادی سرورهای کوچک با مسئولیتهای محدود به جای یک سرو غولپیکر همهکاره داشته باشیم. این وضعیت، جوهره مقیاسپذیری افقی را تشکیل میدهد. تجزیه اپلیکیشن به بخشهای مختلف باعث میشود بتوانید تنگناهای واقعی که در اپلیکیشن خود دارید را شناسایی و ترمیم کنید.
یک اپلیکیشن PHP را در نظر بگیرید که کاربران میتوانند وارد آن شده و عکسهایی را آپلود کنند. این اپلیکیشن از استک پایه LAMP استفاده میکند و عکسها روی دیسک ذخیره شده و آدرس آنها در پایگاه داده ذخیره میشود. در این سناریو چالش این است که بین چند سرور اپلیکیشن که دادههای یکسانی را به اشتراک گذاردهاند یکپارچگی و انسجام حفظ شود.
برای این که این اپلیکیشن نمونهای را مقیاسپذیر بکنیم، باید وبسرور را از پایگاه داده جدا کنیم. بدین ترتیب میتوانیم چند گره اپلیکیشن داشته باشیم که سرور پایگاه داده مشترکی دارند. این گام نخست است و با کاهش بار وبسرور، باعث بهبود اندکی در عملکرد اپلیکیشن میشود.
برای ایجاد مقیاسپذیری بیشتر باید اقدام به پیادهسازی یک محیط توزیع بار برای پایگاه داده نیز بکنیم. در مقاله «کلاستر چند گرهای MySQL روی اوبونتو ۱۸.۰۴» به روش پیادهسازی چنین کلاستری پرداختهایم.
حفظ یکپارچگی نشستهای کاربران
زمانی که اپلیکیشن از سرور پایگاه داده جدا شد، میتوانیم روی مشکلات خاصی که در پیادهسازی PHP وجود دارد، متمرکز شویم. ابتدا باید یک روش برای مدیریت نشستهای کاربران در میان گرههای مختلف پیدا کنیم. در ادامه چند رویکرد مختلف برای این منظور بررسی شدهاند.
پایگاههای داده رابطهای و Filesystem های شبکهای
افراد زیادی از این رویکرد برای ذخیرهسازی دادههای نشست کاربران در پایگاههای رابطهای مانند MySQL استفاده میکنند، زیرا پیادهسازی آن نسبتاً آسان است. با این حال، این راهحل مطلوبیت زیادی ندارد، زیرا سربار زیادی اضافه میکند. این سربار ناشی از این واقعیت است که برای هر درخواست باید عملیات خواندن و نوشتن روی پایگاه داده صورت بگیرد و در موارد ترافیک بالا، پایگاه داده معمولاً نخستین بخشی است که از کار میافتد.
به طور مشابه استفاده از filesystem شبکهای نیز یک راهحل است که پیادهسازی آسانی دارد، چون نیازمند ایجاد تغییراتی در کد برنامه نیست. اما filesystem شبکهای به دلیل عملیات خواندن/نوشتن کُند است. چون در این مورد نیز برای هر درخواست باید یک بار عملیات خواندن یا نوشتن صورت بگیرد و این مسئله تأثیری منفی روی عملکرد اپلیکیشن دارد.
نشستهای چسبنده (Sticky Sessions)
نشستهای چسبنده در سرور توزیع بار مدیریت میشوند و نیازمند ایجاد هیچ تغییری در گرههای اپلیکیشن نیستند و از این رو سادهترین روش برای مدیریت نشستهای کاربران محسوب میشوند. بدین ترتیب سرور توزیع بار کاربرانی که وارد حساب کاربری خود در اپلیکیشن شدهاند را همواره به سرور یکسانی که اطلاعات نشست در آن ذخیره شده است انتقال میدهد و بدین ترتیب نیازی به اشتراک اطلاعات نشست در میان گرههای مختلف وجود ندارد.
با این حال این راهحل نیز مشکلات جدیدی ایجاد میکند. در این حالت، سرور توزیع بار مسئولیتهای بیشتری دارد و میتواند روی عملکرد آن تأثیر منفی بگذارد و آن را به یک نقطه شکست جدید تبدیل کند. این رویکرد همچنین باعث میشود که درون کلاستر نقاط با توزیع بار مختلف ایجاد شوند، چون کاربرانی که به صورت مجدد مراجعه میکنند همواره از سرور قبلی خود استفاده میکنند و ممکن است گرههای جدیدی که به شبکه اضافه شدهاند بدون کاربر بمانند.
استفاده از سرور Memcached یا Redis
این راهحل نیازمند راهاندازی یک یا چند سرور اضافی برای مدیریت نشستهای کاربران است؛ اما پایدارترین روش برای حل کردن مشکلات نشستهای کاربران محسوب میشود. هم Memcached و هم Redis موتورهای ذخیرهسازی کلید-مقدار بسیار سریعی هستند که مدیریت نشستهای کاربران در PHP را بر عهده میگیرند. به طور خلاصه پس از راهاندازی سرور Memcached یا Redis، باید هر گره را طوری پیکربندی کنید که بتواند به سرور Memcached یا Redis وصل شود و از آن به عنوان یک مدیر نشست استفاده کند. این امر نیازمند نصب یک افزونه PHP و ایجاد تغییراتی ساده در تنظیمات php.ini است.
اطلاعات بیشتر در مورد راهاندازی سرور نشست Memcached برای PHP را میتوانید در مستندات رسمی PHP ملاحظه کنید. در مورد ردیس به راهنمایی که در این لینک ارائه شده است، مراجعه کنید.
انسجام فایل کاربر
تا به این جا ما سرورهای اپلیکیشن و پایگاه داده را از هم جدا کردهایم و به حل مسئله یکپارچگی نشستهای کاربران پرداختیم. اما همچنان نیاز داریم راهحلی برای حفظ یکپارچگی بین فایلهای آپلود شده از سوی کاربران بیابیم، زیرا این فایلها ممکن است در گرههای اپلیکیشن مختلف ذخیره شده باشند.
متدهای مختلفی برای حل این مسئله وجود دارند. در برخی روشها از حالتی شبیه به روش ایجاد یکپارچگی بین نشستهای کاربران استفاده میشود؛ اما خوشبختانه در این مورد پیادهسازی چنین رویکردی آسانتر است. فایلها در هر درخواست از دیسک خوانده یا نوشته نمیشوند و از این رو اشتراک فایل به منابع زیادی نیاز ندارد. یک راهحل مانند GlusterFS در این مورد میتواند کاملاً عملی باشد. در این رویکرد یک درایو ذخیرهسازی مشترک ایجاد میشود که هر محتوایی که در یک گره ایجاد شود در همه گرههای دیگر کلاستر کپی میشود.
راهحل رایج دیگر استفاده از رویکرد object storage برای ذخیرهسازی فایلها است. این رویکرد را میتوان با استفاده از متدهای مختلفی پیادهسازی کرد که از ذخیرهسازی blob ساده پایگاه داده تا سرویسهای ابری مانند AWS S3 و Google Cloud Storage متفاوت هستند. با این حال این رویکرد بسته به شیوه پیادهسازی اپلیکیشن، نیازمند ایجاد تغییرات زیادی در کد اپلیکیشن است.
توزیع بار (Load Balancing)
زمانی که اپلیکیشن به طرز صحیحی تجزیه شد در نهایت زمان آن میرسد که گرههای کپی (replica) ایجاد کنیم که کلاستر اپلیکیشن را تشکیل میدهند. اپلیکیشن نمونه ما تنظیمات زیر را دارد:
هم سرور App01 و هم سرور App02 قابل دسترس هستند و میتوانند درخواستها را به روشی دقیقاً یکسان مدیریت کنند. تنها تفاوت این است که یک سرور توزیع بار راهاندازی شده است که به عنوان نقطه ورود اپلیکیشن عمل میکند. و کاربران را به گرههای مختلفی در کلاستر ارجاع میدهد.
HAProxy که اختصاری برای عبارت «پراکسی با موجودیت بالا» (High Availability Proxy) است یک گزینه متن-باز استاندارد برای توزیع بار محسوب میشود. این وبسرور از سوی اپلیکیشنهایی مانند توییتر، اینستاگرام، و Imgur مورد استفاده قرار میگیرد.
ملاحظات دیگر
آمادهسازی یک اپلیکیشن برای مقیاسپذیری افقی ممکن است در ابتدا ترسناک به نظر برسد؛ اما زمانی که با طرز کار سرور توزیع بار آشنا شدید راحتتر میتوانید مراحل آمادهسازی یک محیط قابل مقیاسپذیری را طی کنید.
به طور طبیعی زمانی که از صفر شروع به ساخت یک اپلیکیشن میکنید ایجاد خصوصیت مقیاسپذیری در آن آسانتر است؛ اما همواره این امکان مهیا نیست. همچنین لازم به ذکر است که مقیاسپذیری دوشادوش نگهداری اپلیکیشن حرکت میکند؛ اما این بدان معنی نیست که اینها به یک معنی هستند و همچنین لازم نیست که همه اپلیکیشنها مقیاسپذیر باشند. از سوی دیگر سرعت چیزی است که همه اپلیکیشنها از منافع آن بهرهمند میشوند.
اگر این مطلب برای شما مفید بوده است، آموزشهای زیر نیز به شما پیشنهاد میشوند:
- برنامهنویسی PHP و هر آنچه برای شروع باید بدانید
- مجموعه آموزشهای رایگان PHP
- مجموعه آموزشهای ابزارها و راهکارهای مدیریت وبسایتها
- گنجینه برنامه نویسی PHP
- مجموعه آموزشهای طراحی و برنامه نویسی وب
- آموزش فریمورک لاراول PHP Laravel برای ساخت فروشگاه اینترنتی
==
به مولا که ایول ،
من یه جا راجع به مقیاس پذیری زبان golang خوندم نفهمیدم چی میگفت ، ولی ایده ی کلی مقیاس پذیری رو فهمیدم ، سپاس فراوان