ایجاد برنامه ی نظارت بر سرویس با .NET Core

سرویس

سرویس با .NET Core ، دراین مقاله می خواهیم در رابطه با نحوه ی ایجاد یک برنامه ی نظارت بر سرویس با .NET Core را توضیح دهیم ، اما چه برنامه ای؟

به زبان ساده: برنامه ای است که امکان نظارت بر سرویس ها در یک شبکه و ذخیره ی نتایج حاصل از نظارت در یک پایگاه داده، در این مورد SQL Server، را فراهم می کند.

سرویس با .NET Core

می دانم که ابزارهای زیادی وجود دارد که می تواند این امکان را فراهم کند، همچنین ابزارهای بهتری وجود دارد که می توان با پول خرید ولی هدف من از این راهنما این است که نحوه ی استفاده از قدرت .NET Core برای ساخت برنامه ای که توسعه دهندگان بتوانند برای نیازمندی های شخصی گسترش و توسعه دهند را نشان دهم.

ایده ی اصلی این است: داشتن فرآیندی برای اجرا به روش های نامحدود جهت نظارت بر میزبان ها، پایگاه داده ها و API ها.

نتایج نظارت را در پایگاه داده ی SQL Server ذخیره کرده، سپس می توان یک رابط کاربری شیک برای کاربر نهایی ساخت و وضعیت برای هر سرویس را نشان داد.

می توان اهداف بسیاری برای نظارت داشت اما بهتر است به کاربران اجازه داده شود تا مشترک سرویس هایی خاص و نه تمام سرویس ها شوند.

برای مثال، DBA ها (مدیران پایگاه داده) نیاز به نظارت بر سرورها و نه APIها دارند، توسعه دهندگان نیاز به نظارت بر پایگاه داده های توسعه و APIها دارند، و … .

همچنین به داشتن نمایشگرهای بزرگ در اتاق توسعه ی خود و نظارت بر وضعیت سرویس های خود و در بهترین حالات، داشتن نمودارها فکر کنید.
یک ویژگی خاص می تواند داشتن یک سرویس اعلان برای ارسال پیام هایی به تمام مدیران در صورتیکه یک یا تعداد بیشتری از سرویس ها شکست خوردند، باشد.

در این متن، سرویس به معنای هدفی مانند میزبان، پایگاه داده، API است.

در این راهنما، با نظارت بر سرویس های زیر کار خواهیم کرد:

سرویس

سرویس

پیش زمینه

همانطور که پیش تر گفتیم، برنامه ای جهت نظارت بر اهداف موجود (میزبان ها، پایگاه داده ها، APIها) ایجاد خواهیم کرد، بنابراین نیاز به داشتن دانش پایه در رابطه با این مفاهیم داریم.

میزبان ها به وسیله ی عمل پینگ نظارت خواهند کرد، بنابراین پکیج های مرتبط با شبکه سازی را برای اجرای این عمل اضافه خواهیم کرد.

پایگاه داده ها به وسیله ی اتصالات باز و بسته نظارت خواهند کرد.

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

بنابراین در آن صورت، داشتن کاربری خاص جهت اتصال با پایگاه داده و تنها همان عمل برای جلوگیری از نفوذ (هک) بهتر است.

RESTful API ها به وسیله ی سرویس گیرنده ی REST نظارت خواهند کرد تا عملی که JSON ساده برمی گرداند را هدف بگیرد.

پایگاه داده

داخل انباره (repository) ، یک دایرکتوری با نام \Resources\Database وجود دارد و این دایرکتوری حاوی فایل های پایگاه داده ای مرتبط است، حتما فایل های زیر را به این ترتیب اجرا کنید:

سرویس

سرویس

میتوان اسناد پایگاه داده را در اینجا یافت.

سرویس

سرویس

لطفا فراموش نکنید که من با یک solution در ماشین محلی خود کار می کنم.

یک API نمونه در منابع برای انجام آزمون ها وجود دارد، اما نیاز است رشته ی اتصال را تغییر داده و سرویس های خود را بر اساس نیازهایتان اضافه کنید.

همچنین پیشنهاد می کنم رشته های اتصال واقعی را در جدول ServiceEnvironment در معرض دید قرار ندهید، لطفا به DBA خود که ارائه دهنده به شماست درخواست دهید.

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

.NET Core Solution

حال نیاز است پروژه های این solution را تعریف کنیم تا فهمی روشن در رابطه با دامنه ی پروژه پیدا کنیم:

سرویس

سرویس

ServiceMonitor.Core

این پروژه حاوی تمام تعاریف موجودیت ها و دسترسی پایگاه داده است.

بنابراین نیاز است پکیج های زیر را به پروژه اضافه کنیم:

سرویس

سرویس

این پروژه حاوی سه لایه است:

  • Business Logic (منطق کسب و کار)
  • Database Access (دسترسی پایگاه داده)
  • Entities (موجودیت ها)

کد کلاس DashboardService :

using System;
using System.Threading.Tasks;
using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.Logging;
using ServiceMonitor.Core.BusinessLayer.Contracts;
using ServiceMonitor.Core.BusinessLayer.Responses;
using ServiceMonitor.Core.DataLayer;
using ServiceMonitor.Core.DataLayer.DataContracts;
using ServiceMonitor.Core.EntityLayer;

namespace ServiceMonitor.Core.BusinessLayer
{
    public class DashboardService : Service, IDashboardService
    {
        public DashboardService(ILogger<DashboardService> logger, ServiceMonitorDbContext dbContext)
            : base(logger, dbContext)
        {
        }

        public async Task<IListResponse<ServiceWatcherItemDto>> GetActiveServiceWatcherItemsAsync()
        {
            Logger?.LogDebug("'{0}' has been invoked", nameof(GetActiveServiceWatcherItemsAsync));

            var response = new ListResponse<ServiceWatcherItemDto>();

            try
            {
                response.Model = await DbContext.GetActiveServiceWatcherItems().ToListAsync();

                Logger?.LogInformation("The service watch items were loaded successfully");
            }
            catch (Exception ex)
            {
                response.SetError(Logger, nameof(GetActiveServiceWatcherItemsAsync), ex);
            }

            return response;
        }

        public async Task<IListResponse<ServiceStatusDetailDto>> GetServiceStatusesAsync(string userName)
        {
            Logger?.LogDebug("'{0}' has been invoked", nameof(GetServiceStatusesAsync));

            var response = new ListResponse<ServiceStatusDetailDto>();

            try
            {
                var user = await DbContext.GetUserAsync(userName);

                if (user == null)
                {
                    Logger?.LogInformation("There isn't data for user '{0}'", userName);

                    return new ListResponse<ServiceStatusDetailDto>();
                }
                else
                {
                    response.Model = await DbContext.GetServiceStatuses(user).ToListAsync();

                    Logger?.LogInformation("The service status details for '{0}' user were loaded successfully", userName);
                }
            }
            catch (Exception ex)
            {
                response.SetError(Logger, nameof(GetServiceStatusesAsync), ex);
            }

            return response;
        }

        public async Task<ISingleResponse<ServiceEnvironmentStatus>> GetServiceStatusAsync(ServiceEnvironmentStatus entity)
        {
            Logger?.LogDebug("'{0}' has been invoked", nameof(GetServiceStatusAsync));

            var response = new SingleResponse<ServiceEnvironmentStatus>();

            try
            {
                response.Model = await DbContext.GetServiceEnvironmentStatusAsync(entity);
            }
            catch (Exception ex)
            {
                response.SetError(Logger, nameof(GetServiceStatusAsync), ex);
            }

            return response;
        }
    }
}

ServiceMonitor.Common

قراردادها

IWacher
IWatchResponse
ISerializer

کد رابط IWatcher

using System.Threading.Tasks;

namespace ServiceMonitor.Common.Contracts
{
    public interface IWatcher
    {
        string ActionName { get; }

        Task<WatchResponse> WatchAsync(WatcherParameter parameter);
    }
}

کد رابط IWatchResponse

namespace ServiceMonitor.Common.Contracts
{
    public interface IWatchResponse
    {
        bool Success { get; set; }

        string Message { get; set; }

        string StackTrace { get; set; }
    }
}

کد رابط ISerializer

namespace ServiceMonitor.Common.Contracts
{
    public interface ISerializer
    {
        string Serialize<T>(T obj);

        T Deserialze<T>(string source);
    }
}

Watchers (نظاره گرها)

این ها پیاده سازی ها هستند:

  •  DatabaseWatcher
  • HttpRequestWatcher
  •  PingWatcher

کد کلاس DatabaseWatcher

using System;
using System.Data.SqlClient;
using System.Threading.Tasks;
using ServiceMonitor.Common.Contracts;

namespace ServiceMonitor.Common
{
    public class DatabaseWatcher : IWatcher
    {
        public string ActionName
            => "OpenDatabaseConnection";

        public async Task<WatchResponse> WatchAsync(WatcherParameter parameter)
        {
            var response = new WatchResponse();

            using (var connection = new SqlConnection(parameter.Values["ConnectionString"]))
            {
                try
                {
                    await connection.OpenAsync();

                    response.Success = true;
                }
                catch (Exception ex)
                {
                    response.Success = false;
                    response.Message = ex.Message;
                    response.StackTrace = ex.ToString();
                }
            }

            return response;
        }
    }
}

کد کلاس HttpRequestWatcher

using System;
using System.Threading.Tasks;
using ServiceMonitor.Common.Contracts;

namespace ServiceMonitor.Common
{
    public class HttpRequestWatcher : IWatcher
    {
        public string ActionName
            => "HttpRequest";

        public async Task<WatchResponse> WatchAsync(WatcherParameter parameter)
        {
            var response = new WatchResponse();

            try
            {
                var restClient = new RestClient();

                await restClient.GetAsync(parameter.Values["Url"]);

                response.Success = true;
            }
            catch (Exception ex)
            {
                response.Success = false;
                response.Message = ex.Message;
                response.StackTrace = ex.ToString();
            }

            return response;
        }
    }
}

کد کلاس PingWatcher

using System.Net.NetworkInformation;
using System.Threading.Tasks;
using ServiceMonitor.Common.Contracts;

namespace ServiceMonitor.Common
{
    public class PingWatcher : IWatcher
    {
        public string ActionName
            => "Ping";

        public async Task<WatchResponse> WatchAsync(WatcherParameter parameter)
        {
            var ping = new Ping();

            var reply = await ping.SendPingAsync(parameter.Values["Address"]);

            return new WatchResponse
            {
                Success = reply.Status == IPStatus.Success ? true : false
            };
        }
    }
}

ServiceMonitor.API

این پروژه نشان دهنده ی RESTful API برای نظارت بر سرویس است.

بنابراین دو کنترل کننده خواهیم داشت:

DashboardController

AdministrationController. Dashboard

دارای تمامی اعمال مربوط به نتایج کاربر نهایی و Administration حاوی تمامی اعمال مربوط ذخیره ی اطلاعات (ایجاد، تصحیح و حذف) است.

Dashboard

کد کلاس DashboardController

using System.Threading.Tasks;
using Microsoft.AspNetCore.Mvc;
using Microsoft.Extensions.Logging;
using ServiceMonitor.Core.BusinessLayer.Contracts;
using ServiceMonitor.WebApi.Responses;

namespace ServiceMonitor.WebApi.Controllers
{
#pragma warning disable CS1591
    [Route("api/v1/[controller]")]
    [ApiController]
    public class DashboardController : ControllerBase
    {
        protected ILogger Logger;
        protected IDashboardService Service;

        public DashboardController(ILogger<DashboardController> logger, IDashboardService service)
        {
            Logger = logger;
            Service = service;
        }
#pragma warning restore CS1591

        /// 
<summary>
        /// Gets service watcher items (registered services to watch with service monitor)
        /// </summary>

        /// <returns>A sequence of services to watch</returns>
        [HttpGet("ServiceWatcherItem")]
        [ProducesResponseType(200)]
        [ProducesResponseType(204)]
        [ProducesResponseType(500)]
        public async Task<IActionResult> GetServiceWatcherItemsAsync()
        {
            Logger?.LogDebug("'{0}' has been invoked", nameof(GetServiceWatcherItemsAsync));

            var response = await Service.GetActiveServiceWatcherItemsAsync();

            return response.ToHttpResponse();
        }

        /// 
<summary>
        /// Gets the details for service watch
        /// </summary>

        /// <param name="id">Service ID</param>
        /// <returns></returns>
        [HttpGet("ServiceStatusDetail/{id}")]
        [ProducesResponseType(200)]
        [ProducesResponseType(204)]
        [ProducesResponseType(500)]
        public async Task<IActionResult> GetServiceStatusDetailsAsync(string id)
        {
            Logger?.LogDebug("'{0}' has been invoked", nameof(GetServiceStatusDetailsAsync));

            var response = await Service.GetServiceStatusesAsync(id);

            return response.ToHttpResponse();
        }
    }
}

Administration

کد کلاس AdministrationController

using System.Threading.Tasks;
using Microsoft.AspNetCore.Mvc;
using Microsoft.Extensions.Logging;
using ServiceMonitor.Core.BusinessLayer.Contracts;
using ServiceMonitor.WebApi.Requests;
using ServiceMonitor.WebApi.Responses;

namespace ServiceMonitor.WebApi.Controllers
{
#pragma warning disable CS1591
    [Route("api/v1/[controller]")]
    [ApiController]
    public class AdministrationController : ControllerBase
    {
        protected ILogger Logger;
        protected IAdministrationService Service;

        public AdministrationController(ILogger<AdministrationController> logger, IAdministrationService service)
        {
            Logger = logger;
            Service = service;
        }
#pragma warning restore CS1591

        /// 
<summary>
        /// Saves a result from service watch action
        /// </summary>

        /// <param name="request">Service status result</param>
        /// <returns>Ok if save it was successfully, Not found if service not exists else server internal error</returns>
        [HttpPost("ServiceEnvironmentStatusLog")]
        [ProducesResponseType(200)]
        [ProducesResponseType(404)]
        [ProducesResponseType(500)]
        public async Task<IActionResult> PostServiceStatusLogAsync([FromBody]ServiceEnvironmentStatusLogRequest request)
        {
            Logger?.LogDebug("'{0}' has been invoked", nameof(PostServiceStatusLogAsync));

            var response = await Service
                .CreateServiceEnvironmentStatusLogAsync(request.ToEntity(), request.ServiceEnvironmentID);

            return response.ToHttpResponse();
        }
    }
}

ServiceMonitor

این پروژه حاوی تمامی اشیاء برای Service Monitor Client (سرویس گیرنده/مشتری نظارت بر سرویس) است.

در این پروژه، پکیج Newtonsoft.Json را برای سریالی کردن JSON اضافه کرده ایم.

در ServiceMonitor.Common رابطی با نام ISerializer وجود دارد و به این دلیل است که نمی خواهم استفاده از سریال کننده ی خاصی را اجبار کنم، می توانید آن را در این مرحله تغییر دهید.

کد کلاس ServiceMonitorSerializer

using Newtonsoft.Json;
using ServiceMonitor.Common.Contracts;

namespace ServiceMonitor
{
    public class ServiceMonitorSerializer : ISerializer
    {
        public string Serialize<T>(T obj)
            => JsonConvert.SerializeObject(obj);

        public T Deserialze<T>(string source)
            => JsonConvert.DeserializeObject<T>(source);
    }
}

سپس، بر روی کلاس MonitorController کار خواهیم کرد.

در این کلاس، تمامی اعمال نظارت را اجرا و تمام نتایج را از طریق AdministrationController در Service Monitor API در پایگاه داده ذخیره خواهیم کرد.

کد کلاس MonitorController

using System;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.Extensions.Logging;
using ServiceMonitor.Common;
using ServiceMonitor.Common.Contracts;
using ServiceMonitor.Models;

namespace ServiceMonitor
{
    public class MonitorController
    {
        public MonitorController(AppSettings appSettings, ILogger logger, IWatcher watcher, RestClient restClient)
        {
            AppSettings = appSettings;
            Logger = logger;
            Watcher = watcher;
            RestClient = restClient;
        }

        public AppSettings AppSettings { get; }

        public ILogger Logger { get; }

        public IWatcher Watcher { get; }

        public RestClient RestClient { get; }

        public async Task ProcessAsync(ServiceWatchItem item)
        {
            while (true)
            {
                try
                {
                    Logger?.LogTrace("{0} - Watching '{1}' for '{2}' environment", DateTime.Now, item.ServiceName, item.Environment);

                    var watchResponse = await Watcher.WatchAsync(new WatcherParameter(item.ToDictionary()));

                    if (watchResponse.Success)
                        Logger?.LogInformation(" Success watch for '{0}' in '{1}' environment", item.ServiceName, item.Environment);
                    else
                        Logger?.LogError(" Failed watch for '{0}' in '{1}' environment", item.ServiceName, item.Environment);

                    var watchLog = new ServiceStatusLog
                    {
                        ServiceID = item.ServiceID,
                        ServiceEnvironmentID = item.ServiceEnvironmentID,
                        Target = item.ServiceName,
                        ActionName = Watcher.ActionName,
                        Success = watchResponse.Success,
                        Message = watchResponse.Message,
                        StackTrace = watchResponse.StackTrace
                    };

                    try
                    {
                        await RestClient.PostJsonAsync(AppSettings.ServiceStatusLogUrl, watchLog);
                    }
                    catch (Exception ex)
                    {
                        Logger?.LogError(" Error on saving watch response ({0}): '{1}'", item.ServiceName, ex.Message);
                    }
                }
                catch (Exception ex)
                {
                    Logger?.LogError(" Error watching service: '{0}': '{1}'", item.ServiceName, ex.Message);
                }

                Thread.Sleep(item.Interval ?? AppSettings.DelayTime);
            }
        }
    }
}

پیش از اجرای برنامه ی کنسولی، از این جنبه ها اطمینان حاصل کنید :

  1.  پایگاه داده ی ServiceMonitor موجود باشد.
  2. پایگاه داده ی ServiceMonitor دارای اطلاعات طبقه بندی های سرویس، سرویس ها، نظاره گرهای سرویس و کاربران باشد.
  3. ServiceMonitor API موجود باشد.

می توانیم مقدار بازگردانده شده ی url (نشانی اینترنتی) api/v1/Dashboard/ServiceWatcherItems را بررسی کنیم:

{  
  "message":null,
  "didError":false,
  "errorMessage":null,
  "model":[  
    {  
      "serviceID":1,
      "serviceEnvironmentID":1,
      "environment":"Development",
      "serviceName":"Northwind Database",
      "interval":15000,
      "url":null,
      "address":null,
      "connectionString":"server=(local);database=Northwind;user id=johnd;password=SqlServer2017$",
      "typeName":"ServiceMonitor.Common.DatabaseWatcher, ServiceMonitor.Common, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null"
    },
    {  
      "serviceID":2,
      "serviceEnvironmentID":3,
      "environment":"Development",
      "serviceName":"DNS",
      "interval":3000,
      "url":null,
      "address":"192.168.1.1",
      "connectionString":null,
      "typeName":"ServiceMonitor.Common.PingWatcher, ServiceMonitor.Common, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null"
    },
    {  
      "serviceID":3,
      "serviceEnvironmentID":4,
      "environment":"Development",
      "serviceName":"Sample API",
      "interval":5000,
      "url":"http://localhost:5612/api/values",
      "address":null,
      "connectionString":null,
      "typeName":"ServiceMonitor.Common.HttpWebRequestWatcher, ServiceMonitor.Common, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null"
    }
  ]
}

همانطور که می توانیم ببینیم، API تمامی سرویس های DefaultUser را برمی گرداند،.

لطفا این مفهوم که یک کاربر می تواند مشترک بیش از یک سرویس جهت نظاره شود را به خاطر بسپارید،.

بدیهی است که در این نمونه، کاربر پیش فرض ما مشترک تمام سرویس ها است اما می توانیم این پیوند را در جدول ServiceUser تغییر دهیم.

کد کلاس Program

using System;
using System.Threading.Tasks;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.Logging;
using ServiceMonitor.Common;
using ServiceMonitor.Common.Contracts;

namespace ServiceMonitor
{
    class Program
    {
        private static ILogger logger;
        private static readonly AppSettings appSettings;

        static Program()
        {
            logger = LoggingHelper.GetLogger<Program>();

            var builder = new ConfigurationBuilder().AddJsonFile("appsettings.json");

            var configuration = builder.Build();

            appSettings = new AppSettings
            {
                ServiceWatcherItemsUrl = configuration["serviceWatcherItemUrl"],
                ServiceStatusLogUrl = configuration["serviceStatusLogUrl"],
                DelayTime = Convert.ToInt32(configuration["delayTime"])
            };
        }

        static void Main(string[] args)
        {
            StartAsync(args).GetAwaiter().GetResult();

            Console.ReadLine();
        }

        static async Task StartAsync(string[] args)
        {
            logger.LogDebug("Starting application...");

            var initializer = new ServiceMonitorInitializer(appSettings);

            try
            {
                await initializer.LoadResponseAsync();
            }
            catch (Exception ex)
            {
                logger.LogError("Error on retrieve watch items: {0}", ex);
                return;
            }

            try
            {
                initializer.DeserializeResponse();
            }
            catch (Exception ex)
            {
                logger.LogError("Error on deserializing object: {0}", ex);
                return;
            }

            foreach (var item in initializer.Response.Model)
            {
                var watcherType = Type.GetType(item.TypeName, true);

                var watcherInstance = Activator.CreateInstance(watcherType) as IWatcher;

                var task = Task.Factory.StartNew(async () =>
                {
                    var controller = new MonitorController(appSettings, logger, watcherInstance, initializer.RestClient);

                    await controller.ProcessAsync(item);
                });
            }
        }
    }
}

هنگامیکه جنبه های پیشین را بررسی کردیم، اکنون به تغییر برنامه ی کنسولی می پردازیم، خروجی کنسول این است:

dbug: ServiceMonitor.Program[0]
      Starting application
sr trce: ServiceMonitor.Program[0]
      ۰۶/۲۰/۲۰۱۷ ۲۳:۰۹:۳۰ - Watching 'Sample API' for 'Development' environment
trce: ServiceMonitor.Program[0]
      ۰۶/۲۰/۲۰۱۷ ۲۳:۰۹:۳۰ - Watching 'Northwind Database' for 'Development' environment
trce: ServiceMonitor.Program[0]
      ۰۶/۲۰/۲۰۱۷ ۲۳:۰۹:۳۰ - Watching 'DNS' for 'Development' environment
trce: ServiceMonitor.Program[0]
      ۰۶/۲۰/۲۰۱۷ ۲۳:۰۹:۳۵ - Watching 'DNS' for 'Development' environment
trce: ServiceMonitor.Program[0]
      ۰۶/۲۰/۲۰۱۷ ۲۳:۰۹:۳۷ - Watching 'Sample API' for 'Development' environment
trce: ServiceMonitor.Program[0]
      ۰۶/۲۰/۲۰۱۷ ۲۳:۰۹:۳۹ - Watching 'DNS' for 'Development' environment
trce: ServiceMonitor.Program[0]
      ۰۶/۲۰/۲۰۱۷ ۲۳:۰۹:۴۲ - Watching 'DNS' for 'Development' environment
trce: ServiceMonitor.Program[0]
      ۰۶/۲۰/۲۰۱۷ ۲۳:۰۹:۴۳ - Watching 'Sample API' for 'Development' environment
trce: ServiceMonitor.Program[0]
      ۰۶/۲۰/۲۰۱۷ ۲۳:۰۹:۴۵ - Watching 'DNS' for 'Development' environment
trce: ServiceMonitor.Program[0]
      ۰۶/۲۰/۲۰۱۷ ۲۳:۰۹:۴۷ - Watching 'Northwind Database' for 'Development' environment
trce: ServiceMonitor.Program[0]
      ۰۶/۲۰/۲۰۱۷ ۲۳:۰۹:۴۸ - Watching 'Sample API' for 'Development' environment
trce: ServiceMonitor.Program[0]
      ۰۶/۲۰/۲۰۱۷ ۲۳:۰۹:۴۸ - Watching 'DNS' for 'Development' environment
trce: ServiceMonitor.Program[0]
      ۰۶/۲۰/۲۰۱۷ ۲۳:۰۹:۵۱ - Watching 'DNS' for 'Development' environment
trce: ServiceMonitor.Program[0]
      ۰۶/۲۰/۲۰۱۷ ۲۳:۰۹:۵۳ - Watching 'Sample API' for 'Development' environment
trce: ServiceMonitor.Program[0]
      ۰۶/۲۰/۲۰۱۷ ۲۳:۰۹:۵۴ - Watching 'DNS' for 'Development' environment
trce: ServiceMonitor.Program[0]
      ۰۶/۲۰/۲۰۱۷ ۲۳:۰۹:۵۷ - Watching 'DNS' for 'Development' environment

حال به بررسی داده های ذخیره شده در پایگاه داده می پردازیم، لطفا جدول ServiceEnvironmentStatus را بررسی کنید، نتیجه ای به این صورت خواهید گرفت:

ServiceEnvironmentStatusID ServiceEnvironmentID Success WatchCount  LastWatch
-------------------------- -------------------- ------- ----------- -----------------------
۱                          ۴                    ۱       ۲۱۲         ۲۰۱۸-۱۱-۲۲ ۲۳:۱۱:۳۴.۱۱۳
۲                          ۱                    ۱       ۷۸          ۲۰۱۸-۱۱-۲۲ ۲۳:۱۱:۳۳.۳۷۰
۳                          ۳                    ۱       ۳۶۶         ۲۰۱۸-۱۱-۲۲ ۲۳:۱۱:۳۴.۶۲۰

(۳ row(s) affected)

چگونه همگی باهم کار می کنند؟

برنامه ی کنسولی تمامی سرویس ها جهت نظارت را از API گرفته و سپس شروع به دادن وظیفه به ازای هر مورد نظارت به روشی نامتناهی درون MonitorController می کند، ضمن تأخیری برای هر وظیفه وجود دارد.

این وقفه (interval) در تعریف Service تنظیم می شود، اما اگر مقدار تعریف شده ای برای Interval وجود ندارد، وقفه از AppSettings گرفته می شود.

بنابراین پس از اجرای عمل Watch، نتیجه از طریق API در پایگاه داده ذخیره شده و فرآیند، خود را تکرار می کند.

اگر می خواهید یک عملیات watch برای انواع دیگر انجام دهید، می توانید کلاس Watcher خود را ایجاد کنید.

نکات جالب

DatabaseWatcher با SQL Server کار می کند، بنابراین چگونه به MySQL، PostgreSQL، Oracle و دیگر DBMSها متصل شوید؟

کلاس Watcher خود را برای DBMS خاصی ایجاد و رابط IWatcher را پیاده سازی کرده و برای اتصال پایگاه داده ی هدف کد بنویسید.

آیا می توانیم نظارت بر سرویس را در پلتفرم های غیر ویندوزی میزبانی کنیم؟

بله، از آنجاییکه .NET Core چندپلتفرمی است، می توانیم این پروژه را بر روی Windows، Mac OS و Linux میزبانی کنیم.

همانطور که می دانیم، پشتیبانی محلی برای ASMX در .NET Core وجود ندارد اما می توانیم بر هر دو نوع سرویس نظارت کنیم، به سادگی ردیف هایی را به جدول Service بیفزایید، ASMX با .asmx پایان می یابد.

در این نسخه ی اولیه، هیچ پیکربندی در ارتباط با امنیت وجود ندارد چراکه بهتر است آن پیاده سازی را با توجه به سناریوی خود اضافه کنید؛

میتوانید این پروژه را با Windows Authentication (احراز هویت ویندوز)، Authentication دلخواه یا افزودن سرویس خارجی برای احراز هویت انجام دهید، آیا جور در می آید؟

بهبود کد

  •  افزودن پروژه ی UI (رابط کاربری) برای نمایش وضعیت سرویس ها به شیوه ای زیبا برای کاربران نهایی، با استفاده از برخی فریمورک های سمت کاربر (front-end) مانند AngularJS یا ReactJS
  • افزودن اعلان ها برای مدیران در خطاهای بحرانی در طول نظارت بر سرویس ها (email، sms و …)
  •  فکر می کنم داشتن TypeName در ServiceCategory در عوض ServiceWatcher بهتر است.
  • افزودن امنیت برای API

زهره سلطانیان

نوشته‌های مرتبط

دیدگاه‌ها

*
*

این سایت از اکیسمت برای کاهش هرزنامه استفاده می کند. بیاموزید که چگونه اطلاعات دیدگاه های شما پردازش می‌شوند.