X.Spectator - pemantauan negara dalam .NET



Saat ini, sebagian besar sistem informasi adalah solusi kompleks dengan arsitektur yang agak kompleks dan banyak saling ketergantungan. Selama pengoperasian sistem tersebut, pada saat beban puncak, beberapa modul mungkin gagal, atau mungkin tidak berfungsi dengan benar. Dalam hal ini, sistem berhenti menjadi stabil dan mungkin berhenti memproses semua permintaan yang masuk dengan benar. Untuk memastikan operasi sistem yang stabil, berbagai strategi dapat diimplementasikan.

Dalam beberapa kasus (jika ini diizinkan), terjemahan sistem menjadi apa yang disebut. "Mode aman". Dalam mode ini, sistem dapat memproses lebih banyak panggilan dengan sedikit penurunan keakuratan hasil pemrosesan kueri. Dalam kasus lain, penskalaan sumber daya komputasi dan meningkatkan jumlah modul sistem individual yang bertanggung jawab untuk memproses permintaan yang masuk dapat membantu. Dalam hal ini, tugas yang paling penting adalah menentukan keadaan sistem , tergantung pada yang mana, satu atau tindakan lain harus sudah diambil untuk menstabilkan pekerjaannya.

Dalam proyek-proyek saya, saya telah memecahkan masalah serupa dengan berbagai cara selama beberapa waktu. Sejak saya masih di universitas, saya memiliki proyek kecil yang ditulis dalam. NET 4.0, dari mana dari waktu ke waktu saya mengambil potongan kecil kode untuk proyek nyata. Saya telah lama merencanakan untuk memperbaiki proyek ini, membersihkannya dan mengaturnya dalam bentuk kerangka kerja terpisah yang memungkinkan kita untuk dengan indah dan minimalis menyelesaikan masalah pemantauan keadaan sistem. Setelah menghabiskan beberapa malam dan beberapa hari libur, saya menata kode ini dan mempostingnya di GitHub . Selanjutnya, saya mengusulkan untuk mempertimbangkan secara lebih rinci apa dan bagaimana diimplementasikan dalam proyek ini.

Untuk menentukan perlunya mengubah mode operasi sistem, diusulkan untuk menggunakan pendekatan menggunakan "sensor" dan "pengamat" virtual.



Entitas dan definisi


Entitas dasar yang akan digunakan:

  • Sensor - bertanggung jawab untuk memeriksa status salah satu indikator sistem;
  • Pengamat - menginterogasi satu, atau beberapa sensor. Mengubah kondisinya tergantung pada pembacaan sensor saat ini;
  • Kalkulator status - menghitung kondisi saat ini berdasarkan log metrik.
  • Status log - satu set indikator untuk masing-masing sensor yang menunjukkan waktu pemungutan suara.

Untuk setiap abstraksi, ada implementasi dasar, serta mekanisme untuk ekspansi sederhana dan nyaman. Mari kita pertimbangkan secara lebih rinci.

Sensor


Antarmuka IProbe dasar. Kelas yang menerapkan IProbe memberikan, berdasarkan permintaan, nilai parameter sistem yang mereka amati, atau modul / layanan tertentu.

public interface IProbe { string Name { get; } Task<ProbeResult> Check(); } 

Sebagai hasil dari metode Periksa , instance IProbe mengembalikan struktur 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}"; } 

di mana bidang Sukses menentukan apakah parameter diuji dengan sukses dari sudut pandang sensor, dan bidang Data dan Pengecualian dapat menyimpan informasi tambahan jika perlu untuk debugging, atau logging.

Pengamat


Antarmuka dasar ISpectator . Ini memonitor sistem, menghasilkan peristiwa pada saat mengubah keadaan sistem untuk memberi tahu semua modul yang berlangganan acara ini. Ia menggunakan instance kelas yang mengimplementasikan antarmuka IStateEvaluator untuk menghitung keadaan saat ini.

  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(); } 

Selama setiap jajak pendapat sensor, pengamat meningkatkan acara StateChanged, dan objek tipe StateEventArgs diteruskan ke pelanggan acara ini, yang berisi informasi tentang kondisi sistem saat ini.

Kalkulator negara


Antarmuka IEvaluator dasar. Menghitung kondisi sistem saat ini berdasarkan log status sensor.

  public interface IStateEvaluator<TState> { TState Evaluate( TState currentState, DateTime stateChangedLastTime, IReadOnlyCollection<JournalRecord> journal); } 

Log negara


Kumpulan contoh struktur JournalRecord. Contoh JournalRecord menyimpan informasi tentang semua sensor yang disurvei pada saat polling diprakarsai oleh pengamat.

  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)}]"; } 

Cara kerjanya


Proses perhitungan keadaan sistem dapat digambarkan sebagai berikut: masing-masing sensor yang tertanam dalam sistem dapat menentukan salah satu parameter dari sistem / modul / layanan yang diamati. Misalnya, dapat memperbaiki jumlah permintaan aktif eksternal ke API, jumlah RAM yang digunakan, jumlah entri dalam cache, dll.

Setiap sensor dapat ditugaskan untuk satu atau beberapa pengamat. Setiap pengamat dapat bekerja dengan satu atau beberapa sensor. Pengamat harus mengimplementasikan antarmuka ISpectator dan menghasilkan peristiwa jika terjadi perubahan status, atau pemungutan suara sensor.

Selama pemeriksaan selanjutnya dari keadaan sistem, pengamat mengumpulkan semua "sensor" -nya, membentuk sebuah array untuk menulis ke log negara. Jika keadaan sistem telah berubah, pengamat menghasilkan peristiwa yang sesuai. Pelanggan acara yang menerima informasi tentang perubahan dapat mengubah parameter operasi sistem. Pada saat yang sama, "komputer" dari berbagai jenis dapat digunakan untuk menentukan keadaan sistem.

Mode operasi sinkron dan asinkron


Pertimbangkan dua skenario utama di mana pengamat memulai survei "sensor".

Mode sinkron


Sebuah survei sensor diikuti oleh perhitungan ulang keadaan sistem disebabkan oleh daya tarik langsung dari salah satu modul sistem kepada pengamat.
Dalam hal ini, pengamat dan sensor bekerja di utas yang sama. Kesalahan perhitungan kondisi sistem dilakukan sebagai bagian dari operasi di dalam sistem.

Proyek ini sudah memiliki implementasi dasar dari pengamat tersebut - SpectatorBase .

Lihat kode
  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 { //cleanup state records _journal.RemoveAll(o => o.Time < now.Subtract(RetentionPeriod)); _journal.Add(new JournalRecord(now, results)); } finally { _journalLock.ExitWriteLock(); } //Recalculate state var state = _stateEvaluator.Evaluate(State, StateChangedDate, _journal); if (!EqualityComparer<TState>.Default.Equals(State, state)) { ChangeState(state, results.Where(o => !o.Success).Select(o => o.ProbeName)); } OnHealthChecked(now, results); } protected virtual void OnHealthChecked(DateTime now, IReadOnlyCollection<ProbeResult> results) => HealthChecked?.Invoke(this, new HealthCheckEventArgs(now, results)); } 


Mode asinkron


Dalam hal ini, interogasi sensor terjadi secara asinkron dari proses sistem dan dapat dilakukan dalam utas terpisah. Implementasi dasar dari pengamat semacam itu juga sudah merupakan proyek - AutomatedSpectator .

Lihat kode
 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(); } 


Kesimpulan


Menggunakan X.Spectator membantu saya secara pribadi dalam beberapa proyek yang sangat dimuat untuk secara signifikan meningkatkan stabilitas sejumlah layanan. Kerangka yang diusulkan terbaik telah membuktikan dirinya ketika diimplementasikan dalam sistem terdistribusi yang dibangun atas dasar arsitektur microservice. Pilihan integrasi yang paling optimal adalah menggunakan prinsip inversi kontrol, yaitu, implementasi dependensi, ketika proses penerapan sensor diimplementasikan menggunakan wadah IoC, dan pengamat disajikan dalam bentuk singletones, di mana satu instance dari masing-masing pengamat dapat diakses oleh berbagai kelas modul dan layanan.

Tautan dan informasi bermanfaat


Repositori proyek
Contoh
Paket NuGet

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


All Articles