ConnectionFisherClient
ConnectionFisherClient بخش کلاینت ConnectionFisherServer است. برای هر line ورودی، چند child line را همزمان به سمت next باز میکند و probe پنجبایتی FISH? را روی هرکدام میفرستد. نخستین child line که پاسخ معتبر FISH! بگیرد انتخاب میشود و بقیه بسته میشوند.
این node زمانی کاربرد دارد که مسیر خروجی پایدار نیست یا میخواهید چند اتصال را همزمان امتحان کنید و اولین مسیری را انتخاب کنید که رسیدن به ConnectionFisherServer متناظر را تأیید میکند.
جایگاه رایج
چیدمان سادهٔ TCP:
TcpListener -> ConnectionFisherClient -> TcpConnector
TcpListener -> ConnectionFisherServer -> TcpConnector
همراه با تونلهای stream دیگر در میانه مسیر:
TesterClient -> ConnectionFisherClient -> MuxClient -> MuxServer -> ConnectionFisherServer -> TesterServer
کلاینت و سرور باید دو سر یک مسیر منطقی stream باشند. هر تونل میانی باید بایتهای ثابت probe را بدون تغییر عبور دهد.
این نود چه میکند؟
- مقدار
settings.simultaneous-tries-perlineرا میخواند. - با هر
Initدر جهت upstream روی main line، به تعداد تنظیمشده child line داخلی میسازد. - upstream
Initرا روی هر child line بهnextمیفرستد. - probe ثابت
FISH?را روی هر child line میفرستد. - منتظر پاسخ ثابت
FISH!میماند. - تا زمان انتخاب یک child، payloadِ upstream روی main line را در صف نگه میدارد.
- اولین child که پاسخ معتبر دریافت کند را انتخاب میکند.
- همه child lineهای انتخابنشده را میبندد.
- payloadهای upstream صفشده را به ترتیب برای child انتخابشده میفرستد.
- payloadهای downstream از child انتخابشده را به main line برمیگرداند.
ConnectionFisherClient یک تونل میانی stream است. مستقیماً socket یا packet line نمیسازد.
نمونه تنظیم
{
"name": "fisher-client",
"type": "ConnectionFisherClient",
"settings": {
"simultaneous-tries-perline": 3
},
"next": "tcp-out"
}
سمت سرور متناظر:
{
"name": "fisher-server",
"type": "ConnectionFisherServer",
"next": "service"
}
فیلدهای اجباری
فیلدهای سطح اصلی:
| فیلد | نوع | توضیح |
|---|---|---|
name | string | نام یکتای node در config. |
type | string | باید دقیقاً "ConnectionFisherClient" باشد. |
next | string | اجباری. node stream بعدی که child lineهای نامزد را میگیرد. |
ConnectionFisherClient باید node قبلی نیز داشته باشد؛ این node ابتدا یا انتهای chain نیست.
تنظیمات اختیاری
| گزینه | نوع | پیشفرض | توضیح |
|---|---|---|---|
simultaneous-tries-perline | integer >= 1 | 2 | تعداد child lineهای نامزد که برای هر main line ورودی ساخته میشوند. |
settings میتواند حذف شود. مقادیر کمتر از 1 رد میشوند.
تبادل Probe
probe مقدار ثابتی دارد و secret قابل تنظیمی برای آن وجود ندارد:
| جهت | Bytes | معنی |
|---|---|---|
| کلاینت به سرور | FISH? | بلافاصله پس از Init در upstream، روی هر child line فرستاده میشود. |
| سرور به کلاینت | FISH! | پاسخ الزامی ConnectionFisherServer. |
این markerها فقط نشان میدهند که child line به ConnectionFisherServer متناظر رسیده است؛ هیچ احراز هویت، رمزنگاری یا تضمین صحتی برای ترافیک فراهم نمیکنند.
مدل Line
ConnectionFisherClient از دو نوع line استفاده میکند:
| Line | هدف |
|---|---|
| main line | line ورودی اصلی از node قبلی که در سمت previous باقی میماند. |
| child lines | lineهای نامزد داخلی که کلاینت میسازد و فقط به سمت next میروند. |
main line هیچگاه مستقیم به next فرستاده نمیشود و child lineها نیز هرگز به سمت prev نمیروند. پس از انتخاب یک child، تونل payload و callbackهای Est، Pause، Resume و Finish را بهصورت دستی میان main line و child منتخب جابهجا میکند.
جریان انتخاب
برای هر upstream Init روی main line:
- state مربوط به main line مقداردهی اولیه میشود.
- حداکثر
simultaneous-tries-perlinechild روی همان worker ساخته میشود؛ انتخاب همزمان تلاش تازه را متوقف میکند. - هر child line به سمت
nextمقداردهی اولیه میشود. - هر child پس از Init و با مجوز transport،
FISH?میفرستد. - اگر هنوز برندهای بهصورت همزمان انتخاب نشده باشد، timeout انتخاب
5000 msزمانبندی میشود. - تا مشخص شدن child برنده، payloadِ upstream روی main line در صف میماند.
- اولین child که
FISH!دریافت کند، child انتخابشده میشود. - child lineهای بازنده بسته میشوند.
- بایتهای همراه
FISH!پیش از پاسخهای جدید ناشی از داده برنامه به node قبلی فرستاده میشوند. - صف upstream بهترتیب و فقط تا وقتی transport Pause نکرده برای child منتخب تخلیه میشود.
اگر هیچ child پیش از timeout معتبر شناخته نشود، main line بسته میشود.
محدودیتها و Buffer
| محدودیت | مقدار | رفتار |
|---|---|---|
| Selection timeout | 5000 ms | اگر childای انتخاب نشود، main line بسته میشود. |
| Pending upstream payload | 2 MiB | اگر payload صفشده از این حد بگذرد، main line بسته میشود. |
| پاسخ ناقص child | 4 bytes | پاسخ کامل پنجبایتی بیدرنگ اعتبارسنجی میشود. |
| بدنه همراه پاسخ / ترتیب انتخاب | 2 MiB | بایتهای marker از سقف داده برنامه کم نمیکنند. |
| تعداد bufferهای pending upstream | 1,024 | ورودی TCP خالی entry اشغال نمیکند. |
required_padding_left | 0 | node header پروتکل را به bufferهای موجود اضافه نمیکند. |
برای probeِ FISH? یک buffer کوچک جداگانه گرفته میشود. payload کاربر در همان bufferهای WaterWall در صف میماند یا ارسال میشود.
رفتار Lifecycle
قبل از انتخاب:
- payload upstream از main line در صف قرار میگیرد
- payload downstream از child بهعنوان داده handshake در نظر گرفته میشود
- اولین Est transport از child عضو معتبر، مستقل از انتخاب و پیش از FISH!، یک بار به main line فرستاده میشود.
- childای که از Init با transport از قبل برقرار برگردد، همان اعلان یکباره را ایجاد میکند.
- فشار transport child پیش از انتخاب ذخیره میشود؛ اولین payload نگهداریشده مستقل از انتخاب، مبدأ main را Pause میکند
بعد از انتخاب:
- payloadِ upstream روی main line با
tunnelNextUpStreamPayloadبه child منتخب فرستاده میشود. - payloadِ downstream روی child منتخب با
tunnelPrevDownStreamPayloadبه node قبلی میرود. - انتخاب و تکمیل handshake هیچ Est دیگری ایجاد نمیکنند.
- Pause و resume فقط میان child منتخب و main line جابهجا میشوند.
اگر child منتخب بسته شود، main line نیز بسته میشود. اگر همه childها پیش از انتخاب برنده بسته شوند، main line هم بسته خواهد شد.
صف upstream برنامه با پذیرش transactional به 2 MiB و 1,024 buffer محدود میماند. پس از ثبت اولین payload در FIFO، Pause مبدأ برنامه منتشر میشود. این دلیل با فشار transport child منتخب جمع میشود؛ Resume یک child دیگر یا صرف انتخاب، آن را آزاد نمیکند. probe و پاسخ FISH همچنان اجازه پیشرفت دارند، چون این توقف فقط به مبدأ برنامه اعمال میشود. تخلیه پس از انتخاب یا Resume یک تولیدکننده مستقل است: Pause آن را متوقف میکند و ورودی re-entrant پشت داده قدیمی قرار میگیرد. Pause پاسخ main پیش از انتخاب ذخیره و پس از handshake به child منتخب اعمال میشود تا پاسخ FISH! را متوقف نکند. بایتهای همراه FISH! پیش از پاسخهای جدید ناشی از داده برنامه ارسال میشوند؛ صف موقت برای حفظ ترتیب downstream نیز سقف 2 MiB دارد. انتخاب همزمان ایجاد تلاشهای تازه را متوقف میکند و timeout غیرضروری ایجاد نمیشود.
نکتهها
- با
ConnectionFisherServerمتناظر استفاده کنید. - برای chainهای stream طراحی شده است، نه packet-line.
- افزایش
simultaneous-tries-perlineتعداد و بار اتصالها را نیز بالا میبرد. - probe ثابت و عمومی است؛ در مسیرهایی که به امنیت نیاز دارند از TLS یا nodeهای رمزنگاری، احراز هویت و مسیریابی استفاده کنید.
- upstream
Estو downstreamInitبرای این node غیرفعال هستند.
پشتیبانی از splice و بازنشستگی state راهاندازی
هر دو node، buffer عادی و payload موجود در pipe خصوصی را میپذیرند. هر delivery که وارد parsing
handshake یا ترتیبدهی هنگام انتخاب child شود، با sbufEnsureOrdinary() کامل به buffer عادی تبدیل
میشود. FIFO برنامه buffer را بدون خواندن محتوا نگه میدارد. marker کامل پنجبایتی میتواند تا 2 MiB
داده برنامه همراه داشته باشد؛ marker از سقف داده برنامه کم نمیکند و marker ناقص حداکثر چهار بایت دارد.
پس از راهاندازی و تخلیه FIFO، client فضای handshake و صف خالی را آزاد میکند، اما نگاشت main به child منتخب و کنترل جریان endpoint برای payload، فشار و Finish باقی میماند. server پس از اتمام Init سمت backend و replay، همه state خط را آزاد و صفر میکند و روی همان line برای همیشه bypass میشود. payload آماده بدون inspection، materialization یا سقف backlog راهاندازی عبور میکند. server همچنان backend را با نخستین payload برنامه مقداردهی میکند؛ childهای بازنده اتصال backend نمیسازند. برای TCP آماده، ترجیح خواندن عادی ثبت نمیشود.
متادیتای نود
metadata برگرفته از کد منبع:
| ویژگی | مقدار |
|---|---|
| node flags | kNodeFlagSupportsSplice |
can_have_prev | true |
can_have_next | true |
layer_group | kNodeLayer4 |
layer_group_prev_node | kNodeLayer4 |
layer_group_next_node | kNodeLayer4 |
required_padding_left | 0 bytes |