التصحيح كعملية

يُعتقد أن التطوير يستغرق حوالي 10٪ من الوقت ، والتصحيح يستغرق 90٪. ربما يكون هذا البيان مبالغًا فيه ، ولكن أي مطور سيوافق على أن تصحيح الأخطاء عملية تتطلب الكثير من الموارد ، خاصة في الأنظمة الكبيرة متعددة الخيوط.

وبالتالي ، يمكن أن يؤدي تحسين وتنظيم عملية التصحيح إلى فوائد كبيرة في شكل ساعات عمل موفرة ، مما يزيد من سرعة حل المشكلات ، وفي النهاية ، يزيد من ولاء المستخدمين.



اقترح سيرجي Shchegrikovich (dotmailer) في مؤتمر DotNext 2018 Piter النظر في تصحيح الأخطاء كعملية يمكن وصفها وتحسينها. إذا كنت لا تزال لا تملك خطة واضحة للعثور على الأخطاء - تحت مقطع الفيديو ونص نص تقرير سيرجي.

(وفي نهاية المنشور ، أضفنا جاذبية جون سكيت لجميع الشركات التابعة ، تأكد من البحث)



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

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

أدوات التصحيح



بالطبع ، لا يمكن تصحيح الأخطاء بدون تصحيح الأدوات المساعدة. المفضلة هي:

  • لدى Windbg ، بالإضافة إلى المصحح نفسه ، وظائف غنية لدراسة مقالب الذاكرة. إن تفريغ الذاكرة هو جزء من حالة العملية. يمكنك أن تجد فيه قيمة حقول الأشياء ، مكدسات الاستدعاء ، ولكن ، للأسف ، تفريغ الذاكرة ثابت.
  • PerfView هو منشئ ملفات تعريف مكتوب على أعلى تكنولوجيا ETW .
  • Sysinternals هي أداة مساعدة كتبها Mark Russinovich ، والتي تسمح لك بالحفر أكثر قليلاً في جهاز نظام التشغيل.

خدمة السقوط


لنبدأ بمثال من حياتي سأظهر فيه كيف تؤدي الطبيعة غير المنهجية لعملية التصحيح إلى عدم الكفاءة.

ربما حدث هذا للجميع ، عندما تأتي إلى شركة جديدة في فريق جديد لمشروع جديد ، ثم من اليوم الأول ترغب في تحقيق فوائد لا يمكن إصلاحها. لذلك كان معي. في ذلك الوقت ، كان لدينا خدمة تلقت html للإدخال ، وإخراج الصور للإخراج.

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



الخدمة لم تكن حاسمة بالنسبة لنا ويمكننا البقاء على قيد الحياة. لكنني انضممت إلى المشروع وكان أول شيء قررت فعله هو إصلاحه.

أين يذهب مطورو .NET إذا كان هناك شيء لا يعمل؟ يذهبون إلى EventViewer. ولكن هناك لم أجد أي شيء باستثناء السجل الذي سقطت فيه الخدمة. لم تكن هناك رسائل حول الخطأ الأصلي ، ولا مكدس استدعاء.



هناك أداة مثبتة لما يجب فعله بعد ذلك - نلف main بالكامل في try-catch .

 try { ProcessRequest(); } catch (Exception ex) { LogError(ex); } 

الفكرة بسيطة: ستعمل try-catch ، وسوف تزعجنا ، وسنقرأها ونصلح الخدمة. نقوم بترجمة ونشر للانتاج وتعطل الخدمة ولا يوجد خطأ. أضف catch آخر.

 try { ProcessRequest(); } catch (Exception ex) { LogError(ex); } catch { LogError(); } 

نكرر العملية: تعطل الخدمة ، لا توجد أخطاء في السجلات. آخر شيء يمكن أن يساعد هو في finally ، والذي يطلق عليه دائمًا.

 try { ProcessRequest(); } catch (Exception ex) { LogError(ex); } catch { LogError(); } finally { LogEndOfExecution(); } 

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

لقد مر أسبوعان.



نظرت في PerformanceMonitor ، حيث رأيت خدمة تتعطل ، ثم ترتفع ، ثم تنخفض مرة أخرى. تسمى هذه الحالة باليأس وتبدو كما يلي:



في مجموعة متنوعة من الملصقات ، هل تحاول معرفة مكان المشكلة حقًا؟ بعد عدة ساعات من التأمل ، تظهر المشكلة فجأة:



الخط الأحمر هو عدد المقابض الأصلية التي تمتلكها العملية. المقبض الأصلي هو مرجع لمورد نظام التشغيل: الملف ، التسجيل ، مفتاح التسجيل ، كائن المزامنة ، إلخ. بالنسبة لبعض التوليفات الغريبة للظروف ، فإن الانخفاض في نمو عدد المقابض يتزامن مع اللحظات التي سقطت فيها الخدمة. هذا يؤدي إلى فكرة أنه في مكان ما يوجد تسرب في المقابض.

نأخذ تفريغ ذاكرة ، نفتحه في WinDbg. نبدأ في تنفيذ الأوامر. دعنا نحاول رؤية قائمة انتظار الإنهاء لتلك الكائنات التي يجب تحريرها بواسطة التطبيق.

 0:000> !FinalizeQueue 

في نهاية القائمة ، وجدت مستعرض ويب.


الحل بسيط - خذ WebBrowser dispose به:

 private void Process() { using (var webBrowser = new WebBrowser()) { // Processing ... } } 

يمكن استخلاص استنتاجات هذه القصة على النحو التالي: أسبوعان طويلان للغاية وأطول من أن يعثروا على dispose غير مدعو ؛ أننا وجدنا حلًا للمشكلة - الحظ ، نظرًا لعدم وجود نهج محدد ، لم تكن هناك طبيعة منهجية.

بعد ذلك ، كان لدي سؤال: كيفية الظهور لأول مرة بشكل فعال وماذا أفعل؟

للقيام بذلك ، تحتاج إلى معرفة ثلاثة أشياء فقط:

  1. قواعد التصحيح
  2. خوارزمية للبحث عن الأخطاء.
  3. تقنيات التصحيح الاستباقية.

قواعد التصحيح


  1. كرر الخطأ.
  2. إذا لم تقم بإصلاح الخطأ ، فلن يتم إصلاحه.
  3. افهم النظام.
  4. تحقق من القابس.
  5. فرق تسد.
  6. انتعش.
  7. هذا هو الخطأ الخاص بك.
  8. خمسة لماذا.

هذه قواعد واضحة جدًا تصف نفسها.

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

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

افهم النظام. قال بريان كيرنيغان ذات مرة أنه إذا كنا أذكياء جدًا لكتابة هذا النظام ، فعلينا أن نكون أذكياء بشكل مضاعف لأول مرة.

مثال صغير للقاعدة. يرصد الرصد لدينا الرسوم البيانية:


هذا رسم بياني لعدد الطلبات التي تتم معالجتها بواسطة خدمتنا. بمجرد النظر إليها ، توصلنا إلى فكرة أنه سيكون من الممكن زيادة سرعة الخدمة. في هذه الحالة ، يرتفع الجدول ، قد يكون من الممكن تقليل عدد الخوادم.

يتم تحسين أداء الويب ببساطة: نأخذ PerfView ، ونشغلها على آلة الإنتاج ، وتزيل التتبع في غضون 3-4 دقائق ، ونأخذ هذا التتبع إلى الآلة المحلية ونبدأ في دراسته.

إحدى الإحصائيات التي يظهرها PerfView هي جامع القمامة.



بالنظر إلى هذه الإحصائيات ، رأينا أن الخدمة تنفق 85٪ من وقتها في جمع القمامة. يمكنك أن ترى في PerfView بالضبط حيث يتم قضاء هذا الوقت.



في حالتنا ، هذا يخلق سلاسل. يقترح التصحيح نفسه: نستبدل جميع السلاسل بـ StringBuilders. محليًا ، نحصل على زيادة في الإنتاجية بنسبة 20-30٪. نشر على الإنتاج ، راجع النتائج مقارنة بالجدول الزمني القديم:



لا تقتصر قاعدة "فهم النظام" فقط على فهم كيفية حدوث التفاعلات في نظامك ، وكيفية سير الرسائل ، ولكن حول محاولة تصميم نظامك.

في المثال ، يوضح الرسم البياني النطاق الترددي. ولكن إذا نظرت إلى النظام بأكمله من وجهة نظر نظرية قائمة الانتظار ، فقد اتضح أن إنتاجية نظامنا يعتمد على معلمة واحدة فقط - سرعة وصول الرسائل الجديدة. في الواقع ، لم يكن لدى النظام ببساطة أكثر من 80 رسالة في المرة الواحدة ، لذلك لا توجد طريقة لتحسين هذا الجدول الزمني.

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

تتعلق قاعدة "التحقق من المكونات" بالحقائق والبيانات. لا يبدأ التصحيح بتشغيل WinDbg أو PerfView على آلات الإنتاج ، بل يبدأ بالتحقق من الحقائق والبيانات. إذا كانت الخدمة لا تستجيب ، فقد لا تعمل.

فرق تسد. هذه هي القاعدة الأولى وربما الوحيدة التي تتضمن تصحيح الأخطاء كعملية. إنه يتعلق بالفرضيات وترويجها واختبارها.

لم ترغب إحدى خدماتنا في التوقف.



نفترض فرضية: ربما هناك دورة في المشروع تعالج شيئًا لا نهائيًا.

يمكنك اختبار الفرضية بطرق مختلفة ، أحد الخيارات هو أخذ ملف ذاكرة. نقوم بسحب مكدسات الاستدعاء من التفريغ وجميع سلاسل ~*e!ClrStack باستخدام الأمر ~*e!ClrStack . نبدأ في النظر ورؤية ثلاثة تيارات.







مؤشر الترابط الأول في الرئيسي ، والثاني في معالج OnStop() ، وكان مؤشر الترابط الثالث في انتظار بعض المهام الداخلية. وبالتالي ، فإن فرضيتنا ليست مبررة. لا يوجد حلقات ، كل الخيوط تنتظر شيئًا. الجمود على الأرجح.

خدمتنا تعمل على النحو التالي. هناك مهمتان - التهيئة والعمل. تفتح التهيئة اتصالاً بقاعدة البيانات ، ويبدأ العامل في معالجة البيانات. يحدث الاتصال بينهما من خلال علامة مشتركة ، والتي يتم تنفيذها باستخدام TaskCompletionSource .

نفترض الفرضية الثانية: ربما لدينا طريق مسدود من مهمة واحدة للمهمة الثانية. للتحقق من ذلك ، يمكنك رؤية كل مهمة على حدة من خلال WinDbg.



اتضح أن إحدى المهام سقطت ، والثانية لم تسقط. في المشروع رأينا الكود التالي:

 await openAsync(); _initLock.SetResult(true); 

هذا يعني أن مهمة التهيئة تفتح الاتصال وبعد ذلك يضبط TaskCompletionSource على true. ولكن ماذا لو وقع استثناء هنا؟ ثم ليس لدينا الوقت لتعيين SetResult على true ، لذلك كان الإصلاح لهذا الخطأ كما يلي:

 try { await openAsync(); _initLock.SetResult(true); } catch(Exception ex) { _initLock.SetException(ex); } 

في هذا المثال ، طرحنا فرضيتين: الحلقة اللانهائية والمأزق. تساعد قاعدة "فرق تسد" على توطين الخطأ. التقريبات المتتالية تحل مثل هذه المشاكل.

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

انتعش. هذه القاعدة هي أنك تحتاج فقط إلى النهوض من على الطاولة والمشي ، وشرب الماء ، والعصير أو القهوة ، وفعل أي شيء ، ولكن الأهم هو صرف الانتباه عن مشكلتك.

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

هذا هو الخطأ الخاص بك. سأقول عن هذه القاعدة بمثال.

حدثت مشكلة في AccessViolationException . بالنظر إلى مكدس الاستدعاءات ، رأيت أنه حدث عندما أنشأنا استعلام LinqToSql داخل عميل sql.



من هذا الخطأ كان من الواضح أنه في مكان ما يتم انتهاك سلامة الذاكرة. لحسن الحظ ، في ذلك الوقت استخدمنا بالفعل نظام إدارة التغيير. نتيجة لذلك ، بعد ساعتين أصبح من الواضح ما حدث: قمنا بتثبيت .Net 4.5.2 على آلات الإنتاج لدينا.



وفقًا لذلك ، نرسل الخطأ إلى Microsoft ، ويفحصونه ، ونتواصل معهم ، ويصلحون الخطأ في .Net 4.6.1.



بالنسبة لي ، أدى هذا إلى 11 شهرًا من العمل مع دعم Microsoft ، بالطبع ، ليس كل يوم ، ولكن الأمر استغرق 11 شهرًا من البداية لإصلاحه. بالإضافة إلى ذلك ، أرسلنا لهم عشرات الجيجابايت من مقالب الذاكرة ، وضعنا المئات من التجمعات الخاصة للقبض على هذا الخطأ. وطوال هذا الوقت ، لم نتمكن من إخبار عملائنا بأن مايكروسوفت هي المسؤولة ، وليس نحن. لذلك ، فإن الخطأ هو لك دائمًا.

خمسة لماذا. نحن في شركتنا نستخدم مرونة. مرونة جيدة لتجميع السجل.

جئت للعمل في الصباح ، والأكاذيب المرنة.



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

الأسئلة "لماذا؟" يساعد في العثور على جذر المشكلة. في المثال ، يمكننا إيقاف المسار الصحيح عدة مرات ، ولكن الإصلاح الكامل يبدو كالتالي: نقوم بتحديث المكون الإضافي ، وبدء الخدمات ، وزيادة الذاكرة وتقديم ملاحظة للمستقبل ، في المرة القادمة ، عند إضافة عقد جديدة إلى الكتلة ، نحتاج إلى التأكد من وجود ذاكرة كافية على Master العقد

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

خوارزمية التصحيح


للمرة الأولى ، قرأت عن خوارزمية التصحيح في تطبيقات تصحيح أخطاء كتاب جون روبنز. يصف عملية التصحيح كما يلي:



هذه الخوارزمية مفيدة لحلقتها الداخلية - العمل مع الفرضية.

مع كل دورة ، يمكننا التحقق من أنفسنا: هل نعرف المزيد عن النظام أم لا؟ إذا طرحنا فرضيات ، تحقق ، أنها لا تعمل ، ولا نتعلم أي شيء جديد حول تشغيل النظام ، فقد حان الوقت على الأرجح للتحديث. سؤالان حاليان في هذه المرحلة: أي الفرضيات التي قمت باختبارها والفرضية التي تختبرها الآن.

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

لدي مثال جيد لهذه الخوارزمية. وقع استثناء على إحدى خدمات الويب لدينا.



فكرنا الأول ليس مشكلتنا. ولكن وفقًا للقواعد ، لا تزال هذه مشكلتنا.

أولا ، كرر الخطأ. لكل ألف طلب ، هناك StructureMapException واحدة تقريبًا ، حتى نتمكن من إعادة إظهار المشكلة.

ثانيًا ، نحن نحاول وصف المشكلة: إذا قدم المستخدم طلب http لخدمتنا في الوقت الذي تحاول فيه StructureMap إنشاء تبعية جديدة ، فسيحدث استثناء.

ثالثًا ، نفترض أن برنامج StructureMap عبارة عن غلاف ، وهناك شيء بداخله يثير استثناءًا داخليًا. نقوم باختبار الفرضية باستخدام procdump.exe.

 procdump.exe -ma -e -f StructureMap w3wp.exe 

اتضح أن الداخل هو NullReferenceException .



من خلال دراسة مكدس الاستدعاءات لهذا الاستثناء ، نتفهم أنه يحدث داخل أداة إنشاء الكائنات في StructureMap نفسها.



لكن NullReferenceException ليست هي المشكلة نفسها ، ولكن النتيجة. أنت بحاجة إلى فهم مكان حدوثه ومن الذي يقوم بإنشائه.

طرحنا الفرضية التالية: لسبب ما ، ترجع شفرتنا تبعية فارغة. بالنظر إلى أنه في .Net يتم وضع جميع الكائنات في الذاكرة واحدة تلو الأخرى ، إذا نظرنا إلى الكائنات الموجودة في الكومة التي تقع قبل NullReferenceException ، فربما تشير إلى الرمز الذي طرح الاستثناء.

في WinDbg يوجد أمر - قائمة الكائنات القريبة !lno . يظهر أن الكائن الذي نهتم به هو وظيفة لامدا ، والتي يتم استخدامها في التعليمات البرمجية التالية.

 public CompoundInterceptor FindInterceptor(Type type) { CompoundInterceptop interceptor; if (!_analyzedInterceptors.TryGetValue(type, out interceptor)) { lock (_locker) { if (!_analyzedInterceptors.TryGetValue(type, out interceptor)) { var interceptorArray = _interceptors.FindAll(i => i.MatchesType(type)); interceptor = new CompoundInterceptor(interceptorArray); _analyzedInterceptors.Add(type, interceptor); } } } return interceptor; } 

في هذا الكود ، نتحقق أولاً مما إذا كانت القيمة في Dictionary _analyzedInterceptors في _analyzedInterceptors ، إذا لم نجدها ، ثم نضيف قيمة جديدة داخل lock .

من الناحية النظرية ، لا يمكن لهذا الرمز أن يعيد قيمة فارغة. لكن المشكلة هنا هي في _analyzedInterceptors ، التي تستخدم _analyzedInterceptors عاديًا في بيئة متعددة الخيوط ، وليس ConcurrentDictionary .

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

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

التصحيح الاستباقي


في جوهره ، يجيب التصحيح الاستباقي على السؤال "ماذا يحدث عندما يظهر خطأ."



يمكن رؤية أهمية تقنيات التصحيح الاستباقية في مخطط دورة حياة الخطأ.



تكمن المشكلة في أنه كلما طال عمر الخطأ ، زادت الموارد (الوقت) الذي نقضيه عليه.

تركز قواعد تصحيح الأخطاء وخوارزمية التصحيح على اللحظة التي يتم فيها العثور على الخطأ ويمكننا معرفة ما يجب فعله بعد ذلك. في الواقع ، نريد تحويل تركيزنا في الوقت الذي تم فيه إنشاء الخطأ. أعتقد أننا يجب أن نفعل الحد الأدنى من المنتج القابل للازالة (MDP) ، أي منتج يحتوي على الحد الأدنى الضروري من البنية التحتية لتصحيح الأخطاء بكفاءة في الإنتاج.

يتكون MDP من شيئين: وظيفة اللياقة البدنية وطريقة الاستخدام.

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

في حالة MDP ، تعتبر وظيفة اللياقة البدنية اختبار تصحيح الأخطاء. يمكنك استخدام أي شيء تريده لكتابة مثل هذه الاختبارات: NUnit ، MSTest ، وما إلى ذلك. ولكن ، نظرًا لأن التصحيح غالبًا ما يعمل مع أدوات خارجية ، فسأثبت استخدام Pester (إطار اختبار وحدة powerhell) كمثال. زائد هنا هو أنه يعمل بشكل جيد مع سطر الأوامر.

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

 Describe 'Debuggability' { It 'Contains line numbers in PDBs' { Get-ChildItem -Path . -Recurse -Include @("*.exe", "*. dll ") ` | ForEach-Object { &symchk.exe /v "$_" /s "\\network\" *>&1 } ` | Where-Object { $_ -like "*Line nubmers: TRUE*" } ` | Should -Not –BeNullOrEmpty } } 

يتحقق هذا الاختبار من أنه تم إعطاء جميع أحرف pdb إلى خادم الرمز وتم إعطاؤها بشكل صحيح ، أي تلك التي تحتوي على أرقام الأسطر بداخلها. للقيام بذلك ، خذ النسخة المجمعة من الإنتاج ، واعثر على جميع ملفات exe و dll ، وقم بتمرير جميع هذه الثنائيات من خلال الأداة المساعدة syschk.exe ، المضمنة في أدوات التصحيح لحزمة windows. تقوم الأداة المساعدة syschk.exe بفحص الملف الثنائي باستخدام خادم الرمز ، وإذا عثرت على ملف pdb هناك ، فإنها تطبع تقريرًا عنه. في التقرير ، نبحث عن السطر "أرقام الأسطر: TRUE". وفي النهاية نتحقق من أن النتيجة ليست "فارغة أو فارغة".

يجب دمج هذه الاختبارات في خط أنابيب النشر المستمر. بعد اجتياز اختبارات التكامل واختبارات الوحدة ، يتم إطلاق وظائف اللياقة البدنية.

سوف أعرض مثالاً آخر مع التحقق من المكتبات الضرورية في الكود.

 Describe 'Debuggability' { It 'Contains package for logging' { Get-ChildItem -Path . -Recurse -Name "packages.config" ` | ForEach-Object { Get-Content "$_" } ` | Where-Object { $_ -like "*nlog*" } ` | Should -Not –BeNullOrEmpty } } 

في الاختبار ، نأخذ جميع ملفات package.config ونحاول العثور على مكتبات nlog فيها. وبالمثل ، يمكننا التحقق من استخدام حقل معرف الارتباط داخل حقل nlog.

طرق الاستخدام. آخر شيء يتكون من MDP هو المقاييس التي تحتاج إلى جمعها.

سأوضح من خلال مثال طريقة USE ، التي تم تعميمها بواسطة Brendan Gregg.الفكرة بسيطة: إذا كان هناك أي مشكلة في الكود ، يكفي أخذ ثلاثة مقاييس: الاستخدام (التشبع) ، الأخطاء (الأخطاء) ، مما سيساعد على فهم مكان المشكلة.

تقوم بعض الشركات ، على سبيل المثال Circonus (التي تراقب البرامج بشكل سلس) ، ببناء لوحات التحكم الخاصة بها على شكل مقاييس معينة.



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

إذا كنت تأخذ ميزة تجارية ، فعلى الأرجح ، يمكنك تمييز ثلاثة مقاييس فيها:

  • الاستخدام - طلب وقت المعالجة.
  • التشبع هو طول قائمة الانتظار.
  • أخطاء - أي حالات استثنائية.

كمثال ، دعنا نلقي نظرة على رسم بياني لعدد الطلبات المعالجة التي يقوم بها أحد أنظمتنا. كما ترى ، لم تعالج الخدمة الطلبات للساعات الثلاث الماضية.



الفرضية الأولى التي قدمناها هي أن الخدمة سقطت ونحن بحاجة إلى إعادة تشغيلها. عند التحقق ، اتضح أن الخدمة تعمل ، فهي تستخدم 4-5 ٪ من وحدة المعالجة المركزية.



الفرضية الثانية هي أن الخطأ يقع داخل الخدمة التي لا نراها. سنستخدم الأداة etrace.

 etrace --kernel Process ^ --where ProcessName=Ex5-Service ^ --clr Exception 

تسمح لك الأداة بالاشتراك في أحداث ETW في الوقت الفعلي وعرضها على الشاشة.



نرى أنه ينخفض OutOfMemoryException. ولكن ، السؤال الثاني ، لماذا لا يوجد في السجلات؟ الجواب سريع - نعترضه ، نحاول تنظيف الذاكرة ، ننتظر قليلاً ونبدأ العمل مرة أخرى.

 while (ShouldContinue()) { try { Do(); } catch (OutOfMemoryException) { Thread.Sleep(100); GC.CollectionCount(2); GC.WaitForPendingFinalizers(); } } 

الفرضية التالية هي أن شخصًا ما يأكل الذاكرة بالكامل. وفقًا لتفريغ الذاكرة ، توجد معظم الكائنات في ذاكرة التخزين المؤقت.

 public class Cache { private static ConcurrentDictionary<int, String> _items = new ... private static DateTime _nextClearTime = DateTime.UtcNow; public String GetFromCache(int key) { if (_nextClearTime < DateTime.UtcNow) { _nextClearTime = DateTime.UtcNow.AddHours(1); _items.Clear(); } return _items[key]; } } 

يظهر الرمز أنه يجب مسح ذاكرة التخزين المؤقت كل ساعة. لكن الذاكرة لم تكن كافية ، حتى أنهم لم يصلوا إلى التنظيف. لنلقِ نظرة على مثال لمقياس ذاكرة التخزين المؤقت للاستخدام.



وفقًا للجدول الزمني ، فإنه مرئي على الفور - زادت الذاكرة ، وبدأت الأخطاء على الفور.

لذا ، استنتاجات حول ما هو التصحيح الاستباقي.

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

فكيف لإصلاح الخلل بكفاءة؟


  1. استخدم التصحيح الاستباقي.
  2. اتبع الخوارزمية.
  3. اختبار الفرضيات.



هذه المرة الراعي لإعلاننا هو جون سكيت. حتى لو لم تكن ذاهبًا إلى موسكو من أجل DotNext الجديد ، فإن الفيديو يستحق نظرة (حاول جون بجد).

Source: https://habr.com/ru/post/ar420793/


All Articles