TcpConnector
TcpConnector adapter خروجی TCP است. line را از node قبلی میگیرد، مقصد را از تنظیمات node یا routing context انتخاب میکند، اتصال TCP را باز میکند و payloadها را میان chain و server راه دور جابهجا میسازد.
جایگاه رایج:
TcpListener -> ... -> TcpConnector -> سرویس TCP راه دور
این node معمولاً انتهای یک stream chain است.
قابلیتها
- انتخاب آدرس و پورت مقصد برای هر line.
- resolve کردن نام domain از طریق یک
DomainResolverداخلی و async. - باز کردن یک socket TCP خروجی.
- نوشتن payloadهای upstream از node قبلی روی socket راه دور.
- فرستادن دادههای خواندهشده از socket راه دور بهعنوان payload downstream به node قبلی.
- فرستادن downstream
Estپس از برقراری موفق اتصال TCP خروجی. - اعمال گزینههای socket مانند
TCP_NODELAY،TCP_FASTOPEN، اندازه bufferها،SO_MARK، device binding و source-IP binding در صورت پشتیبانی platform.
TcpConnector در انتهای chain قرار میگیرد، با upstream Init راه میافتد و به node next نیاز ندارد.
نمونه تنظیم
{
"name": "outbound-tcp",
"type": "TcpConnector",
"settings": {
"address": "example.com",
"port": 443,
"nodelay": true,
"fastopen": false,
"large-send-buffer": true,
"large-recv-buffer": 4194304,
"fwmark": 10,
"interface": "eth0",
"source-ip": "192.0.2.10",
"domain-strategy": "prefer-ipv4"
}
}
نمونه چند مقصد وزندار
{
"name": "outbound-tcp",
"type": "TcpConnector",
"settings": {
"addresses": [
{
"address": "192.0.2.10",
"port": 443,
"weight": 5,
"nodelay": true,
"interface": "eth0",
"domain-strategy": "prefer-ipv4"
},
{
"address": "example.org",
"port": "dest_context->port",
"weight": 1,
"source-ip": "198.51.100.10",
"domain-strategy": "only-ipv4"
},
{
"address": "2001:db8:1::/64",
"port": 443,
"weight": 1,
"interface": null,
"source-ip": null,
"domain-strategy": "only-ipv6"
}
],
"nodelay": true,
"fastopen": false,
"large-send-buffer": true,
"large-recv-buffer": true,
"interface": "eth0",
"domain-strategy": "prefer-ipv4"
}
}
برای هر line جدید، یکی از objectهای addresses با احتمالی متناسب با weight آن انتخاب میشود. گزینههایی که در object مقصد نوشته نشدهاند از سطح بالای settings به ارث میرسند. برای interface و source-ip میتوانید با مقدار null، مقدار بهارثرسیده را فقط برای همان مقصد غیرفعال کنید.
فیلدهای ضروری
فیلدهای سطح بالا:
| فیلد | نوع | توضیح |
|---|---|---|
name | string | نام دلخواه node. باید داخل فایل config یکتا باشد. |
type | string | باید دقیقاً "TcpConnector" باشد. |
settings | object | تنظیمات مقصد و socket. |
settings باید دقیقاً از یک سبک مقصد استفاده کند:
| سبک | فیلدهای لازم | توضیح |
|---|---|---|
| مقصد تکی | address و port | هر line از همان قانون مقصد استفاده میکند. |
| مقصدهای وزندار | addresses | برای هر line یک مقصد بر اساس وزن انتخاب میشود. |
addresses را با address یا port top-level ترکیب نکنید.
انتخاب مقصد
مقصد تکی
از address و port مستقیماً زیر settings استفاده کنید.
{
"settings": {
"address": "93.184.216.34",
"port": 443
}
}
مقادیر معتبر برای address:
| مقدار | رفتار |
|---|---|
| string IPv4 | اتصال به آن آدرس IPv4. |
| string IPv6 | اتصال به آن آدرس IPv6. |
| string domain | resolve کردن domain پیش از اتصال. |
"src_context->address" | استفاده از آدرس source در routing context مربوط به line. |
"dest_context->address" | استفاده از آدرس destination در routing context مربوط به line. |
نام domain ثابت باید بین ۱ تا ۲۵۵ بایت باشد.
مقادیر معتبر برای port:
| مقدار | رفتار |
|---|---|
| عدد | اتصال به آن پورت ثابت. باید 1 تا 65535 باشد. |
"src_context->port" | استفاده از پورت source در routing context مربوط به line. |
"dest_context->port" | استفاده از پورت destination در routing context مربوط به line. |
random(x,y) در parser فعلی برای TcpConnector پشتیبانی نمیشود.
مقصدهای weighted
برای انتخاب یک مقصد وزندار بهازای هر line از addresses استفاده کنید.
{
"settings": {
"addresses": [
{
"address": "192.0.2.10",
"port": 443,
"weight": 3
},
{
"address": "198.51.100.20",
"port": 443,
"weight": 1
}
]
}
}
هر object باید شامل باشد:
| فیلد | نوع | توضیح |
|---|---|---|
address | string | همان فرمهای معتبر address در مقصد تکی. |
port | عدد یا string خاص | همان فرمهای معتبر port در مقصد تکی. |
weight | عدد صحیح مثبت | وزن نسبی این مقصد هنگام انتخاب. |
هر object میتواند گزینههای زیر را هم برای همان مقصد تغییر دهد:
| گزینه |
|---|
nodelay |
fastopen |
large-send-buffer |
large-recv-buffer |
fwmark |
interface |
source-ip |
domain-strategy |
parser برای سازگاری کلید قدیمی و غلطنویسیشده adresses را هم میپذیرد، اما در config جدید از addresses استفاده کنید و هر دو نام را همزمان ننویسید.
تنظیمات اختیاری
| گزینه | نوع | پیشفرض | توضیح |
|---|---|---|---|
nodelay | boolean | true | فعالسازی TCP_NODELAY روی socketهای خروجی. |
fastopen | boolean | false | در صورت پشتیبانی platform، TCP_FASTOPEN را درخواست میکند. |
large-send-buffer | boolean یا عدد مثبت | false، یا true اگر mux وجود داشته باشد و حذف شده باشد | تنظیم SO_SNDBUF. |
large-recv-buffer | boolean یا عدد مثبت | false، یا true اگر mux وجود داشته باشد و حذف شده باشد | تنظیم SO_RCVBUF. |
fwmark | integer | تنظیم نشده | اعمال socket mark به سبک Linux از طریق SO_MARK در صورت پشتیبانی. |
interface | string | تنظیم نشده | محدود کردن socketهای خروجی به یک network device محلی در صورت پشتیبانی. |
source-ip | string | تنظیم نشده | socket خروجی را با یک source port موقت به source IP محلی مشخص bind میکند. |
domain-strategy | string یا integer | dns.domain-strategy در core | نحوه انتخاب نتایج DNS برای مقصدهای domain. |
هنگام استفاده از addresses میتوانید این فیلدها را در سطح بالای settings بهعنوان پیشفرض بنویسید؛ هر object مقصد میتواند مقدار خودش را جایگزین کند.
گزینههای socket buffer
large-send-buffer و large-recv-buffer مقادیر زیر را میپذیرند:
| مقدار | معنا |
|---|---|
true | استفاده از buffer بزرگ پیشفرض WaterWall برای socket؛ در حال حاضر 16777216 بایت. |
false | تنظیم نکردن صریح اندازه buffer و استفاده از مقدار پیشفرض kernel. |
| عدد مثبت | درخواست مستقیم آن تعداد بایت. |
اگر chain نهایی شامل MuxClient یا MuxServer باشد، گزینههای حذفشدهی buffer
اندازهی پیشفرض 16 MiB را صریحاً درخواست میکنند، مستقل از core misc.tcp-tune
و موفقیت اجرای sysctl. در نبود Mux در chain، گزینههای حذفشده به kernel واگذار
میشوند. مقادیر صریح، از جمله false، در هر دو حالت اولویت دارند.
مقصدهای TcpConnector برای گزینههای حذفشده مقدار مؤثر سطح اصلی را به ارث میبرند.
domain strategy
domain-strategy مشخص میکند وقتی DNS هم IPv4 و هم IPv6 برمیگرداند، کدام آدرس برای مقصد domain انتخاب شود.
اگر این گزینه را ننویسید، مقدار dns.domain-strategy در core به کار میرود. در نبود آن هم WaterWall از "prefer-ipv4" استفاده میکند.
مقادیر معتبر string:
| مقدار | رفتار |
|---|---|
"prefer-ipv4" | نخستین نتیجه IPv4 را انتخاب میکند و اگر وجود نداشت به IPv6 برمیگردد. |
"prefer-ipv6" | نخستین نتیجه IPv6 را انتخاب میکند و اگر وجود نداشت به IPv4 برمیگردد. |
"only-ipv4" | فقط نتیجه IPv4 را میپذیرد؛ نبودن IPv4 یعنی resolution برای آن line قابل استفاده نیست. |
"only-ipv6" | فقط نتیجه IPv6 را میپذیرد؛ نبودن IPv6 یعنی resolution برای آن line قابل استفاده نیست. |
"accept-dns-returned-order" | نخستین آدرس قابل استفاده در ترتیب بازگشتی resolver را انتخاب میکند. |
مقادیر integer قدیمی هم پذیرفته میشوند:
| مقدار | استراتژی |
|---|---|
0 | accept-dns-returned-order |
1 | prefer-ipv4 |
2 | prefer-ipv6 |
3 | only-ipv4 |
4 | only-ipv6 |
در addresses وزندار، هر object میتواند domain-strategy مخصوص lineهایی را تعریف کند که همان مقصد را انتخاب کردهاند.
interface، source IP و egress pinning
interface socket خروجی را به یک device محلی محدود میکند.
در Linux، WaterWall در صورت امکان از SO_BINDTODEVICE استفاده میکند. روی platformهای بدون device binding، socket به آدرس IPv4 مربوط به interface bind میشود. اگر آدرسی پیدا نشود، راهاندازی اتصال شکست میخورد.
source-ip socket خروجی را قبل از connect به یک source IP محلی مشخص bind میکند. خانواده آدرس باید با خانواده مقصد مطابقت داشته باشد.
اگر loop protection در TunDevice یک egress pin خودکار ایجاد کرده باشد و interface را ننوشته باشید، TcpConnector از همان pin استفاده میکند. source-ip بهتنهایی آن interface را تغییر نمیدهد؛ یا source IP را از همان interface انتخاب کنید، یا interface را صریحاً بنویسید.
نمونههای رایج
مقصد راه دور ثابت
{
"name": "out",
"type": "TcpConnector",
"settings": {
"address": "example.com",
"port": 443,
"domain-strategy": "prefer-ipv4"
}
}
اتصال به destination context موجود
{
"name": "context-out",
"type": "TcpConnector",
"settings": {
"address": "dest_context->address",
"port": "dest_context->port"
}
}
این حالت پس از nodeهایی کاربرد دارد که metadata مقصد را میخوانند، بازنویسی یا مسیریابی میکنند.
bind به یک آدرس محلی مشخص
{
"name": "egress-ip-a",
"type": "TcpConnector",
"settings": {
"address": "203.0.113.10",
"port": 443,
"source-ip": "192.0.2.10"
}
}
استخر egress weighted
{
"name": "egress-pool",
"type": "TcpConnector",
"settings": {
"addresses": [
{
"address": "edge-a.example.net",
"port": 443,
"weight": 4,
"interface": "eth0"
},
{
"address": "edge-b.example.net",
"port": 443,
"weight": 1,
"interface": "eth1"
}
],
"domain-strategy": "prefer-ipv4"
}
}
رفتار chain
هنگام upstream Init، TcpConnector state مربوط به line را مقداردهی میکند، قانون مقصد را برمیگزیند، address و port را در destination context میگذارد و domain strategy را تنظیم میکند.
جریان داخلی:
- تنظیمات مقصد شامل
address،port، گزینههای socket و اطلاعات اختیاری CIDR randomization انتخاب میشوند. DomainResolverداخلی مقصد را در صورت domain بودن resolve میکند.- پس از تبدیل مقصد به IP و port،
TcpConnectorsocket خروجی را میسازد و اتصال async را آغاز میکند. - با موفقیت اتصال، downstream
Estبه node قبلی فرستاده میشود. - payloadهای upstream روی socket نوشته میشوند و دادههای خواندهشده از socket به payload downstream تبدیل میشوند.
اگر آمادهسازی مقصد، DNS resolution، ساخت socket یا اتصال ناموفق باشد، line در جهت downstream و به سمت node قبلی finish میشود.
domain resolution
resolve کردن domain بهشکل async انجام میشود. TcpConnector یک DomainResolver داخلی میسازد و پیش از resolution، domain-strategy انتخابشده را در destination context ذخیره میکند.
payloadهای رسیده در زمان انتظار برای DNS نزد resolver میمانند. payloadهایی که پس از موفقیت DNS و پیش از برقراری TCP میرسند نیز تا آماده شدن socket در TcpConnector صف میشوند.
CIDR randomization روی IPهای ثابت
یک address ثابت IP میتواند پسوند CIDR داشته باشد. در این حالت TcpConnector پیش از اتصال، host part را بهصورت تصادفی انتخاب میکند.
{
"settings": {
"address": "198.51.100.0/24",
"port": 443
}
}
{
"settings": {
"address": "2001:db8:1::/64",
"port": 443
}
}
پسوندهای CIDR فقط روی آدرسهای IP ثابت معتبر هستند. روی domainها، "src_context->address" یا "dest_context->address" معتبر نیستند.
قوانین پیادهسازی:
| خانواده آدرس | طول prefix معتبر | توضیح |
|---|---|---|
| IPv4 | 0 تا 32 | /0 هر ۳۲ بیت آدرس را تصادفی میکند؛ /32 آدرس را ثابت نگه میدارد. |
| IPv6 | 64 تا 128 | /64 هر ۶۴ بیت بخش host را با حفظ prefix شبکه تصادفی میکند؛ /128 آدرس را ثابت نگه میدارد. |
پسوند باید فقط شامل رقمهای دهدهی باشد. prefix خالی، نامعتبر یا خارج از بازه رد میشود. prefix طولانیتر فقط بیتهای باقیماندهٔ بخش host را تصادفی میکند.
کنترل جریان و buffering
در حالی که DNS، connect یا socket writeها در انتظار هستند، TcpConnector ممکن است payloadهای upstream را صف کند.
آستانههای فعلی:
| وضعیت صف | رفتار |
|---|---|
رسیدن حجم یا هزینهٔ ظرفیت صف به 1 KiB | node قبلی pause میشود. |
| تکمیل writeهای pending | node قبلی resume میشود. |
عبور حجم یا هزینهٔ ظرفیت صف از 16 MiB | اتصال بسته و line finish میشود. |
پیش از ارسال Pause، buffer، حسابداری صف و وضعیت اعلان ثبت میشوند تا callback تودرتو ترتیب FIFO را تغییر ندهد.
حجم منطقی و هزینهٔ ظرفیت buffer (شامل sbuf، padding و alignment) هرکدام سقف مستقل 16 MiB دارند؛ مقدار برابر سقف پذیرفته میشود.
با رسیدن هرکدام به 1 KiB، Pause ارسال میشود. buffer خالی نیز هزینهٔ ظرفیت دارد.
بودجهٔ TCP صف adapter و buffer فعال WIO را با هم حساب میکند. نوشتن جزئی تا زمان آزاد شدن buffer، هزینهٔ ظرفیت آن را کم نمیکند.
عبور از سقف، همان اتصال را از طریق مالک line میبندد.
این آستانهها نمیگذارند هنگام کند بودن server راه دور یا آماده نبودن اتصال، مصرف حافظه بیحد رشد کند.
مهلت برقراری اتصال
هر تلاش برای برقراری اتصال خروجی TCP یک مهلت 60 ثانیهای دارد که با پیشفرض
proxy_connect_timeout در NGINX هماهنگ است. این مهلت پس از Resolve شدن آدرس و
با آغاز تلاش اتصال TCP شروع میشود و پس از برقراری اتصال لغو میشود. خطاهای فوری
اتصال همچنان میتوانند line را زودتر ببندند. این مهلت از idle timeout اولیهی
listener و محدودیت پاکسازی اتصالهای بیفعالیت connector جداست؛ TcpConnector
مرحلهی idle جداگانهای برای انتظار اولین payload ندارد.
idle timeout
هر اتصال خروجی در جدول idle همان worker دنبال میشود. timeout فعلی نزدیک به 600 ثانیه است و هر read یا write آن را تمدید میکند.
این محدودیت دهدقیقهای برای پاکسازی اتصالهای بیفعالیت با timeout فعال پیشفرض
TcpListener هماهنگ است. اگر مقصد زودتر اتصال را ببندد، finish بدون انتظار
تا پایان مهلت idle به سمت downstream فرستاده میشود. این مقدار سقف NGINX نیست؛
اگر مقصد fallback بیفعالیتی طولانیتری را مجاز بداند، محدودیت idle هر دو adapter
باید آن را پوشش دهد.
اگر اتصال منقضی شود، socket بسته میشود و downstream finish به node قبلی فرستاده میشود.
نکات و هشدارها
TcpConnectorیک chain end خروجی است و بهnextنیاز ندارد.- DNS resolution async است و از
domain-strategyانتخابشده استفاده میکند. fwmark،TCP_FASTOPENو device binding به platform وابسته هستند.fwmarkروی Windows در دسترس نیست.source-ipباید یک آدرس IP معتبر باشد و باید با خانواده آدرس مقصد مطابقت داشته باشد.- کلید قدیمی
adressesبرای سازگاری پذیرفته میشود، اماaddressesنامگذاری مستندشده است.
متادیتای node
متادیتای برگرفته از کد منبع:
| ویژگی | مقدار |
|---|---|
| node flags | kNodeFlagChainEnd | kNodeFlagSupportsSplice |
can_have_prev | true |
can_have_next | false |
layer_group | kNodeLayer4 |
layer_group_prev_node | kNodeLayer4 |
layer_group_next_node | kNodeLayerNone |
required_padding_left | 0 بایت |