معماری سایت کافه بازار – از هزاران درخواست در روز به هزاران درخواست در ثانیه
مهران اخوان در وبلاگ کافه بازار، تجربیات خود را در چند سال گذشته برای رسیدن به یک معماری مقیاسپذیر برای کافهبازار به عنوان اصلیترین بستر اشتراک برنامههای اندرویدی ایران، بیان کرده است. وب سایت مهندسی داده ضمن تقدیر از این کار ارزشمند که هم باعث انتقال تجربیات به علاقهمندان میشود و هم با مشاهده بازخوردهای کاربران، به غنای کار کمک خواهد کرد، به باز نشر آن در این مقاله اقدام کرده است.
کافه بازار از همان ابتدا ۳۵ میلیون کاربر نداشت و برای رسیدن به این عدد چالشهای فنی گوناگونی را پشت سر گذاشته است. کافه بازار کارش را با دو نفر توسعهدهنده شروع کرد و امروز بیش از صد نفر توسعهدهنده در این شرکت کار میکنند. اوایل راه کم تجربه بودیم و در طول مسیر روز به روز با مسائل جدیدتری آشنا میشدیم. در این مطلب میخواهیم با تمرکز بر چالشهای بزرگ شدن، مرور کوتاهی بر سرگذشت کافه بازار داشته باشیم. در ادامه از مشکلاتی که با آنها مواجه شدهایم و راهحلهایی که برایشان پیدا کردهایم با شما خواهیم گفت.
تولد بازار
اسفند ۸۹، بازار برای اولین بار منتشر شد. برنامه بازار برای اندروید را با جاوا، سمت سرور را با پایتون و چارچوب جنگو (Django) نوشتیم. سرور اصلی کافه بازار یک ماشین مجازی بود. از Nginx به عنوان وب سرور، و برای پایگاه داده از پستگرس (Postgres) استفاده کردیم.
اولین چالش جدی ما زمانی بود که به مرز ۵۰ هزار کاربر رسیدیم و متوجه شدیم که برنامه صفحات را به کندی بارگذاری میکند و کاربرها به تناوب با خطا روبهرو میشوند. پس از بررسی بخشهای مختلف سمت سرور متوجه شدیم که این مشکل ناشی از اختلالات اتصال (Connection) برنامه به پایگاه داده است و به دلیل تعداد زیاد درخواستها، پایگاه داده توان پاسخگویی به همه آنها را ندارد. راه حلی که برای کم کردن تعداد درخواستها به پایگاه داده پیدا کردیم، اضافه کردن لایهی کَش (Cache) با استفاده از Memcached بود. بدین شکل که پاسخ درخواستهای پرتکرار را در حافظه ذخیره میکردیم و در درخواستهای بعدی بدون نیاز به دسترسی به پایگاه داده، همان دادهی کَش شده را پاسخ میدادیم. با این روش سرعت پاسخگویی به مقدار زیادی افزایش پیدا کرد و دیگر خطایی از سمت برنامه نگرفتیم.
کابوس کاربران همزمان
با ادامه رشد تعداد کاربران به مشکل جدیدی برخوردیم. برنامه بازار گاهی به سرعت صفحات را بارگذاری میکرد و گاهی بسیار کند. پس از بررسی لایههای مختلف متوجه شدیم که در هر لحظه فقط یک اتصال بین Nginx و جنگو برقرار میشود و تا زمانی که این اتصال بسته نشود درخواستهای بعدی در صف Nginx منتظر میمانند. یکی از راهحلهای متداول برای رفع این مشکل در پروژههای پایتونی استفاده از یک سرور WSGI، مانند گونیکورن (Gunicorn) است. گونیکورن چندین نمونه از برنامهی جنگو را اجرا میکند و از این پس Nginx به عنوان پراکسی درخواستها را از کاربر گرفته و به گونیکورن ارسال میکند. به این ترتیب توانستیم به تعداد ورکرهای (Worker) گونیکورن، درخواستهای همزمان را پاسخ دهیم.
نیاز به یک سرور قدرتمند
سال ۹۱، در مرز ۱ میلیون کاربر، دیگر ماشین مجازی پاسخگو نبود و اولین سرور اختصاصی را خریدیم و تمام سرویسها را روی آن راهاندازی کردیم.
بهبود معماری نرمافزار
با افزایش امکانات و گسترش خدمات بازار، کدهای پروژه هم بیشتر میشد. در این زمان تعداد توسعهدهندهها به ۱۵ نفر رسیده بود و هماهنگیهای لازم برای حفظ یکپارچگی و تمیزی کد سختتر شده بود. بنابراین تصمیم گرفتیم برای حل این مشکل کدهای مربوط به پرداخت را به عنوان یک پروژه جدید، جدا کنیم که سرویسهای کوچکتری داشته باشیم و هر کس بتواند به صورت مستقل روی سرویس مشخصی کار کند. این پروژه را روی سرور مجزایی راهاندازی کردیم و این نقطهی شروعی بود برای سرورهای بیشتر.
پایان دوران ارتقای سرورها
وقتی تعداد درخواستهای یک سرویس از توان پاسخگویی آن بیشتر شود، دو راه برای تغییر مقیاس وجود دارد
- تغییر مقیاس عمودی (Vertical Scaling)
- تغییر مقیاس افقی (Horizontal Scaling)
در تغییر مقیاس عمودی، افزایش توان پاسخگویی با ارتقای سختافزاری سرورهای موجود انجام میشود. در تغییر مقیاس افقی، با افزودن سرورهای بیشتر به سیستم امکانپذیر میگردد.
ما تا سال ۹۲ برای افزایش توان پاسخگویی، سرور قویتری را جایگزین سرورهای موجود میکردیم یا به اصطلاح از تغییر مقایس عمودی استفاده میکردیم. اما ارتقا دادن سرور تا حدی امکانپذیر است. بالاخره به نقطهای رسیدیم که نیاز به افزایش تعداد سرورها حس میشد و در زمستان ۹۲ تصمیم گرفتیم که سرویس کافهبازار را روی دو سرور راهاندازی کنیم و درخواستها را بین این دو پخش کنیم. برای متعادل کردن بار از DNS استفاده کردیم. اما برای پاسخگویی به تمام این درخواستها هنوز یک نمونه از پایگاه داده وجود داشت که گلوگاه سامانه بود. برای حل این مشکل از قابلیت Replication در پستگرس استفاده کردیم و نسخهای از پایگاه داده را در سرور دوم بالا آوردیم. این قابلیت در پستگرس فقط میتواند به شکل Master-Slave باشد. به این معنی که به غیر از سرور اصلی، بقیه سرورها قابلیت نوشتن ندارند. خوشبختانه بیشتر درخواستهای ما هم نیاز به نوشتن در دیتابیس نداشتند. بنابراین سرور دوم را فقط به منظور خواندن از پایگاه داده در نظر گرفتیم.
نمونهای دیگر از تغییر مقیاس افقی که در سال ۹۲ انجام دادیم، پروژهی تغییر نحوهی محاسبهی تعداد نصبهای فعال برنامهها بود. ما تا قبل از این پروژه، برای محاسبهی این عدد، بخشی از کاربران را به صورت تصادفی انتخاب میکردیم و برنامههای نصب شدهی آنها را ساعت به ساعت برای بکاند (backend) میفرستادیم و بر این اساس میزان نصب هر برنامه را تخمین میزدیم. مشخصاً این روش دارای دقت بسیار پایینی بود، در نتیجه تصمیم گرفتیم این محاسبه را برای تمام کاربران انجام دهیم. چالشی که برای رسیدن به این هدف داشتیم تعداد بسیار زیاد کاربران بود و با توجه به ارسال ساعت به ساعت این اطلاعات به قدرت محاسباتی بالایی نیاز داشتیم. برای حل این مشکل پروژهی جدیدی به نام «چرتکه» را شروع کردیم که روی چند سرور به طور همزمان اجرا میشد و هر کدام از سرورها بخشی از این محاسبات را به عهده میگرفتند. در پایان هر روز یکی از این سرورها به عنوان سرور هماهنگکننده انتخاب میشد و اطلاعات لازم را از سایر سرورها میگرفت و نتیجهی تجمیع شده را به کافه بازار میفرستاد. ده سرور تا سال ۹۴ به این پروژه خدمترسانی کردند.
به سوی مایکروسرویسها
ما در کافه بازار به استقلال تیمها اهمیت زیادی میدهیم، تا هر تیمی بتواند با سرعتی بالا به سوی اهدافش حرکت کند. اما وجود فقط تعداد کمی پروژهی بزرگ باعث وابستگی تیمهای مختلف به یکدیگر میشد و نیاز به هماهنگیهای غیر ضروری افراد و تیمها را در پی داشت. با افزایش تعداد توسعهدهندهها و بزرگ شدن تیمها، این وابستگی به یک مشکل بزرگ تبدیل شد که باعث کاهش سرعت خروجی تیمها میشد. همچنین با بزرگتر شدن سرویسها مقیاسپذیری سختتر و سختتر میشد. برای حل این مشکلات تصمیم گرفتیم معماری کلی کافه بازار را به سمت مایکروسرویسها ببریم و در ادامه هر سرویس بزرگ را به چندین سرویس کوچک شکستیم. این معماری جدید باعث شد که هر تیمی بتواند مسئولیت تعدادی از مایکروسرویسها را به عهده بگیرد و بدون وابستگی به دیگر تیمها روی آنها کار کند. روند شکستن کافه بازار از زمستان ۹۴ شروع شد و هماکنون بیش از ۵۰ مایکروسرویس در حال سرویسدهی هستند.
بر فراز ابرها
از دیگر چالشهای ما، وظیفهی نگهداری از سرورها بود که در همهی سطوح (از سختافزار تا سیستم عامل و تمام سرویسهای آن) به عهده تیم استفادهکننده از آن بود. در حال حاضر بیش از ۵۰ سرور برای محصول کافه بازار استفاده میشوند. این مسئله هزینهی زمانی زیادی برای تیمها داشت و همچنین در مواقع اضطراری امکان تغییر مقیاس سریع سرویس وجود نداشت. علاوه بر این، امکان استفاده از منابع سرورها به صورت مشترک برای سرویسها وجود نداشت و هر سرویس فقط از منابع یک سرور میتوانست استفاده کند. در نتیجه استفادهی بهینه از منابع سرورها امکانپذیر نبود.
تیمی به نام زیرساخت با وظیفهی یافتن راه حلی مناسب برای این مشکلات شکل گرفت. این تیم پس از بررسی راه حلهای متفاوت، کوبرنتیز (Kubernetes) را به عنوان راهحل نهایی انتخاب کرد. با استفاده از کوبرنتیز میتوانیم تغییر مقیاس هر مایکروسرویس را به طور جداگانه و خودکار انجام دهیم و از منابع سختافزاری سرورها به صورت بهینه استفاده کنیم. در حال حاضر، قسمت عمدهای از سرویسهای کافه بازار به کوبرنتیز مهاجرت کردهاند.
ما در کافه بازار هنوز راه طولانی در پیش داریم و روز به روز با افزایش تعداد کاربران و قابلیتهای بیشتر، با چالشهای جدیدتری روبهرو میشویم. برای همراهی ما در این راه به ما بپیوندید
عالی بود استفاده کردیم- ما هم مدتی است که مهاجرت به کوبرنیتیز را شروع کردیم.