دسترس‌پذیری بالا در اوج آزمون‌ها: Redis Sentinel برای ۴۵ تا ۷۰ هزار کاربر هم‌زمان

از دسامبر ۲۰۲۱ تا اکتبر ۲۰۲۳ مهندس ارشد بک‌اند «گزینه دو» در تهران بودم. محور اصلی کارم یک معماری Redis Sentinel با دسترس‌پذیری بالا بود که خودم طراحی و راه‌اندازی کردم و در اوج آزمون‌ها ۴۵ تا ۷۰ هزار کاربر هم‌زمان را تحمل کرد. کارهای دیگر حول همین محور بودند: بهینه‌سازی کوئری‌ها و APIها،‏ تست بار،‏ بهبود فرایند انتشار و تیم علم داده‌ای که خودم تشکیل دادم.

زمینه

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

پیش از سمت ارشد،‏ در ژوئن ۲۰۲۱ به «گزینه دو» پیوستم. در همان ماه‌های اول سیستم گزارش‌گیری را از نو طراحی کردم که بار پایگاه‌داده را کم کرد و گزارش‌های پرکاربرد را سریع‌تر کرد. لاگ ساخت‌یافته را هم با Serilog راه انداختم. هر دو کار به‌طور کلی به مسئلهٔ اوج ربط دارند. گزارش‌هایی که پایگاه‌داده را کمتر درگیر می‌کنند،‏ جای بیشتری برای ترافیک آزمون باقی می‌گذارند و با لاگ ساخت‌یافته خیلی راحت‌تر می‌شود فهمید سیستم زیر بار چه کرده است.

مسئلهٔ مهندسی

ترافیک آزمون انفجاری و هم‌زمان است. کاربران با هم شروع می‌کنند،‏ همان صفحه‌ها و APIها به همان ترتیب درخواست می‌گیرند و دقیقه‌های آخرِ پیش از پایان مهلت،‏ موج تازه‌ای از عملیات نوشتن به راه می‌اندازند. به‌طور کلی سه نتیجه از این وضع بیرون می‌آید:

  • مسیرهای پرتکرار همه‌چیز را تعیین می‌کنند. تعداد کمی API بیشتر بار را می‌کشند. هر ناکارآمدی در آن‌ها در تعداد کاربران آنلاین ضرب می‌شود.
  • وابستگی‌های مشترک به نقطهٔ شکست واحد تبدیل می‌شوند. کشی که همهٔ درخواست‌ها به آن سر می‌زنند،‏ تا لحظه‌ای که از کار نیفتاده،‏ کارایی را بالا می‌برد. همین که از کار بیفتد،‏ همهٔ درخواست‌ها در بدترین لحظهٔ ممکن مستقیم سراغ پایگاه‌داده می‌روند.
  • زمان اوج،‏ وقت کشف مشکل نیست. مشکلات باید از قبل و زیر بار شبیه‌سازی‌شده آشکار شوند.

کاری که انجام دادم

  • دسترس‌پذیری بالا برای Redis. برای ترافیک اوج آزمون‌ها یک معماری Redis Sentinel طراحی و راه‌اندازی کردم.
  • بهینه‌سازی پایگاه‌داده و API. کوئری‌های SQL Server،‏ مسیرهای کش و APIهای پرتکرار را بهینه کردم.
  • تست بار پیش از اوج. با تست بار Locust گلوگاه‌ها را پیش از رسیدن اوج آزمون‌ها پیدا کردم.
  • انتشار مطمئن‌تر. قابلیت اطمینان انتشارها را با سرویس‌های مبتنی بر Docker،‏ لاگ ساخت‌یافته و CI/CD بالا بردم.
  • تیم علم داده. یک تیم چهارنفرهٔ علم داده تشکیل دادم و رهبری‌اش را بر عهده داشتم؛‏ این تیم الگوریتم‌های رتبه‌بندی،‏ امتیازدهی و پیشنهاددهی ساخت.

نکته‌های طراحی

توضیحات زیر کلی‌اند: این‌که چنین سیستم‌هایی چطور رفتار می‌کنند و چرا این ابزارها به کارشان می‌آیند. چیدمان داخلی زیرساخت «گزینه دو» در آن‌ها شرح داده نشده است.

چرا Redis Sentinel

یک نمونهٔ تکی Redis سریع و ساده است،‏ اما در نهایت فقط یک ماشین است. Sentinel گروهی از فرایندهای پایشگر را کنار نمونهٔ اصلی و رپلیکاهایش قرار می‌دهد. وقتی تعداد کافی از Sentinelها توافق کنند که نمونهٔ اصلی در دسترس نیست،‏ یکی از رپلیکاها را به نمونهٔ اصلی ارتقا می‌دهند و به کلاینت‌ها خبر می‌دهند نمونهٔ اصلی جدید کجاست. مدل برنامه‌نویسی اپلیکیشن تغییری نمی‌کند؛‏ اپلیکیشن فقط به‌جای استفاده از یک آدرس ثابت،‏ نمونهٔ اصلی فعلی را از Sentinel می‌پرسد.

بده‌بستان‌هایش:

  • تکثیر داده در Redis ناهم‌زمان است؛‏ پس نوشتنی که درست پیش از جابه‌جایی خودکار نمونهٔ اصلی تأیید شده،‏ ممکن است از دست برود. داده‌ای که نباید از دست برود جایش در پایگاه‌داده است و Redis نقش کش یا یک نسخهٔ دوم سریع را دارد.
  • Sentinel با رأی‌گیری تصمیم می‌گیرد: حد نصاب فقط تأیید می‌کند که نمونهٔ اصلی از دسترس خارج شده است و برای خود جابه‌جایی،‏ اکثریت همهٔ Sentinelها باید یک رهبر انتخاب کنند و به آن اجازهٔ جابه‌جایی بدهند. در عمل یعنی دست‌کم سه فرایند Sentinel روی ماشین‌های جداگانه لازم است تا با از کار افتادن یک ماشین،‏ هنوز اکثریت باقی بماند.
  • کلاینت‌ها برای پیدا کردن نمونهٔ اصلی فعلی باید از Sentinel پشتیبانی کنند و بازهٔ کوتاهی را که جابه‌جایی در جریان است تاب بیاورند.
  • Sentinel دسترس‌پذیری می‌دهد،‏ نه شاردینگ. همهٔ داده‌ها همچنان روی یک نمونهٔ اصلی است. Redis Cluster داده‌ها را میان چند گره پخش می‌کند،‏ اما پیچیدگی‌های خودش را دارد؛‏ پس وقتی داده‌ها روی یک ماشین جا می‌شوند،‏ Sentinel اغلب انتخاب ساده‌تری است.

تست بار با Locust

در Locust رفتار کاربر با کد Python توصیف می‌شود؛‏ پس تست می‌تواند به‌جای کوبیدن مداوم یک URL،‏ یک جریان واقعی آزمون را دنبال کند (ورود،‏ باز کردن آزمون،‏ ارسال پاسخ‌ها). این مهم است،‏ چون گلوگاه‌های آزمون اغلب بین مراحل ظاهر می‌شوند؛‏ مثلاً کوئری‌ای که به‌تنهایی مشکلی ندارد،‏ اما وقتی تعداد زیادی پاسخ با هم می‌رسند کند می‌شود.

کوئری‌ها،‏ مسیرهای کش و APIهای پرتکرار

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

انتشار،‏ لاگ و پیام‌رسانی

ایمیج‌های Docker همان بستهٔ اجرایی را در محیط تست و محیط عملیاتی تحویل می‌دهند و CI/CD انتشار را به فرایندی تکرارپذیر تبدیل می‌کند. Serilog رویدادهای ساخت‌یافته با ویژگی‌های نام‌دار می‌نویسد؛‏ بنابراین می‌شود لاگ‌ها را بر اساس درخواست،‏ کاربر یا اندپوینت فیلتر کرد،‏ به‌جای این‌که مثل متن ساده در آن‌ها جست‌وجو کرد. RabbitMQ هم بخشی از پشتهٔ فنی است. به‌طور کلی،‏ یک کارگزار پیام اجازه می‌دهد کارهای کند یا غیرفوری از مسیر درخواست بیرون بروند تا در زمان اوج با کاربران رقابت نکنند.

فناوری‌ها

  • ‎.NET 6
  • SQL Server
  • Redis Sentinel
  • RabbitMQ
  • Docker
  • Serilog
  • Locust برای تست بار

نتیجه

  • معماری Redis Sentinel در اوج آزمون‌ها ۴۵ تا ۷۰ هزار کاربر هم‌زمان را تحمل کرد.
  • بازطراحی قبلی سیستم گزارش‌گیری بار پایگاه‌داده را کم کرد و گزارش‌های پرکاربرد را سریع‌تر کرد.

مطالب مرتبط

تجربه‌های این پروژه را در چند راهنمای عملی هم نوشته‌ام: تست بار API با Locust،‏ بهینه‌سازی SQL Server برای APIهای پرترافیک و کش در ASP.NET Core.

نمونه‌کار پلتفرم آزمون تطبیقی به کار بعدی‌ام در حوزهٔ سنجش در NovaTech می‌پردازد. برای کارهای مربوط به کارایی بک‌اند و دسترس‌پذیری بالا،‏ صفحهٔ همکاری را ببینید.

پرسش‌های رایج

معماری Redis Sentinel چند کاربر هم‌زمان را تحمل کرد؟

معماری Redis Sentinel با دسترس‌پذیری بالا که مهدی مولوی در «گزینه دو» طراحی و راه‌اندازی کرد،‏ در اوج آزمون‌ها ۴۵ تا ۷۰ هزار کاربر هم‌زمان را تحمل کرد.

گلوگاه‌ها پیش از اوج آزمون‌ها چطور پیدا می‌شدند؟

او با تست بار Locust گلوگاه‌ها را پیش از رسیدن اوج پیدا می‌کرد و کوئری‌های SQL Server،‏ مسیرهای کش و APIهای پرتکرار را بهینه کرد.

او در «گزینه دو» چه کار دیگری را رهبری کرد؟

یک تیم چهارنفرهٔ علم داده تشکیل داد و رهبری کرد که الگوریتم‌های رتبه‌بندی،‏ امتیازدهی و پیشنهاددهی ساخت.

در همین بخش

نمونه‌کارها