اینم یه تجربه پراکنده دیگه!

در ادامه سری «ویر کارایی» میخواستم یه پست کوتاه در مورد یه مساله بسیار مهم در حوزه کارایی بنویسم. مساله‌ای که معروف به C10K هست.

اگه بخوام بصورت خیلی تو دل برو و خودمونی این مساله رو تعریف کنم باید بگم که: نوشتن برنامه‌هایی با قابلیت کار کردن با ۱۰ هزار کانکشن همزمان بصورت موازی هست. یعنی در آن واحد ما به ۱۰ هزار مشتری پاسخ بدیم. این مساله در اواخر ده ۱۹۹۰ توسط آقای کگل معرفی شده و ادعا شده که الان سخت‌افزارهای قوی برای ارائه سوریس وجود داره و گلوگاه معمولا بخش نرم‌افزار هست. برای این مساله راه‌حل‌های مختلفی پیشنهاد شده که میتونید پست اصلی آقای کگل رو بخونید. معمولا این راه حل‌ها ترکیبی از تنظیم سیستم عامل به علاوه بهبود نرم افزار هست.

هنوز روی این مساله کار میشه و از نقطه نظر‌های مختلف بررسی میشه. و حتی اخیرا حد ۱۰ هزار کاربر رو به ۱۰ میلیون کاربر همزمان افزایش دادن و مساله جدیدی برای بررسی ارائه کردن.

اما من هنوز در ایران هیچ راه حلی رو به چشم ندیدم که این تعداد کانکشن همزمان داشته باشه. من توی سیستم بانکی کار کردم و با سیستم‌های مخابراتی آشنا هستم. معمولا تعداد کاربران همزمان سیستم‌های بانکی زیر ۴۰۰ کاربر همزمان و سیستم مخابراتی اگه اشتباه نکنم زیر ۱۰۰۰ کاربر هست. یعنی هنوز راه زیادی تا مساله C10K داره. البته باید بگم که چون سیستم‌های مخابراتی و بانکی مبتنی بر تراکنش هست، معمولا پیچیده تر از یک وب سرور هست که بخواد ۱۰ هزار کلاینت همزمان داشته باشه. هنوز هم شخصا سعادت آشنایی با سایتی رو نداشتم که این مقدار بازدید کننده داشته باشه.

امیدوارم در آینده نزدیک یه بخورد نزدیک با این مساله داشته باشم. و این یعنی که سطح سرویس‌ها اینترنتی در کشور ما به شدت افزایش یافته.

همین!

اینم یه تجربه پراکنده دیگه!

یه مدته که اینجا حرف برنامه نویسی زده نشده و میترسم «عنودان بد گهر» فکر کنن که من برنامه نویسی رو فراموش کردم. پس این دفعه سعی میکنم یه موضوع عمیق که به کارایی سرور سوکت مربوط میشه رو بنویسم.

توی یکی از پروژه‌هایی که من انجام میدادم مهمترین نیازمندی این بود که با کارایی بالا از سوکت UDP اطلاعات رو دریافت و پردازش کنم. کل پردازش یه سری عملیات به نسبت ساده ریاضی بود و کل ماجرا توی کارایی اون سوکت UDP خلاصه میشد. خب اولین راهی که پیش من بود این بود که اولا تحقیق و دوما آزمایش کنم. توی تحقیقاتم با توجه به مطلبی که توی این کتاب در مورد Nginx خونده بودم و نتیجه جستجو‌هام به این نتیجه رسیدم که بایستی بصورت Async سوکت‌ها رو مدیریت کنم. توی تحقیقاتم با توجه به معلوم نبودم پلتفرم توسعه با استفاده از Node.JS و C# نمونه‌هایی پیاده کردم و تست کردم و الحق که به نتایج قابل قبولی تو مایه‌های ۱۰۰۰ یا ۱۰۰۰۰ پکت در ثانیه رسیدم. توی C و C++ راه حل‌های متفاوتی برای این کار ارائه شده بود که بهترینش به نظرم کاری بود که توی boost::asio انجام شده بود که کلا روند async رو برای سوکت‌ها فایلها و تایمرها پیاده‌سازی کرده بود. نتیجه استفاده از این کتابخانه بسیار خوب بود و تا الان فراتر از حد نیاز کارایی رو فراهم کرده. کد هم به شدت ساده است. کل سرور در حد ۲۰ خط پیاده‌سازی شده.

اما برم سراغ یه مقدار جزئیات پیاده‌سازی که این کتابخونه از چشم من دور کرده بود. برای اینکه بتونیم کارایی خوبی داشته باشیم بایستی بتونیم با یه روندی منتظر سوکت‌ها نمونده و در صورتی که براشون اتفاق خاصی افتاد اونها رو بررسی کنیم. به این حالت بایستی یه حلقه داشته باشیم که بصورت بی‌پایان اجرا میشه و منتظر رخدادهاست که معروف به حلقه رخداد هست. همانطور که میدونید سوکت‌ها اصلتا در  کرنل مدیریت میشن و اطلاع از رخدادها رو کرنل برای ما فراهم میکنه. پس اگه بخوایم بصورت غیرهمزمان(async) اونها رو بررسی کنیم چاره‌ای نداریم که  کرنل این کار رو برای ما انجام بده. به این شکل که مکانیزمی داشته باشه که ما بتونیم بگیم که حواست به این سوکت‌ها باشه و هر وقت خبری شد خبرم کن. توی سیستم عامل‌های مختلف کرنل مکانیزم‌های متفاوتی برای این کار رو فراهم میکنه:

  • kqueue: مکانیزمی هست که توی خانواده BSD فراهم شده
  • epoll: مکانیزمی هست که توی کرنل لینوکس فراهم شده
  • event port: در سولاریس مورد استفاده قرار گرفته
  • Overlapped I/O: به پیاده سازی اختصاصی این ماجرا در ویندوز گفته میشه.
  • Input/Output Completion Port: روندی است که ویندوز و سولاریس اون رو پیاده‌سازی کردن.

البته باید بگم که برخی از این مکانیزم‌ها به سیستم عامل‌های دیگه هم پورت شدن و توی اونها هم قابل دسترسی هستند.

خب اگه ما بخوایم کدی بنویسیم که از یکی از این مکانیزم‌ها به درستی استفاده کنه و قابل پورت به سیستم عامل‌های دیگه باشه میرسیم به همون boos::asio که دقیقا همین کار رو کرده وسیستم عامل‌های مختلف رو هم پیشتیبانی میکنه. پیاده‌سازی این مکانیزم‌ها معمولا یه سختی هم داره و اون اینه که شما بایستی با system call های کرنل اون سیستم عامل آشنایی خوبی داشته باشی که در اکثر برنامه‌نویسا نیست که این باعث سخت شدن نگهداری کد میشه.

در مجموع استفاده کردن این کتابخانه‌ها که هم بخوبی تست شدن، هم ثبات کافی دارن باعث سادگی کار کاهش زمان مورد نیاز برای توسعه میشه. اما یادتون باشه که این سادگی هزینه داره و اگه یه روزی برسه این برنامه جواب کارتون رو نده شما باید آستیناتون رو بالا بزنید و کد رو یه شخم حسابی بزنید!

همین!

اینم یه تجربه پراکنده!

این پست میخواست یه پست به نسبت بلند باشه. اما هرچی با خودم بالا و پایینش کردم دیدم خوب نیست که توی یک پست جمعش کنم. پس اول یه پست کوتاه می‌نویسم که اصل ماجرا از چه قراره و بعدش ایشالا اگه عمری بود جزئیات و کاربردها و این چیزا رو میگم!

کل این متن از این سایت ایده گرفته شده و میخواد بخش از حرفهایی که اونجا زده شده رو بگه. reactive در معنی لغتی به معنی واکنشدار و واکنشی هست و به سیستمی اطلاق میشه که واکنش قابل قبولی در زمان قابل قبول نسبت به درخواست‌هایی که بهش داده میشه، انجام میده. این سیستم‌های این ویژگی‌ها رو دارن:

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

فعلا همین!