۰
سلام! خیلی وقته ننوشتم، اما میدونید چرا؟ چون دارم با لذت کار میکنم و تجربه میسازم که با شما به اشتراک بذارم.
چندی پیش با راج، دوست عزیزم ملاقات کردم. اون داشت به دنبال راهکارهایی برای مقیاسپذیری اپلیکیشن موبایلش بود که پشتش Django بود و میخواست این برنامه رو برای میلیونها کاربر توسعه بده. چند نکتهای بود که من و راج دربارهش صحبت کردیم، بذارید بهتون بگم.
گرچه پایتون روی C ساخته شده، اما همیشه سریعترین زبان برنامهنویسی نیست. علاوه بر این، مشکل GIL (Global Interpreter Lock) در پایتون باعث میشه مقیاسپذیری روی سیستمهای چند هستهای سختتر بشه.
اما پایتون به خاطر سادگی، جامعه بزرگ و علاقه مردم بهش محبوبه. فریمورک Django هم که تو شرکتهایی مثل Disqus، Pinterest و Instagram استفاده میشه؛ البته اونا تغییراتی هم توی فریمورک دادن ولی این موضوع به ما اطمینان میده که اگه کارمون رو ساده و منطقی نگه داریم، میتونیم چنین مقیاسپذیریهایی رو خواهیم داشت.
وقتی از مقیاسپذیری صحبت میکنیم، فقط منظور زمان اجرای کده نیست؛ بلکه باید زیرساختی که برنامه روی اون اجرا میشه هم در نظر گرفته بشه.
بریم سر اصل مطلب...
احتمالاً با اصطلاحات Vertical Scaling و Horizontal Scaling آشنا هستید.
یکی از روشهای مقیاسپذیری افقی، استفاده از الگوی معماری مثل میکروسرویسها microservices هست. تقسیم برنامه به سرویسهای کوچکتر (معروف به SOA) به شما کمک میکنه هر بخش رو به صورت مستقل مقیاس بدید و بهینهسازیهای خاص هر سرویس رو انجام بدید. حتی میتونید چند سرویس بنویسید با Django و به این صورت اپلیکیشن رو افقی توسعه بدید.
نکته: هزینههای ارتباط بین سرویسها، انتقال داده و مدیریت پیچیدگیها رو هم در نظر داشته باشید.
برای مقیاسپذیری افقی، اپلیکیشن باید stateless باشه؛ یعنی حالت (state) بیرون از اپ ذخیره شه. توی Django این کار با استفاده از Cache Backends مثل Memcache یا Redis انجام میشه که داده رو روی یک سرور جدا نگهداری میکنن تا اپ حالت رو داخل خودش نداشته باشه. اضافه کردن کش به Django میتونه بهتون کمک کنه تا بتونید ۴۵ هزار درخواست در ثانیه رو مدیریت کنید.
اگر زیاد با Django کار نکردید، ممکنه پارامتر CONN_MAX_AGE توی فایل settings.py رو از قلم بندازید که مربوط به زمان حداکثر اتصال به دیتابیس هست و همانند connection pooling عمل میکنه.
به طور پیشفرض، Django پس از هر درخواست اتصال به دیتابیس رو میبنده اما با اتصالات پایدار، از بار اضافه روی دیتابیس جلوگیری میشه و هزینه ایجاد اتصال هم کاهش پیدا میکنه (اتصال به DB حدود ۲۰ میلیثانیه طول میکشه).
پس بهتره مقدار CONN_MAX_AGE رو روی None (یعنی اتصال نامحدود) یا یه مقدار متناسب با حجم درخواستهاتون تنظیم کنید.
با توجه به اینکه راج از PostgreSQL استفاده میکرد، توصیه کردیم اتصالها رو از Django جدا کنیم و از ابزارهایی مثل PgBouncer استفاده کنیم که به تنظیم اندازه اتصالها، تعداد کلاینتها و حداکثر اتصالها به دیتابیس کمک میکنه. همچنین باید پارامتر max_connections توی postgres.conf برای تعداد اتصالهای همزمان بهینه شده باشه.
اگه بکاند شما به سختی مقیاسپذیر میشه، احتمالاً دیتابیس عامل گلوگاه شماست. چند نکته برای بهبود عملکرد در پایگاه داده:
انتخاب دیتابیس مناسب: من با PostgreSQL راحتترم و تجربه خوبی دارم. این دیتابیس به خاطر عملکرد، قابلیت مقیاس و یکپارچگی دادهها محبوبه. البته اگر دنبال NoSQL هستید و میخواهید تحمل پارتیشن داشته باشید (CAP theorem توصیه میکنه) باز PostgreSQL قابلیت پارتیشنبندی داره (ببینید).
ایندکسها: اضافه کردن ایندکس مناسب میتونه سرعت کوئریهای SELECT رو افزایش بده و زمان پاسخ به کاربر رو کاهش بده. بهتره کوئریهای کند (> ۳۰ms) و پر تکرار رو آنالیز کنید. اما زیادهروی نکنید چون ایندکسها INSERT و UPDATE رو کند میکنن و فضای بیشتری اشغال میکنن. ابزار pgFouine میتونه برای شناسایی کوئریهای کند کمک کنه.
تنظیم سختافزار: CPU و RAM باید بر اساس تعداد اتصالات بهینه تنظیم بشن. ابزار Pgtune در این زمینه خیلی کمک میکنه.
یکی دیگه از روشها برای کاهش زمان درخواست/پاسخ، حذف Middlewareهای اضافه است که اپ شما بهشون نیاز نداره.
هر درخواست که به Django میاد باید از این میدلورها بگذره و این باعث اضافه شدن حدود ۲۰ تا ۳۰ میلیثانیه تاخیر به چرخه درخواست/پاسخ میشه.
مثلاً اپ راج از React Native مستقیم به APIهای Django درخواست میداد و نیازی به Middlewareهایی مثل sessions (django.contrib.sessions)، messages (django.contrib.messages) و admin (django.contrib.admin) نداشت. پس میتونید اینها رو از فایل تنظیمات settings > INSTALLED_APPS و settings > MIDDLEWARE حذف کنید.
چند نکته مفید موقع بازبینی کد که باید دقت کنید:
select_related() و prefetch_related() برای کاهش تعداد کوئریهای دیتابیس استفاده میشن:
select_related با انجام JOIN در SQL، دادههای مرتبط رو تو یک کوئری دریافت میکنه.
prefetch_related کوئریهای جداگانه برای هر ارتباط اجرا میکنه و بعد عملیات join رو تو پایتون انجام میده.
استفاده درست از اینها میتونه به شدت بار دیتابیس رو کاهش بده.
برای درج یا آپدیت دستهای دادهها بهتره از کوئریهای Bulk (مثل bulk_create() و bulk_update()) استفاده کنید، مثلاً ۱۰۰۰ یا ۵۰۰۰ رکورد رو یک جا وارد کنید.
اگر فقط اطلاعات خاصی نیاز دارید از توابعی مثل values() و only() استفاده کنید که فقط ستونهای مورد نیاز رو فراخوانی کنند و زمان پاسخ رو کاهش بدن.
طراحی زیرساخت باید قابلیت مقیاسپذیری و نگهداری راحت رو داشته باشه.
ابزارهایی مثل Docker امکان کانتینرسازی رو فراهم میکنن و با کمک Kubernetes میتونید کانتینرها رو مدیریت و در لحظه مقیاسبندی (افزایش یا کاهش) کنید بدون هیچ downtime.
یکی دیگه از گزینهها معماری Serverless هست که برای بارهای ناپایدار و bursty خیلی خوبه یا جایی که عملیات مبتنی بر event انجام میشه، مثلاً AWS Lambda.
بعد از مقیاسپذیری افقی، Amazon RDS امکان مقیاسپذیری عمودی رو هم در صورت افزایش ناگهانی ترافیک فراهم میکنه. حتماً استفاده از هارد SSD با IOPS بالا (برای بارهای تراکنشی مانند دیتابیسهای relational و NoSQL) رو در نظر داشته باشید.
مهمترین قسمت ماجرا اینجاست که چطور متوجه بشیم چیزی اشکال داره؟ کجا گلوگاهه؟ CPU زیادی استفاده میکنه یا حافظه؟
راهاندازی سیستمهای مانیتورینگ مثل Prometheus یا munin لازم هست برای شناسایی مشکلات و بررسی اگر تغییرات بهبود ایجاد کردن یا خیر.
همچنین فعال کردن لاگهای debug در قسمت settings > LOGGING تو Django، به شما زمان اجرای کوئریها رو نشون میده. معمولاً هدف اینه که زمان پاسخ API زیر ۱۰۰ میلیثانیه باشه و کوئریها هم در حدود ۲۰ میلیثانیه اجرا بشن.
نکته: این تنظیم فقط در محیط توسعه (Development) فعال باشه.
خلاصه اینکه وقتی دارید برای بارهای سنگین محصول میسازید، کلید کار کنجکاوی، پشتکار و نوآوریه. هر تغییر کوچک میتونه تاثیر بزرگی داشته باشه چون میلیونها درخواست روش اثر میذاره.
همیشه مانیتور کنید، اندازهگیری کنید و اشکالزدایی کنید؛ این شعار موفقیت در مقیاسپذیریه.
اگر نظری، سوال یا تجربهای دارید با کمال میل گوش میدم. موفق باشید و خوش بفرمایید به دنیای مقیاسپذیری!
۰
کد با می متعهد است که بالاترین سطح کیفی آموزش را در اختیار شما بگذارد. هدف به اشتراک گذاشتن دانش فناوری اطلاعات و توسعه نرم افزار در بالاترین سطح ممکن برای درستیابی به جامعه ای توانمند و قدرتمند است. ما باور داریم هر کسی میتواند با استمرار در یادگیری برنامه نویسی چالش های خود و جهان پیرامون خود را بر طرف کند و به موفقیت های چشم گیر برسد. با ما در این مسیر همراه باشید. کد با می اجتماع حرفه ای برنامه نویسان ایرانی.