پرش به مطلب اصلی

KeepAliveServer

KeepAliveServer بخش سرور KeepAliveClient است. فریم‌های keepalive را که از کلاینت و در جهت upstream می‌رسند decode می‌کند، payload‌های عادی را به next می‌فرستد، به فریم ping با pong پاسخ می‌دهد و payload‌های downstream را در همان قالب فریم قرار می‌دهد.

این node را فقط به‌عنوان peer متناظر KeepAliveClient به کار ببرید. ورودی upstream آن باید فریم‌شده باشد و به‌تنهایی بایت‌های معمولی و بدون فریم payload را تشخیص نمی‌دهد.

KeepAliveServer یک تونل عادی و قابل ترکیب است. socket یا object‌ای از نوع line_t نمی‌سازد، line را آزاد نمی‌کند و timer دوره‌ای ندارد.

جایگاه رایج​

جفت مستقیم داخل یک process:

TesterClient -> KeepAliveClient -> KeepAliveServer -> TesterServer

چیدمان TCP روی دو سرور:

client side: TcpListener -> KeepAliveClient -> TcpConnector
server side: TcpListener -> KeepAliveServer -> TcpConnector

هر تونل میان کلاینت و سرور باید بایت‌های فریم keepalive را بدون تغییر عبور دهد.

این نود چه می‌کند؟​

  • با دریافت Init در upstream، state محلی line را مقداردهی اولیه می‌کند.
  • بایت‌های فریم‌شده upstream را تا کامل شدن فریم‌ها buffer می‌کند.
  • فریم‌های عادی upstream را decode و payload آن‌ها را به next می‌فرستد.
  • به فریم ping در upstream، با فرستادن فریم pong در downstream پاسخ می‌دهد.
  • pong frame‌های upstream را نادیده می‌گیرد.
  • فریم‌های عادی خالی و type‌های ناشناخته را دور می‌اندازد.
  • payload‌های downstream را به‌عنوان keepalive frame عادی encode می‌کند و به node قبلی می‌فرستد.
  • payload‌های downstream بزرگ‌تر از 1 MiB را به چند frame عادی تقسیم می‌کند.
  • state محلی line را از بین می‌برد و Finish را در همان جهت منتشر می‌کند.

سرور timer جداگانه‌ای برای keepalive ندارد؛ KeepAliveClient مسئول ساخت ping‌های دوره‌ای است.

نمونه تنظیم​

{
"name": "ka-server",
"type": "KeepAliveServer",
"settings": {},
"next": "service"
}

سمت کلاینت متناظر:

{
"name": "ka-client",
"type": "KeepAliveClient",
"settings": {
"ping-interval": 60000
},
"next": "transport"
}

فیلدهای اجباری​

فیلدهای سطح اصلی:

فیلدنوعتوضیح
namestringنام یکتای node در config.
typestringباید دقیقاً "KeepAliveServer" باشد.
settingsobjectاختیاری. پیاده‌سازی فعلی تنظیم اختصاصی برای سرور ندارد.
nextstringدر استفاده عادی اجباری است. node بعدی payload‌های decodeشده upstream را می‌گیرد.

KeepAliveServer باید node قبلی داشته باشد و توسط KeepAliveClient متناظر قابل دسترس باشد.

تنظیمات​

KeepAliveServer در حال حاضر تنظیم معنی‌داری ندارد. زمان‌بندی keepalive در KeepAliveClient با settings.ping-interval تنظیم می‌شود.

فرمت Frame​

سرور از همان prefix پنج‌بایتی client استفاده می‌کند:

Bytesفیلدتوضیح
0..3body lengthعدد صحیح unsigned و 32-bit با ترتیب big-endian. این طول شامل بایت type فریم به‌علاوه بایت‌های payload است.
4frame kind1 payload عادی، 2 ping، یا 3 pong.

نوع‌های frame:

KindمعنیBody payload
1payload عادیبایت‌های payload اصلی WaterWall.
2pingدر ترافیک keepalive عادی خالی است.
3pongپاسخ خالی به یک ping frame.

طول body شامل بایت kind هم می‌شود. هر فریم حداکثر 1 MiB (1,048,576 بایت) payload دارد و حداکثر طول بدنه 1,048,577 است. payload‌های downstream بزرگ‌تر به چند فریم تقسیم می‌شوند. هر دو peer از همین prefix پنج‌بایتی استفاده می‌کنند.

رفتار جهت‌ها​

Incoming callbackرفتار
upstream Initbuffer خواندن را مقداردهی اولیه می‌کند و سپس Init را به next می‌فرستد.
upstream Payloadبایت‌ها را ورودی فریم‌شده KeepAliveClient در نظر می‌گیرد و payload‌های عادی decodeشده را به next می‌فرستد.
upstream Est / Pause / Resumeبه next فرستاده می‌شوند.
upstream Finishstate محلی را از بین می‌برد و سپس Finish را به next می‌فرستد.
downstream Payloadpayload را در یک یا چند فریم عادی encode و به node قبلی ارسال می‌کند. buffer payload‌های خالی برای استفاده مجدد برمی‌گردد.
downstream Est / Pause / Resumeبه node قبلی فرستاده می‌شوند.
downstream Finishstate محلی را از بین می‌برد و سپس Finish را به node قبلی می‌فرستد.
downstream Initغیرفعال؛ رسیدن به این callback به‌عنوان chain flow نامعتبر fatal در نظر گرفته می‌شود.

پردازش ورودی Framed​

بایت‌های upstream در یک splice_stream_t جمع می‌شوند؛ بنابراین ممکن است یک فریم در چند callbackِ payload تکه‌تکه برسد یا چند فریم در یک callback دریافت شوند.

رفتار decoder محافظه‌کارانه است:

  • وقتی prefix یا body کامل نرسیده، منتظر بایت‌های بعدی می‌ماند.
  • اگر طول body فریم خارج از بازهٔ 1..1,048,577 باشد، line از طریق owner بسته می‌شود
  • فریم‌های عادی خالی دور ریخته می‌شوند.
  • type‌های ناشناخته فریم دور ریخته می‌شوند.
  • فریم‌های کامل ابتدا تخلیه می‌شوند؛ اگر پس از compaction هزینهٔ تخصیص همچنان بیش از سقف باشد، line بسته می‌شود

یادداشت‌های Buffer و Padding​

KeepAliveServer هنگام encode کردن payload‌های downstream، prefix پنج‌بایتی frame را prepend می‌کند، بنابراین metadata این node چنین است:

required_padding_left = 5
layer_group = kNodeLayer4
layer_group_next_node = kNodeLayer4
layer_group_prev_node = kNodeLayer4

caller باید padding کافی در سمت چپ برای prefix فراهم کند؛ Debug این پیش‌شرط را assert می‌کند. محاسبه padding در chain این فضای اعلام‌شده را رزرو می‌کند.

نکته‌های عملی​

  • این node را با KeepAliveClient جفت کنید؛ به‌تنهایی مفید نیست.
  • فریم‌های ping و pong داخلی جفت KeepAlive هستند و به‌عنوان payload به node‌های اطراف داده نمی‌شوند.
  • سرور به ping‌ها پاسخ می‌دهد، اما health سمت client را اندازه نمی‌گیرد یا pong timeout اعمال نمی‌کند.
  • این node بایت‌های payload را رمزنگاری، احراز هویت، فشرده یا بازنویسی نمی‌کند.
  • برای استفادهٔ مطمئن، transport باید بایت‌های فریم‌شده را بین کلاینت و سرور بدون تغییر نگه دارد.

متادیتای نود​

metadata برگرفته از کد منبع:

ویژگیمقدار
node flagskNodeFlagSupportsSplice
can_have_prevtrue
can_have_nexttrue
layer_groupkNodeLayer4
layer_group_prev_nodekNodeLayer4
layer_group_next_nodekNodeLayer4
required_padding_left5 bytes

پشتیبانی از splice​

KeepAliveServer پرچم kNodeFlagSupportsSplice را دارد. framing در تمام طول اتصال ادامه دارد. decoder از cache پنج‌بایتی splice_stream_t استفاده می‌کند و encoder فقط header پنج‌بایتی را در padding واقعی سمت چپ می‌نویسد. بدنهٔ payload می‌تواند در pipe باقی بماند. payload بزرگ‌تر از ۱ MiB با عملیات range متناسب با نوع buffer تقسیم می‌شود. شکست تخصیص pipe یا فشار ظرفیت می‌تواند fallback کامل به buffer عادی ایجاد کند. فریم‌های کنترلی buffer عادی دارند.

ورودی re-entrant encoder پشت suffix فعال می‌ماند و مجموع آن‌ها به ۸ MiB بایت منطقی و ۱٬۰۲۴ buffer، با احتساب suffix فعال، محدود است. decoder ورودی nested را به ترتیب پردازش می‌کند؛ بایت‌های nested سقف ۸ MiB و هزینهٔ تخصیص باقی‌مانده سقف ۱۶ MiB دارند. پیش از رد هزینهٔ اضافی، ابتدا compaction به buffer عادی، در صورت کاهش هزینه، امتحان می‌شود. فریم‌های کامل پیش از بررسی هزینهٔ نگهداری تخلیه می‌شوند. اعتبارسنجی طول فریم تضمین می‌کند که remainder ناقص، با احتساب header، کوچک‌تر از ۱ MiB + ۵ بایت باشد. Pause فوری منتقل می‌شود و پردازش batch همگامِ پذیرفته‌شده را قطع نمی‌کند. Pause و Resume تکراری نیز شفاف منتقل می‌شوند و Pong آماده منتظر Resume نمی‌ماند. parser ورودی Ping بازگشتی را پشت پاسخ فعال و با همان محدودیت‌های ذخیره‌سازی نگه می‌دارد؛ صف مستقلی برای Pong وجود ندارد. framing تا پایان اتصال لازم است؛ بنابراین این node فعال می‌ماند و bypass دائمی نمی‌شود.

طول بدنهٔ صفر یا بیش از ۱ MiB + ۱، شکست admission یا سرریز ذخیره‌سازی، line از نوع borrowed را از طریق owner می‌بندد. Finish، فریم ناقص، suffix فعال encoder و صف ورودی re-entrant را آزاد می‌کند؛ این node خودش line_t را destroy نمی‌کند.