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

اطلاعیه

همکاران، در اواسط تابستان قصد دارم یک سری مقاله دیگر در مورد طراحی سیستم های صف منتشر کنم: "تجربه VTrade" - تلاشی برای نوشتن چارچوبی برای سیستم های معاملاتی. این مجموعه به بررسی تئوری و عمل ساخت بورس، حراج و فروشگاه می پردازد. در پایان مقاله، از شما دعوت می کنم به موضوعاتی که بیشتر مورد علاقه شما هستند رای دهید.

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

این آخرین مقاله از مجموعه برنامه های کاربردی واکنشی توزیع شده در Erlang/Elixir است. که در مقاله اول شما می توانید مبانی نظری معماری واکنشی را بیابید. مقاله دوم الگوها و مکانیسم های اساسی برای ساخت چنین سیستم هایی را نشان می دهد.

امروز ما مسائل مربوط به توسعه پایه کد و پروژه ها را به طور کلی مطرح خواهیم کرد.

سازماندهی خدمات

در زندگی واقعی، هنگام توسعه یک سرویس، اغلب باید چندین الگوی تعامل را در یک کنترلر ترکیب کنید. به عنوان مثال، سرویس کاربران که مشکل مدیریت پروفایل کاربران پروژه را حل می کند، باید به درخواست های req-resp پاسخ دهد و به روز رسانی پروفایل را از طریق pub-sub گزارش دهد. این مورد بسیار ساده است: در پشت پیام‌رسانی یک کنترل‌کننده وجود دارد که منطق سرویس را پیاده‌سازی می‌کند و به‌روزرسانی‌ها را منتشر می‌کند.

وضعیت زمانی پیچیده تر می شود که ما نیاز به اجرای یک سرویس توزیع شده با تحمل خطا داریم. بیایید تصور کنیم که الزامات برای کاربران تغییر کرده است:

  1. اکنون سرویس باید درخواست ها را در 5 گره خوشه پردازش کند،
  2. قادر به انجام وظایف پردازش پس زمینه،
  3. و همچنین قادر به مدیریت پویا لیست های اشتراک برای به روز رسانی پروفایل باشید.

اظهار نظر: ما موضوع ذخیره سازی مداوم و تکرار داده ها را در نظر نمی گیریم. بیایید فرض کنیم که این مسائل زودتر حل شده اند و سیستم از قبل دارای یک لایه ذخیره سازی قابل اعتماد و مقیاس پذیر است و کنترلرها مکانیسم هایی برای تعامل با آن دارند.

شرح رسمی سرویس کاربران پیچیده تر شده است. از دیدگاه یک برنامه نویس، تغییرات به دلیل استفاده از پیام رسانی حداقل است. برای ارضای اولین نیاز، باید تعادل را در نقطه تبادل req-resp پیکربندی کنیم.

نیاز به پردازش وظایف پس زمینه اغلب اتفاق می افتد. در کاربران، این می تواند بررسی اسناد کاربر، پردازش چند رسانه ای دانلود شده یا همگام سازی داده ها با رسانه های اجتماعی باشد. شبکه های. این وظایف باید به نحوی در داخل خوشه توزیع شوند و پیشرفت اجرا نظارت شود. بنابراین، ما دو گزینه راه حل داریم: یا از الگوی توزیع وظیفه در مقاله قبلی استفاده کنید، یا اگر مناسب نیست، یک زمانبندی کار سفارشی بنویسید که مجموعه پردازنده ها را به روشی که ما نیاز داریم مدیریت کند.

نقطه 3 به پسوند قالب pub-sub نیاز دارد. و برای پیاده سازی، پس از ایجاد یک نقطه تبادل pub-sub، باید علاوه بر این، کنترلر این نقطه را در سرویس خود راه اندازی کنیم. بنابراین، گویی منطق پردازش اشتراک‌ها و لغو اشتراک‌ها را از لایه پیام‌رسانی به پیاده‌سازی کاربران منتقل می‌کنیم.

در نتیجه، تجزیه مشکل نشان داد که برای برآورده کردن الزامات، باید 5 نمونه از سرویس را در گره های مختلف راه اندازی کنیم و یک موجودیت اضافی ایجاد کنیم - یک کنترل کننده pub-sub که مسئول اشتراک است.
برای اجرای 5 هندلر، نیازی به تغییر کد سرویس ندارید. تنها اقدام اضافی تنظیم قوانین تعادل در نقطه تبادل است که کمی بعد در مورد آن صحبت خواهیم کرد.
همچنین یک پیچیدگی اضافی وجود دارد: کنترل کننده pub-sub و زمانبندی کار سفارشی باید در یک نسخه کار کنند. باز هم، سرویس پیام رسانی، به عنوان یک سرویس اساسی، باید مکانیزمی برای انتخاب رهبر ارائه دهد.

انتخاب رهبر

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

در سیستم‌هایی که مستعد تمرکز نیستند، از الگوریتم‌های جهانی و مبتنی بر اجماع، مانند paxos یا raft استفاده می‌شود.
از آنجایی که پیام‌رسانی یک کارگزار و یک عنصر مرکزی است، از همه کنترل‌کننده‌های خدمات - رهبران نامزدها اطلاع دارد. پیام‌رسانی می‌تواند یک رهبر را بدون رأی‌گیری منصوب کند.

پس از راه اندازی و اتصال به نقطه تبادل، تمامی سرویس ها یک پیام سیستمی دریافت می کنند #'$leader'{exchange = ?EXCHANGE, pid = LeaderPid, servers = Servers}... اگر LeaderPid مصادف با pid روند فعلی، به عنوان رهبر، و لیست منصوب می شود Servers شامل تمام گره ها و پارامترهای آنها می شود.
در لحظه ای که یک گره جدید ظاهر می شود و یک گره خوشه فعال قطع می شود، همه کنترل کننده های سرویس دریافت می کنند #'$slave_up'{exchange = ?EXCHANGE, pid = SlavePid, options = SlaveOpts} и #'$slave_down'{exchange = ?EXCHANGE, pid = SlavePid, options = SlaveOpts} بود.

به این ترتیب، همه اجزا از همه تغییرات آگاه هستند و خوشه تضمین می شود که در هر زمان معین یک رهبر داشته باشد.

واسطه ها

برای اجرای فرآیندهای پیچیده پردازش توزیع شده، و همچنین در مشکلات بهینه سازی یک معماری موجود، استفاده از واسطه ها راحت است.
برای اینکه کد سرویس را تغییر ندهید و به عنوان مثال مشکلات مربوط به پردازش اضافی، مسیریابی یا ثبت پیام ها را حل نکنید، می توانید قبل از سرویس یک کنترل کننده پروکسی را فعال کنید که تمام کارهای اضافی را انجام می دهد.

یک مثال کلاسیک از بهینه سازی pub-sub یک برنامه کاربردی توزیع شده با هسته تجاری است که رویدادهای به روز رسانی مانند تغییرات قیمت در بازار را ایجاد می کند و یک لایه دسترسی - سرورهای N که یک API وب سوکت برای مشتریان وب ارائه می کند.
اگر مستقیماً تصمیم بگیرید، خدمات مشتری به صورت زیر است:

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

بیایید تصور کنیم که ما 50000 مشترک برای موضوع "اخبار" داریم. مشترکین به طور مساوی در بین 5 سرور توزیع می شوند. در نتیجه، هر به‌روزرسانی که به نقطه تبادل می‌رسد، 50000 بار تکرار می‌شود: 10000 بار در هر سرور، با توجه به تعداد مشترکین روی آن. طرح خیلی موثری نیست، درست است؟
برای بهبود وضعیت، بیایید پروکسی را معرفی کنیم که همان نام نقطه تبادل است. ثبت کننده جهانی نام باید بتواند نزدیکترین فرآیند را با نام برگرداند، این مهم است.

بیایید این پروکسی را روی سرورهای لایه دسترسی راه‌اندازی کنیم، و تمام فرآیندهای ما که به api websocket خدمات می‌دهند، در آن مشترک می‌شوند، نه در نقطه تبادل اصلی pub-sub در هسته. پروکسی فقط در صورت اشتراک منحصربه‌فرد در هسته مشترک می‌شود و پیام دریافتی را برای همه مشترکین خود تکرار می‌کند.
در نتیجه به جای 5 پیام بین هسته و سرورهای دسترسی 50000 پیام ارسال می شود.

مسیریابی و تعادل

Req-Resp

در اجرای پیام رسانی فعلی، 7 استراتژی توزیع درخواست وجود دارد:

  • default. درخواست برای همه کنترل کننده ها ارسال می شود.
  • round-robin. درخواست ها شمارش می شوند و به صورت چرخه ای بین کنترل کننده ها توزیع می شوند.
  • consensus. کنترل‌کننده‌هایی که خدمات را ارائه می‌کنند به رهبران و بردگان تقسیم می‌شوند. درخواست ها فقط برای رهبر ارسال می شود.
  • consensus & round-robin. این گروه یک رهبر دارد، اما درخواست ها بین همه اعضا توزیع می شود.
  • sticky. تابع هش محاسبه شده و به یک کنترل کننده خاص اختصاص داده می شود. درخواست های بعدی با این امضا به همان رسیدگی کننده می رود.
  • sticky-fun. هنگام مقداردهی اولیه نقطه تبادل، تابع محاسبه هش برای sticky متعادل کردن
  • fun. مشابه با استیکی-فن، فقط شما می توانید آن را تغییر مسیر دهید، رد کنید یا از قبل پردازش کنید.

استراتژی توزیع زمانی تنظیم می شود که نقطه تبادل اولیه شود.

پیام‌رسانی علاوه بر ایجاد تعادل، به شما امکان می‌دهد تا نهادها را برچسب‌گذاری کنید. بیایید به انواع برچسب ها در سیستم نگاه کنیم:

  • برچسب اتصال به شما امکان می دهد بفهمید که رویدادها از طریق کدام ارتباط رخ داده اند. زمانی استفاده می‌شود که یک فرآیند کنترل‌کننده به یک نقطه مبادله یکسان، اما با کلیدهای مسیریابی متفاوت متصل می‌شود.
  • برچسب خدمات. به شما امکان می‌دهد تا کنترل‌کننده‌ها را در گروه‌هایی برای یک سرویس ترکیب کنید و قابلیت‌های مسیریابی و تعادل را گسترش دهید. برای الگوی req-resp، مسیریابی خطی است. ما یک درخواست را به نقطه تبادل ارسال می کنیم، سپس آن را به سرویس منتقل می کند. اما اگر لازم باشد هندلرها را به گروه های منطقی تقسیم کنیم، تقسیم با استفاده از تگ ها انجام می شود. هنگام تعیین یک برچسب، درخواست به گروه خاصی از کنترلرها ارسال می شود.
  • برچسب درخواست به شما امکان می دهد بین پاسخ ها تمایز قائل شوید. از آنجایی که سیستم ما ناهمزمان است، برای پردازش پاسخ های سرویس باید بتوانیم یک RequestTag را هنگام ارسال درخواست مشخص کنیم. از آن ما قادر خواهیم بود پاسخی که به درخواست ما رسیده است را درک کنیم.

میخانه-زیر

برای pub-sub همه چیز کمی ساده تر است. ما یک نقطه تبادل داریم که پیام ها به آن منتشر می شود. نقطه تبادل پیام ها را بین مشترکینی که مشترک کلیدهای مسیریابی مورد نیاز خود شده اند توزیع می کند (می توان گفت که این مشابه با موضوعات است).

مقیاس پذیری و تحمل خطا

مقیاس پذیری سیستم به طور کلی به درجه مقیاس پذیری لایه ها و اجزای سیستم بستگی دارد:

  • خدمات با افزودن گره‌های اضافی به خوشه با کنترل‌کننده‌های این سرویس، مقیاس‌بندی می‌شوند. در طول عملیات آزمایشی، می توانید سیاست متعادل سازی بهینه را انتخاب کنید.
  • خود سرویس پیام رسانی در یک خوشه مجزا معمولاً یا با جابجایی نقاط مبادله بارگذاری شده خاص به گره های خوشه جداگانه یا با افزودن فرآیندهای پراکسی به مناطق بارگذاری شده خاص خوشه مقیاس می شود.
  • مقیاس پذیری کل سیستم به عنوان یک مشخصه به انعطاف پذیری معماری و توانایی ترکیب خوشه های منفرد در یک موجودیت منطقی مشترک بستگی دارد.

موفقیت یک پروژه اغلب به سادگی و سرعت مقیاس بندی بستگی دارد. پیام رسانی در نسخه فعلی آن همراه با برنامه رشد می کند. حتی اگر مجموعه 50 تا 60 دستگاهی نداشته باشیم، می توانیم به فدراسیون متوسل شویم. متاسفانه موضوع فدراسیون خارج از حوصله این مقاله است.

رزرو

هنگام تجزیه و تحلیل تعادل بار، ما قبلاً در مورد افزونگی کنترلرهای سرویس بحث کردیم. با این حال، پیام نیز باید رزرو شود. در صورت خرابی گره یا ماشین، پیام رسانی باید به طور خودکار و در کوتاه ترین زمان ممکن بازیابی شود.

در پروژه هایم از گره های اضافی استفاده می کنم که در صورت سقوط بار را برمی دارند. Erlang یک اجرای استاندارد حالت توزیع شده برای برنامه های OTP دارد. حالت توزیع شده با راه‌اندازی برنامه ناموفق در گره دیگری که قبلاً راه‌اندازی شده است، در صورت شکست، بازیابی را انجام می‌دهد. فرآیند شفاف است؛ پس از یک شکست، برنامه به طور خودکار به گره failover منتقل می شود. می توانید در مورد این قابلیت بیشتر بخوانید اینجا.

کارایی

بیایید سعی کنیم حداقل عملکرد rabbitmq و پیام های سفارشی خود را به طور تقریبی مقایسه کنیم.
یافتم نتایج رسمی تست rabbitmq از تیم openstack.

در بند 6.14.1.2.1.2.2. سند اصلی نتیجه RPC CAST را نشان می دهد:
بلوک های سازنده برنامه های کاربردی توزیع شده تقریب دوم

از قبل هیچ تنظیمات اضافی برای هسته سیستم عامل یا erlang VM انجام نمی دهیم. شرایط انجام آزمایش:

  • erl انتخاب می کند: +A1 +sbtu.
  • تست در یک گره erlang روی یک لپ تاپ با i7 قدیمی در نسخه موبایل اجرا می شود.
  • تست های خوشه ای بر روی سرورهایی با شبکه 10G انجام می شود.
  • کد در کانتینرهای docker اجرا می شود. شبکه در حالت NAT.

کد تست:

req_resp_bench(_) ->
  W = perftest:comprehensive(10000,
    fun() ->
      messaging:request(?EXCHANGE, default, ping, self()),
      receive
        #'$msg'{message = pong} -> ok
      after 5000 ->
        throw(timeout)
      end
    end
  ),
  true = lists:any(fun(E) -> E >= 30000 end, W),
  ok.

سناریو 1: این تست بر روی یک لپ تاپ با نسخه موبایل قدیمی i7 اجرا می شود. آزمایش، پیام رسانی و سرویس بر روی یک گره در یک ظرف Docker اجرا می شود:

Sequential 10000 cycles in ~0 seconds (26987 cycles/s)
Sequential 20000 cycles in ~1 seconds (26915 cycles/s)
Sequential 100000 cycles in ~4 seconds (26957 cycles/s)
Parallel 2 100000 cycles in ~2 seconds (44240 cycles/s)
Parallel 4 100000 cycles in ~2 seconds (53459 cycles/s)
Parallel 10 100000 cycles in ~2 seconds (52283 cycles/s)
Parallel 100 100000 cycles in ~3 seconds (49317 cycles/s)

سناریو 2: 3 گره در حال اجرا بر روی ماشین های مختلف تحت docker (NAT).

Sequential 10000 cycles in ~1 seconds (8684 cycles/s)
Sequential 20000 cycles in ~2 seconds (8424 cycles/s)
Sequential 100000 cycles in ~12 seconds (8655 cycles/s)
Parallel 2 100000 cycles in ~7 seconds (15160 cycles/s)
Parallel 4 100000 cycles in ~5 seconds (19133 cycles/s)
Parallel 10 100000 cycles in ~4 seconds (24399 cycles/s)
Parallel 100 100000 cycles in ~3 seconds (34517 cycles/s)

در همه موارد، استفاده از CPU از 250٪ تجاوز نمی کند.

نمایش نتایج: از

امیدوارم این چرخه به نظر یک ذهن نشینی نباشد و تجربه من برای محققان سیستم های توزیع شده و متخصصانی که در همان ابتدای ساختن معماری های توزیع شده برای سیستم های تجاری خود هستند و با علاقه به Erlang/Elixir نگاه می کنند مفید واقع شود. اما شک داشته باشید ارزشش را دارد...

عکس @chuttersnap

فقط کاربران ثبت نام شده می توانند در نظرسنجی شرکت کنند. ورود، لطفا.

به عنوان بخشی از سری آزمایش VTrade چه موضوعاتی را باید با جزئیات بیشتری پوشش دهم؟

  • تئوری: بازارها، سفارش‌ها و زمان‌بندی آنها: DAY، GTD، GTC، IOC، FOK، MOO، MOC، LOO، LOC

  • کتاب سفارشات. تئوری و عمل پیاده سازی کتاب با گروه بندی

  • تجسم تجارت: تیک ها، میله ها، قطعنامه ها. نحوه نگهداری و چسب زدن

  • بک آفیس. برنامه ریزی و توسعه. نظارت کارکنان و بررسی حوادث

  • API. بیایید بفهمیم چه رابط هایی مورد نیاز است و چگونه آنها را پیاده سازی کنیم

  • ذخیره سازی اطلاعات: PostgreSQL، Timescale، Tarantool در سیستم های معاملاتی

  • واکنش پذیری در سیستم های معاملاتی

  • دیگر. در نظرات می نویسم

6 کاربر رای دادند. 4 کاربر رای ممتنع دادند.

منبع: www.habr.com

اضافه کردن نظر