أشعل النار في الطريق للبقاء على قيد الحياة

غالبًا ما تمثل زيادة نشاط تبادل البيانات بين الخدمات الدقيقة مشكلة في بنية حلول تكنولوجيا المعلومات الحديثة. اضغط على الحد الأقصى والبقاء على قيد الحياة بأي ثمن - وهو تحد خطير لأي تطوير. لذلك ، فإن البحث عن الحلول المثلى هو عملية لا تتوقف. توضح المقالة لفترة وجيزة المشاكل التي قد تنشأ عند استخدام طلبات http المحملة للغاية وطرق تجاوزها.


تبدأ هذه القصة بخطأ. بطريقة ما ، أجرينا اختبار الحمل ، والذي كان العنصر الرئيسي منه هو تنفيذ عدد كبير من طلبات http القصيرة. عميل مكتوب تحت netcore 2.2 ، يبدأ من نقطة ما ، يطرح System.Net.Sockets.SocketException: العنوان قيد الاستخدام بالفعل . أصبح من الواضح بسرعة أن الموانئ لم يكن لديها الوقت لتحريرها على العميل ، وفي مرحلة ما تم رفض النظام لفتح منفذ جديد. الآن ، إذا ذهبنا إلى التعليمات البرمجية ، كانت المشكلة في استخدام النهج القديم مع فئة HttpWebRequest والبناء:


var request = WebRequest.CreateHttp(uri);
using(var resp = request.GetResponse()){ … }

يبدو أننا نحرر المورد ، ويجب تحرير الميناء في الوقت المناسب. ومع ذلك ، أشارت netstat إلى زيادة سريعة في عدد المنافذ في حالة TIME_WAIT. تعني هذه الحالة انتظار إغلاق الاتصال (وربما تلقي البيانات المفقودة). نتيجة لذلك ، قد يكون المنفذ فيه لمدة 1-2 دقيقة. تمت مناقشة هذه المشكلة بشيء من التفصيل في العديد من المقالات ( مشاكل في قائمة انتظار TIME_WAIT ، محفوظات حول TIME_WAIT ). ومع ذلك ، هذا يعني أن دوت نت تحاول "بصدق" إغلاق الاتصال ، ويحدث بالفعل بالفعل من خلال خطأ إعدادات المهلة في النظام.


لماذا يحدث هذا وكيف يتم التعامل معه


لن أتحدث عن البقاء على قيد الحياة . يمكنك أن تقرأ عنها بنفسك. الهدف من المقالة هو محاولة الالتفاف حول أشعل النار ، وضعت بعناية على مسار المطور. وفقًا لـ msdn ، تكون خاصية KeepAlive لفئة HttpWebRequest صحيحة بشكل افتراضي . أي ، طوال هذا الوقت ، "خدع" HttpWebRequest الخادم ، وعرض عليه أن يحافظ على اتصال ، وبعد ذلك يقوم هو نفسه بقطعه. لكي أكون أكثر دقة ، لم يرسل HttpWebRequest بالإعدادات الافتراضية رأس "اتصال: إبقاء على قيد الحياة" ، فهذا الوضع فقط مضمّن في معيار HTTP / 1.1. أول شيء يجب تجربته هو تعطيل KeepAlive بالقوة. إذا قمت بتعيين HttpWebRequest.KeepAlive = false ، فسيظهر في الرأس "اتصال: إغلاق" في الطلب. من المسلم به أن هذا حل المشكلة تمامًا على منصة الاختبار. كخادم ، تم تكوين nginx بصفحة ثابتة.


تم اختبار الكود التالي:


while (true)
{
  var request = WebRequest.CreateHttp(uri);
  request.KeepAlive = false;
  var resp = await request.GetResponseAsync();
  using (var sr = new StreamReader(resp.GetResponseStream()))
  {
    var content = sr.ReadToEnd();
  }
}

ومع ذلك ، عند محاولة البدء على أجهزة الخادم ، تحت الأحمال الثقيلة (أكثر من 1000 طلب في الثانية) ، بدأ هذا الرمز مرة أخرى في إنتاج نفس الأخطاء. الآن فقط كانت المنافذ في حالة CLOSE_WAIT ، LAST_ACK. هذه هي حالات إغلاق الاتصال قبل النهائية عندما ينتظر العميل تأكيدًا من بادئ الإغلاق. يشير مثل هذا السلوك إلى أن العميل يبدأ في "الاختناق" مع الاتصالات المفتوحة حديثًا.


لا تغلق ، أعد الاستخدام


, , . keep-alive HttpClient. .


, , ? keep-alive nginx:


  • keepalive_timeout – ( 15)
  • keepalive_requests – ( 100)

netstat wireshark, . keepalive_requests (> 1000) , .



http , . . , , keep-alive. keep-alive , .


:


  • RunHttpClient – HttpClient "Connection: keep-alive"
  • RunHttpClientClosed – HttpClient "Connection: closed"
  • RunWebRequestClosed - يستخدم فئة HttpWebRequest "اتصال: مغلق"

تم تكوين خادم nginx بالمعلمات التالية:


  • keepalive_timeout 60s ؛
  • طلبات keepalive_ 100000 ؛

طريقةنالإعلاناتيعني
Runhttpclient10001963.3 مللي ثانية
RunWebRequestClosed100013857.4 مللي ثانية
RunHttpClientClosed100011،612.4 مللي ثانية
Runhttpclient10،00019573.9 مللي ثانية
RunWebRequestClosed10،000137،947.4 مللي ثانية
RunHttpClientClosed10،000116،112.9 مللي ثانية

All Articles