رفتن به محتوای اصلی
کوهستان
توضیح کوهستانپروتکل‌های شبکهپروتکل TCP/IP· 7 دقیقه مطالعه· در دیدگاه‌ها

برخورد تأییدهای تأخیری با بافر نِیگل: به دام افتادن مایکروسرویس‌ها در بن‌بست تأخیر ۲۰۰ میلی‌ثانیه‌ای

یک الگوریتم متعلق به سال ۱۹۸۴ برای ذخیره پهنای باند و مکانیزمی از سال ۱۹۸۹ برای کاهش ترافیک شبکه، در مراکز داده مدرن تداخلی فاجعه‌بار پدید آورده‌اند. وقتی مایکروسرویس‌های تعاملی بسته‌های کوچک داده را روی اتصالات پیش‌فرض TCP منتقل می‌کنند، این دو قاعده با هم برخورد کرده و جریمه‌ای ۲۰۰ میلی‌ثانیه‌ای را به اجبار بر هر رفت‌وبرگشت تحمیل می‌کنند.

به قلم بابک ناصری

به‌طور خلاصه

  • الگوریتم نیگل و تأیید تأخیری در دهه ۱۹۸۰ با هدف مدیریت ازدحام شبکه و کاهش سربار ترافیک پایه‌گذاری شدند.
  • ارسال پیاپی دو بسته خرد قبل از دریافت پاسخ، تلاقی میان این دو الگوریتم به وجود آورده و تأخیری ۲۰۰ میلی‌ثانیه‌ای را تحمیل می‌کند.
  • مایکروسرویس‌های امروزی به کرات در این تله گرفتار می‌شوند، به گونه‌ای که محیط‌های توسعه ترجیح می‌دهند الگوریتم نیگل را به صورت پیش‌فرض غیرفعال کنند.

در سال ۱۹۸۴، جان نیگل در شرکت «فورد ایرواسپیس» نشست و فروپاشی شبکه را به چشم دید. اینترنت نوپا در حال خفگی زیر بار بالاسری‌های خود بود؛ فلج‌شده بر اثر پدیده‌ای که او آن را «مسئله بسته‌های کوچک» نامید. مهندسان در حال ساخت برنامه‌هایی بودند که داده‌ها را با هر فشردن کلید ارسال می‌کرد، بی‌آنکه از فاجعه محاسباتی در حال تکوین باخبر باشند.[1]

محاسبه بسیار ساده و بی‌رحمانه بود. هر داده‌ای که روی اتصال پروتکل کنترل انتقال (TCP) ارسال می‌شود، نیازمند یک هدر ۴۰ بایتی برای مسیریابی تا مقصد است. هنگامی که یک کاربر در یک نشست Telnet تنها یک حرف را تایپ می‌کرد، شبکه یک بسته ۴۱ بایتی را تنها برای تحویل یک بایت بار واقعی منتقل می‌کرد.[1]

در پیوندهای ارتباطی کندِ آن نسل اولیه، این بسته‌های ریز انباشته شدند. شبکه نه با حجم داده‌ها، بلکه با پاکت‌های حامل آن اشباع شد. نیگل دریافت که اگر پشته شبکه مداخله نکند، برنامه‌ها همچنان سیم‌ها را با بارهای میکروسکوپی پر خواهند کرد تا جایی که زیرساخت کاملاً زمین‌گیر شود.[1]

راه‌حل او که با نام سند RFC 896 منتشر شد، یک منطق بافرینگ هوشمندانه بود که به الگوریتم نیگل معروف شد. قاعده سرراست بود: اگر فرستنده داده‌های تأییدنشده‌ای در مسیر ارسال دارد، باید هرگونه پیام خروجی جدید و کوچک را ذخیره کند. این پیام‌ها ارسال نمی‌شوند مگر اینکه تأییدیه بازگردد یا داده‌ای به اندازه یک بسته کامل جمع شود.[1]

«مسئله بسته‌های کوچک» که جان نیگل را وادار به تدوین RFC 896 کرد.

نیمه دوم تله

الگوریتم نیگل با موفقیت اینترنت اولیه را از فروپاشی ناشی از ازدحام نجات داد، اما این فقط نیمه نخست سازوکاری بود که توسعه‌دهندگان امروزی را آزار می‌دهد. پنج سال بعد، در سال ۱۹۸۹، «کارگروه مهندسی اینترنت» (IETF) سند RFC 1122 را منتشر کرد و بهینه‌سازی مکملی را با هدف کاهش مکالمات زاید شبکه معرفی نمود.[2]

این بهینه‌سازی دوم، «تأیید تأخیری» (Delayed Acknowledgment) نام داشت. طراحان متوجه شدند که ارسال یک بسته تأییدیه اختصاصی به ازای هر تکه داده دریافتی ناکارآمد است. استاندارد تصریح می‌کرد که «یک TCP باید تأیید تأخیری را پیاده‌سازی کند، اما تأییدیه نباید بیش از حد به تعویق بیفتد»، و حداکثر زمان انتظار را روی ۰.۵ ثانیه محدود کرد.[2]

در عمل، بیشتر سیستم‌های عامل این تأخیر را با یک تایمر ۲۰۰ میلی‌ثانیه‌ای پیاده کردند. گیرنده تأییدیه را نگه می‌داشت، با این امید که برنامه کاربردی پیش از انقضای تایمر پاسخی آماده کند. اگر پاسخی پدید می‌آمد، تأییدیه می‌توانست بر روی داده خروجی سوار شود (Piggybacking) و بدین ترتیب در مصرف یک بسته کامل صرفه‌جویی می‌شد.[2][4]

در نگاه مجزا، هر دو الگوریتم شاهکارهای بهینه‌سازی هستند. الگوریتم نیگل مانع از غرق شدن شبکه در بسته‌های ریز از سوی فرستنده می‌شود و تأیید تأخیری گیرنده را از ارسال انبوهی از تأییدیه‌های خالی بازمی‌دارد. اما هنگامی که هم‌زمان روی یک اتصال مشترک عمل می‌کنند، یک چرخه بازخورد ویرانگر به وجود می‌آورند.[3][6]

بن‌بست ۲۰۰ میلی‌ثانیه‌ای

برخورد زمانی رخ می‌دهد که یک برنامه کاربردی الگوی «نوشتن-نوشتن-خواندن» (write-write-read) را اجرا کند؛ توالی متداولی که در آن کلاینت ابتدا یک هدر می‌فرستد، سپس بدنه کوچکی را ارسال می‌کند و در نهایت منتظر پاسخ می‌ماند. این توالی دقیقاً نقاط کور هر دو الگوریتم را فعال کرده و داده‌ها را در یک بن‌بست ریاضی به دام می‌اندازد.[3][4]

الگوی نوشتن-نوشتن-خواندن برخوردی ایجاد می‌کند که ارتباط را تا زمان انقضای تایمر ۲۰۰ میلی‌ثانیه‌ای متوقف می‌سازد.

ماجرا از جایی آغاز می‌شود که کلاینت نخستین بسته کوچک را ارسال می‌کند. از آنجا که هیچ داده تأییدنشده‌ای در شبکه در جریان نیست، الگوریتم نیگل اجازه می‌دهد این بسته بلافاصله در شبکه عبور کند. سرور بسته را تحویل می‌گیرد، اما برنامه فوراً پاسخی برای بازگشت تولید نمی‌کند.[3]

در سمت سرور، سازوکار تأیید تأخیری وارد عمل می‌شود. پشته TCP سرور ارسال تأییدیه را نگه می‌دارد و تا ۲۰۰ میلی‌ثانیه صبر می‌کند تا شاید برنامه پاسخی بدهد تا تأییدیه همراه آن ارسال شود. هم‌زمان در سمت کلاینت، برنامه قصد ارسال دومین بسته کوچک را دارد.[3][4]

اینجاست که تله بسته می‌شود. پشته TCP کلاینت متوجه می‌شود که بسته نخست هنوز تأیید دریافت نکرده است. بر اساس قاعده نیگل، این پشته از ارسال دومین بسته کوچک امتناع کرده و آن را در بافر نگه می‌دارد. اکنون کلاینت منتظر تأییدیه مانده و سرور منتظر داده‌های بیشتر است.[1][3]

هیچ‌کدام کوتاه نمی‌آیند. شبکه به سکوت کامل فرو می‌رود. این بن‌بست ادامه دارد تا زمانی که تایمر ۲۰۰ میلی‌ثانیه‌ای تأیید تأخیری در سرور منقضی شود. تنها در این لحظه است که سرور تسلیم می‌شود و تأییدیه خالی را به سوی کلاینت می‌فرستد.[3][4]

عیب‌یابی گلوگاه نامرئی

سازوکار دقیق این نقص در سال ۲۰۰۵ توسط استوارت چشایر، مهندس شرکت اپل، در مقاله‌ای شاخص درباره مشکلات کارایی TCP مستندسازی شد. چشایر اثبات کرد که این بن‌بست ناشی از باگ برنامه‌نویسی در هیچ‌کدام از طرفین نیست، بلکه حاصل یک ناسازگاری بنیادین میان دو بهینه‌سازی کاملاً سالم است.[3]

جریمه ۲۰۰ میلی‌ثانیه‌ای در برابر تأخیر فیزیکی ناچیز شبکه در مراکز داده مدرن بسیار بزرگ است.

چشایر خاطرنشان کرد که لایه کاربردی کاملاً نسبت به این چانه‌زنی‌های لایه انتقال نابیناست. توسعه‌دهنده‌ای که کد سطح بالا می‌نویسد، صرفاً یک درخواست شبکه می‌بیند که ۲۰۰ میلی‌ثانیه بیش از حد طول کشیده و این امر غالباً به دیباگ آشفته و بیهوده منطق برنامه‌ای می‌انجامد که در واقع بی‌نقص کار می‌کند.[3]

به محض آنکه کلاینت تأییدیه با تأخیر را دریافت کرد، الگوریتم نیگل بسته دوم را آزاد کرده و تراکنش تکمیل می‌شود. اما خسارت وارد شده است. جریمه اجباری ۲۰۰ میلی‌ثانیه‌ای به چرخه رفت‌وبرگشت تزریق شده؛ تأخیری که هیچ ارتباطی به فاصله فیزیکی شبکه یا پهنای باند ندارد.[3][6]

بهای تحمیلی در دیتاسنتر مدرن

در سال ۱۹۸۴، تأخیر ۲۰۰ میلی‌ثانیه‌ای بهایی کاملاً منطقی برای نجات شبکه از فروپاشی بود. امروز، در مراکز داده مدرن که با پیوندهای فیبر نوری با ظرفیت گیگابیت بر ثانیه کار می‌کنند، این زمان معادل ابدیت است. این همان تفاوت بین یک رابط کاربری روان و یک برنامه کُند و پاسخ‌نداده است.[6]

معماری نرم‌افزارهای امروزی نادانسته این آسیب‌پذیری را تشدید کرده است. گذار به سوی مایکروسرویس‌ها یعنی یک کنش کاربری ساده ممکن است ده‌ها درخواست شبکه درون‌سازمانی را فعال کند. این سرویس‌ها غالباً از طریق REST API یا فراخوانی روال دوردست (RPC) ارتباط برقرار می‌کنند و داده‌های کوچک JSON ارسال می‌کنند که کاملاً با شرایط برخورد نیگل و تأیید تأخیری همخوانی دارد.[6]

اگر یک درخواست کاربر نیازمند پنج فراخوانی متوالی مایکروسرویس باشد و هر تماس در تله نوشتن-نوشتن-خواندن بیفتد، برنامه یک ثانیه کامل تأخیر ساختگی را تجربه می‌کند. مهندسان اغلب هفته‌ها وقت صرف تحلیل کوئری‌های پایگاه داده و بازنویسی کدها می‌کنند، غافل از آنکه لایه انتقال عمداً ترافیک آنان را متوقف کرده است.[6]

تصویرسازی: توسعه‌دهندگان برنامه‌های کاربردی غالباً بن‌بست لایه انتقال را به اشتباه به ناکارآمدی پایگاه داده یا منطق کند نرم‌افزار نسبت می‌دهند.

این مشکل به ویژه در پروتکل‌های درخواست-پاسخ بدون خط‌لوله مانند HTTP/1.1 روی اتصالات ماندگار مشهود است. وقتی کلاینت هدر HTTP را در یک دستور نوشتن و بدنه POST را در دستور بعدی ارسال می‌کند، برخورد عملاً قطعی است. لایه انتقال متوجه نمی‌شود که این دو عملِ نوشتن متعلق به یک درخواست منطقی واحد هستند.[4][6]

برای کاهش این مسئله، برخی مهندسان پیشنهاد بافر کردن داده‌ها در فضای کاربری (User Space) را می‌دهند. اگر برنامه هدر و بدنه را پیش از تحویل به سیستم‌عامل در یک بافر ادغام کند، پشته TCP یک نوشتن بزرگ دریافت می‌کند. این کار رضایت الگوریتم نیگل را جلب کرده و بن‌بست را دور می‌زند، هرچند نیازمند مدیریت حافظه دقیق است.[4]

دور زدن لایه انتقال

پذیرفته‌شده‌ترین راه‌حل عمومی برای این بن‌بست، غیرفعال‌سازی صریح الگوریتم نیگل است. سیستم‌های عامل سوکت‌آپشنی با عنوان TCP_NODELAY فراهم کرده‌اند که به پشته TCP فرمان می‌دهد فارغ از اندازه بسته یا داده‌های تأییدنشده، داده‌ها را فوراً ارسال کند.[4][5]

برای برنامه‌های تعاملی، پهنای باند صرفه‌جویی‌شده توسط الگوریتم نیگل در برابر کارایی از دست رفته بر اثر بن‌بست ناچیز است. با درک این واقعیت، بسیاری از محیط‌های برنامه‌نویسی امروزی پیش‌فرض تاریخی را تغییر داده‌اند. چنان‌که یک مهندس در سال ۲۰۲۲ نوشت، زبان Go با فعال‌سازی صریح TCP_NODELAY روی همه اتصالات، «سازش درستی ایجاد کرد، حتی اگر لحنی از جنس 'ما بهتر از شما می‌دانیم' در آن باشد».[5]

غیرفعال‌سازی تأیید تأخیری نیز از نظر فنی در برخی سیستم‌ها ممکن است. لینوکس پرچم TCP_QUICKACK را دارد که گیرنده را وادار به پاسخ فوری می‌کند. با این حال، از آنجا که توسعه‌دهندگان به‌ندرت کنترلی بر تنظیمات سیستم‌عامل سرورهای ثالث دارند، غیرفعال کردن الگوریتم نیگل در سمت کلاینت مطمئن‌ترین راهکار باقی می‌ماند.[4]

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

ماندگاری بن‌بست ۲۰۰ میلی‌ثانیه‌ای، سندی بر پایداری معماری اینترنت اولیه است. الگوریتم‌هایی که برای حفاظت از شبکه‌های آسیب‌پذیر و کم‌عرض طراحی شده بودند، همچنان در زیرساخت وب مدرن به کار خود ادامه می‌دهند و قواعد سال ۱۹۸۴ را به مایکروسرویس‌های امروز تحمیل می‌کنند.[6]

این تحلیل چگونه انجام شد

روش
ترکیب و استانداردسازی زمان‌بندی‌های ارسال بسته TCP و قوانین برهم‌کنش الگوریتمی برگرفته از RFC 896 و RFC 1122 به منظور بازسازی توالی دقیق وقایعی که بن‌بست ۲۰۰ میلی‌ثانیه‌ای را ایجاد می‌کنند.
یافته
قطعیت ریاضی که هر مایکروسرویس تعاملی با ارسال محموله‌های کوچک‌تر از حداکثر اندازه قطعه روی اتصال پیش‌فرض TCP، با جریمه اجباری ۲۰۰ میلی‌ثانیه‌ای در هر رفت‌وبرگشت مواجه خواهد شد؛ گلوگاهی ساختاری که جز با غیرفعال‌سازی صریح یکی از این دو الگوریتم برطرف نمی‌شود.
داده‌هایی که بر پایهٔ آن‌ها کار کردیم
  • شرط بافرسازی نیگل (انتظار برای تأییدیه اگر داده کمتر از MSS باشد): Hold data < MSS — IETF
  • مهلت زمانی تأیید تأخیری: Up to 500ms, typically 200ms — IETF
محدودیت‌های این تحلیل
این تحلیل پیکربندی‌های پیش‌فرض سوکت TCP را مبنا قرار داده و روش‌های دور زدن هسته یا پروتکل‌های مبتنی بر UDP نظیر QUIC را در نظر نمی‌گیرد.

اصطلاحات کلیدی

حداکثر اندازه سگمنت (MSS)
بزرگ‌ترین حجم داده به بایت که یک رایانه یا دستگاه ارتباطی می‌تواند در یک بسته بدون تکه‌تکه شدن تحویل بگیرد.
TCP_NODELAY
یک گزینه سوکت شبکه که الگوریتم نیگل را غیرفعال کرده و پشته شبکه را وادار به ارسال بلادرنگ داده‌ها بدون توجه به اندازه بسته می‌کند.
تأییدیه سواره (Piggybacking)
پیوست کردن تأییدیه دریافت TCP به یک بسته داده خروجی به جای ارسال جداگانه یک بسته تأییدیه مستقل.
الگوی نوشتن-نوشتن-خواندن
رفتاری در برنامه‌ها که در آن کلاینت دو داده متوالی را پیش از انتظار برای دریافت پاسخ سرور ارسال می‌کند و زمینه‌ساز بن‌بست تأخیر می‌شود.

پرسش‌های متداول

آیا این بن‌بست ترافیک UDP را نیز متأثر می‌کند؟

خیر. پروتکل UDP تحویل بسته‌ها را تضمین نمی‌کند، بنابراین نه از تأییدیه استفاده می‌کند و نه از بافر نیگل. پروتکل‌های مدرنی مانند QUIC که بر پایه UDP ساخته شده‌اند، این بن‌بست را به کلی دور می‌زنند.

آیا می‌توان مدت زمان تایمر ۲۰۰ میلی‌ثانیه‌ای را تغییر داد؟

در اغلب سیستم‌های عامل، تایمر تأیید تأخیری در سطح هسته هاردکد شده و به راحتی از فضای کاربری قابل تغییر نیست. در لینوکس می‌توان هسته را با مقدار HZ متفاوتی بازکامپایل کرد، اما تغییر پرچم‌های سوکت راه‌حلی بسیار امن‌تر است.

آیا دانلود فایل‌های حجیم نیز دچار این مشکل می‌شود؟

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

بررسی عمیق دیدگاه‌ها

محافظان پهنای باند

کارایی شبکه و کاهش بار بالاسری بسته‌ها همچنان از اولویت‌های حیاتی به شمار می‌رود.

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

مهندسان حداقل تأخیر

ارسال بلادرنگ داده‌ها برای برنامه‌های تعاملی مدرن امری حیاتی و غیرقابل چشم‌پوشی است.

سازندگان بازی‌های چندنفره، سامانه‌های بلادرنگ و بسترهای مایکروسرویس تأکید دارند که پهنای باند ارزان شده اما سرعت انتقال فیزیکی تابع محدودیت سرعت نور است. از این منظر، جریمه ۲۰۰ میلی‌ثانیه‌ای یک مانع ساختگی و غیرقابل پذیرش است و غیرفعال ساختن الگوریتم نیگل با TCP_NODELAY باید حداقل استاندارد هر خدمت تحت شبکه باشد.

اصول‌گرایان پروتکل

برنامه‌های کاربردی باید با قوانین لایه انتقال همگام شوند نه اینکه آن‌ها را دور بزنند.

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

مهندسان حداقل تأخیر 50%اصول‌گرایان پروتکل 30%محافظان پهنای باند 20%
مهندسان حداقل تأخیر
ارسال بلادرنگ داده‌ها برای برنامه‌های تعاملی مدرن امری حیاتی و غیرقابل چشم‌پوشی است.
اصول‌گرایان پروتکل
برنامه‌های کاربردی باید با قوانین لایه انتقال همگام شوند نه اینکه آن‌ها را دور بزنند.
محافظان پهنای باند
کارایی شبکه و کاهش بار بالاسری بسته‌ها همچنان از اولویت‌های حیاتی به شمار می‌رود.

دیدگاه‌هایی که این گزارش پوشش نداده

  • اپراتورهای شبکه تلفن همراه
  • تولیدکنندگان دستگاه‌های اینترنت اشیاء

منابع

پوشش منابع

6 منبع

3 دیدگاه شناسایی‌شده

مهندسان حداقل تأخیر 50%اصول‌گرایان پروتکل 30%محافظان پهنای باند 20%
  1. [1]IETFمحافظان پهنای باند

    RFC 896: Congestion Control in IP/TCP Internetworks

    مطالعه در IETF →
  2. [2]IETFمحافظان پهنای باند

    RFC 1122: Requirements for Internet Hosts - Communication Layers

    مطالعه در IETF →
  3. [3]Stuart Cheshireمهندسان حداقل تأخیر

    TCP Performance problems caused by interaction between Nagle's Algorithm and Delayed ACK

    مطالعه در Stuart Cheshire →
  4. [4]Wikipediaاصول‌گرایان پروتکل

    TCP delayed acknowledgment

    مطالعه در Wikipedia →
  5. [5]Hacker Newsمهندسان حداقل تأخیر

    Golang disables Nagle's Algorithm by default

    مطالعه در Hacker News →
  6. [6]تیم سردبیری کوهستاناصول‌گرایان پروتکل

    تحلیل تیم سردبیری کوهستان

    مطالعه در تیم سردبیری کوهستان →

نظرات

همیشه در جریان باشید

هر زاویه. هر روز.

اخبار دیدگاه‌ها با پوشش کامل منابع و تحلیل دیدگاه‌ها، هر روز و رایگان.