منبع اصلی نوشتار زیر در این لینک قرار دارد

ویرکارایی: سرور سوکت با کارایی بالا در C++

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

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

توی یکی از پروژه‌هایی که من انجام میدادم مهمترین نیازمندی این بود که با کارایی بالا از سوکت 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 های کرنل اون سیستم عامل آشنایی خوبی داشته باشی که در اکثر برنامه‌نویسا نیست که این باعث سخت شدن نگهداری کد میشه.

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

همین!



برچسب ها : ,