معماری Muru؛ جداسازی ارتباط با client از اجرای Python

چرا مهم است

اگر برنامه WSGI یا ASGI داری، این معماری نشان می‌دهد پذیرش اتصال و اجرای Python در Muru کجا انجام می‌شوند. می‌توانی ببینی چرا شکست import در release تازه، پاسخ‌گویی release قبلی را متوقف نمی‌کند.

قبل از خواندن، این‌ها را بدان۵ مفهوم
fork

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

بیشتر: fork

WSGI

استاندارد ارتباط میان web server و برنامه Python که اطلاعات request را در environ به برنامه می‌دهد.

بیشتر: WSGI

event loop

سازوکاری که اجرای کارهای async و رسیدگی به رویدادها را هماهنگ می‌کند.

بیشتر: event loop

ASGI

استاندارد ارتباط میان server و برنامه Python با پشتیبانی از اجرای async؛ اطلاعات ارتباط در scope در اختیار برنامه قرار می‌گیرد.

در حال نگارش: ASGI

queue

ساختاری برای نگهداری کارهای منتظر تا امکان پردازش آن‌ها فراهم شود.

در حال نگارش: queue

Graham Dumpleton در توضیح معماری Muru نشان می‌دهد که کار میان چهار بخش edge،؜ host،؜ worker و controller تقسیم شده است. در حالت development،؜ دستور muructl run یک برنامه Rust است که edge و controller را در خود دارد و یک process جداگانه Python برای host اجرا می‌کند.

بخش edge اتصال‌های client را می‌پذیرد، TLS و HTTP/1.1 و HTTP/2 را مدیریت می‌کند و مقصد هر request را تعیین می‌کند. ارائه فایل‌های static،؜ فشرده‌سازی response و تولید صفحه‌های خطا نیز بر عهده همین بخش است. edge هیچ کد Python اجرا نمی‌کند؛ بنابراین request کند در برنامه، اجرای این بخش را معطل نمی‌کند.

host برنامه را یک بار import می‌کند و با fork،؜ worker‌ها را می‌سازد. خودش به request پاسخ نمی‌دهد و اگر worker از کار بیفتد، جایگزینش می‌کند. هر worker یک interpreter Python با کد بارگذاری‌شده برنامه و لایه‌ای کوچک به زبان Rust دارد. این لایه request را برای WSGI به یک thread و برای ASGI به event loop تحویل می‌دهد. controller نیز host‌ها و مسیر ترافیک را مدیریت می‌کند، اما در مسیر عبور request نیست.

edge ابتدا release مقصد و سپس worker دارای کمترین request در حال اجرا را انتخاب می‌کند. اگر همه مشغول باشند، request در queue منتظر می‌ماند. ارتباط با worker از طریق Unix socket انجام می‌شود، اما داده دوباره به متن HTTP تبدیل نمی‌شود: اطلاعات request به‌شکل پیام‌های دارای نوع و سپس body فرستاده می‌شود.

جریان داده با سرعت مصرف هماهنگ است. اگر client کند باشد، این کندی تا برنامه منتقل می‌شود تا داده بدون محدودیت در buffer جمع نشود. body نیز فقط هم‌زمان با خواندن برنامه دریافت می‌شود. به‌گفته نویسنده، این مسیر اضافی روی یک ماشین، هزینه یک کپی بیشتر از هر byte و چند ده microsecond کار برای هر request دارد؛ نکته اصلی معماری، استقلال edge از process‌های Python است.

هر release ترکیبی ثابت از کد، نسخه Python و تنظیمات، همراه با host و worker‌های آن است. هنگام reload،؜ controller یک release تازه کنار قبلی راه می‌اندازد و تنها پس از آماده‌شدن آن ترافیک را منتقل می‌کند. اگر import شکست بخورد، release قبلی همچنان پاسخ می‌دهد. همین جداسازی اجازه می‌دهد چند نسخه برنامه، حتی با نسخه‌های متفاوت Python،؜ هم‌زمان اجرا شوند. در اجرای تحت daemon،؜ edge یک process مستقل است و controller همراه admin API در daemon قرار می‌گیرد.

نکته کلیدی

در Muru،؜ reload را شروع یک release تازه بدان، نه تغییر release فعلی. به خاطر بسپار که انتقال ترافیک فقط پس از آماده‌شدن نسخه تازه انجام می‌شود و controller در مسیر request قرار ندارد.

خواندن مقاله اصلی (در زبانه‌ی جدید باز می‌شود)
برچسب‌ها (۴)