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

الكيانات والتعاريف
الكيانات الأساسية الواجب استخدامها:
- المستشعر - مسؤول عن التحقق من حالة أحد مؤشرات النظام ؛
- مراقب - يستجوب واحدًا أو عدة مستشعرات. يغير حالته اعتمادًا على القراءات الحالية لأجهزة الاستشعار ؛
- حاسبة الحالة - تحسب الحالة الحالية بناءً على سجل المقاييس.
- سجل الحالة - مجموعة من المؤشرات لكل من أجهزة الاستشعار التي تشير إلى وقت الاقتراع.
لكل تجريد ، هناك تطبيق أساسي ، وكذلك آليات للتوسع البسيط والمريح. لننظر فيها بمزيد من التفصيل.
الاستشعار
واجهة
IProbe الأساسية. توفر الطبقات التي تنفذ IProbe ، عند الطلب ، قيمة معلمات النظام التي يلاحظونها ، أو وحدة / خدمة معينة.
public interface IProbe { string Name { get; } Task<ProbeResult> Check(); }
نتيجة لطريقة
التحقق ، يقوم مثيل IProbe بإرجاع بنية ProbeResult
public struct ProbeResult { public string ProbeName { get; set; } public DateTime Time { get; set; } public bool Success { get; set; } public string Data { get; set; } public Exception Exception { get; set; } public override string ToString() => $"{Time}: {Success}"; }
حيث يحدد حقل النجاح ما إذا كان قد تم اختبار المعلمة بنجاح من وجهة نظر المستشعر ، ويمكن لحقول البيانات والاستثناء تخزين معلومات إضافية في حالة الضرورة للتصحيح أو التسجيل.
مراقب
الواجهة الأساسية لـ
ISpectator . يراقب النظام ، ويولد الأحداث في وقت تغيير حالة النظام لإخطار جميع الوحدات النمطية التي تم الاشتراك فيها. يستخدم مثيل لفئة يقوم بتنفيذ واجهة IStateEvaluator لحساب الحالة الحالية.
public interface ISpectator<TState> where TState : struct, IConvertible { event EventHandler<StateEventArgs<TState>> StateChanged; event EventHandler<HealthCheckEventArgs> HealthChecked; TState State { get; } TimeSpan Uptime { get; } string Name { get; } void AddProbe(IProbe probe); void CheckHealth(); }
أثناء كل استطلاعات من أجهزة الاستشعار ، يرفع المراقب الحدث StateChanged ويتم تمرير كائن من النوع
StateEventArgs إلى المشتركين في هذا الحدث ، والذي يحتوي على معلومات حول الحالة الحالية للنظام.
آلة حاسبة الدولة
واجهة
IEvaluator الأساسية. يحسب الحالة الحالية للنظام بناءً على سجل حالة المستشعر.
public interface IStateEvaluator<TState> { TState Evaluate( TState currentState, DateTime stateChangedLastTime, IReadOnlyCollection<JournalRecord> journal); }
سجل الدولة
مجموعة من مثيلات بنية JournalRecord. تقوم نسخة JournalRecord بتخزين معلومات حول كل أجهزة الاستشعار التي تم استطلاعها في وقت بدء الاستطلاع من قبل المراقب.
public struct JournalRecord { public JournalRecord(DateTime time, IEnumerable<ProbeResult> values) { Time = time; Values = values.ToImmutableList(); } public DateTime Time { get; set; } public IReadOnlyCollection<ProbeResult> Values { get; set; } public override string ToString() => $"{Time}: [{string.Join(",", Values)}]"; }
works كيف يعمل
يمكن وصف عملية حساب حالة النظام على النحو التالي: يمكن لكل جهاز استشعار مدمج في النظام تحديد إحدى معلمات النظام / الوحدة / الخدمة المرصودة. على سبيل المثال ، يمكن أن يحدد عدد الطلبات النشطة الخارجية إلى واجهة برمجة التطبيقات ، وكمية ذاكرة الوصول العشوائي المستخدمة ، وعدد الإدخالات في ذاكرة التخزين المؤقت ، إلخ.
يمكن تعيين كل مستشعر إلى مراقب واحد أو عدة مراقبين. يمكن لكل مراقب العمل مع واحد أو عدة أجهزة استشعار. يجب أن يقوم المراقب بتطبيق واجهة ISpectator وإنشاء أحداث في حالة حدوث تغيير في الحالة ، أو استطلاع أجهزة الاستشعار.
أثناء الفحص التالي لحالة النظام ، يستطلع المراقب جميع "مجساته" ، مما يشكل مجموعة للكتابة إلى سجل الحالة. إذا تغيرت حالة النظام ، يقوم المراقب بإنشاء حدث مناسب. يمكن لمشتركي الأحداث الذين يتلقون معلومات حول التغيير تغيير معلمات تشغيل النظام. في الوقت نفسه ، يمكن استخدام "أجهزة الكمبيوتر" من أنواع مختلفة لتحديد حالة النظام.
أوضاع التشغيل المتزامن وغير المتزامن
النظر في اثنين من السيناريوهات الرئيسية التي يبدأ فيها المراقب دراسة استقصائية عن "أجهزة الاستشعار".
وضع متزامن
يتم إجراء مسح للمحاسيس يتبعه إعادة حساب حالة النظام بسبب النداء المباشر لإحدى وحدات النظام للمراقب.
في هذه الحالة ، يعمل المراقب وأجهزة الاستشعار في نفس الخيط. يتم تنفيذ الحساب الخاطئ لحالة النظام كجزء من عملية داخل النظام.
يحتوي المشروع بالفعل على تنفيذ أساسي لهذا المراقب -
SpectatorBase .
عرض الكود public class SpectatorBase<TState> : ISpectator<TState> where TState : struct, IConvertible { private TState _state; private readonly IList<IProbe> _probes; private readonly IStateEvaluator<TState> _stateEvaluator; private readonly List<JournalRecord> _journal; private readonly ReaderWriterLockSlim _journalLock; private readonly ReaderWriterLockSlim _stateLock; private readonly Stopwatch _stopwatch; public event EventHandler<StateEventArgs<TState>> StateChanged; public event EventHandler<HealthCheckEventArgs> HealthChecked; public virtual TState State { get { _stateLock.EnterReadLock(); try { return _state; } finally { _stateLock.ExitReadLock(); } } } public TimeSpan Uptime => _stopwatch.Elapsed; public string Name { get; set; } public IReadOnlyCollection<JournalRecord> Journal { get { _journalLock.EnterReadLock(); try { return _journal; } finally { _journalLock.ExitReadLock(); } } } public DateTime StateChangedDate { get; private set; } public TimeSpan RetentionPeriod { get; private set; } public SpectatorBase(IStateEvaluator<TState> stateEvaluator, TimeSpan retentionPeriod, TState initialState) { RetentionPeriod = retentionPeriod; _state = initialState; StateChangedDate = DateTime.UtcNow; _stateEvaluator = stateEvaluator; _stopwatch = Stopwatch.StartNew(); _probes = new List<IProbe>(); _journal = new List<JournalRecord>(); _journalLock = new ReaderWriterLockSlim(); _stateLock = new ReaderWriterLockSlim(); } public void AddProbe(IProbe probe) => _probes.Add(probe); protected virtual void ChangeState(TState state, IEnumerable<string> failedProbes) { _stateLock.EnterWriteLock(); try { _state = state; } finally { _stateLock.ExitWriteLock(); } StateChangedDate = DateTime.UtcNow; StateChanged?.Invoke(this, new StateEventArgs<TState>(state, StateChangedDate, failedProbes)); } public virtual void CheckHealth() { var results = new Stack<ProbeResult>(); var tasks = _probes .Select(async o => { results.Push(await o.Check().ConfigureAwait(false)); }) .ToArray(); Task.WaitAll(tasks); var now = DateTime.UtcNow; _journalLock.EnterWriteLock(); try {
وضع غير متزامن
في هذه الحالة ، يحدث استجواب المستشعرات بشكل غير متزامن من عمليات النظام ويمكن إجراؤه في سلسلة منفصلة. التنفيذ الأساسي لمثل هذا المراقب هو بالفعل بالفعل مشروع -
AutomatedSpectator .
عرض الكود public class AutomatedSpectator<TState> : SpectatorBase<TState>, IAutomatedSpectator<TState> where TState : struct, IConvertible { public TimeSpan CheckHealthPeriod { get; } private readonly System.Timers.Timer _timer; public AutomatedSpectator( TimeSpan checkHealthPeriod, TimeSpan retentionPeriod, IStateEvaluator<TState> stateEvaluator, TState initialState) : base(stateEvaluator, retentionPeriod, initialState) { CheckHealthPeriod = checkHealthPeriod; _timer = new System.Timers.Timer(CheckHealthPeriod.TotalMilliseconds); _timer.Elapsed += (sender, args) => CheckHealth(); _timer.AutoReset = true; } public void Start() => _timer.Start(); }
▌ الخاتمة
ساعدني استخدام X.Spectator شخصيًا في العديد من المشاريع المحملة بدرجة كبيرة لزيادة استقرار عدد من الخدمات بشكل كبير. لقد أثبت أفضل إطار مقترح أنه يتم تنفيذه في الأنظمة الموزعة المبنية على أساس بنية الخدمات المصغرة. يستخدم خيار التكامل الأمثل مبدأ عكس التحكم ، أي تطبيق التبعيات ، عندما يتم تنفيذ عملية تطبيق المستشعرات باستخدام حاوية IoC ، ويتم تقديم المراقبين في شكل مفردات ، حيث يمكن الوصول إلى مثيل واحد لكل مراقب بواسطة فئات مختلفة من الوحدات والخدمات.
▌ الروابط والمعلومات المفيدة
→
مستودع المشروع→
أمثلة→
حزمة NuGet