zoukankan      html  css  js  c++  java
  • 性能计数器数据收集服务

    本文演示了一个Windows服务收集性能计数器的数据,将性能计数器数据写入数据库。Windows服务中调用WebAPI服务中。下面简要介绍下我的改造,项目虽小,其中用到了众多的开源项目Topshelf、NLog、Dapper,ASP.NET Web API,Newtonsoft.Json等等:

    1、数据库模型,以下是MS SQL Server的模型:

       1:  USE [PerfmonCounter]
       2:  GO
       3:  /****** Object:  Table [dbo].[service_counter_snapshots]    Script Date: 01/25/2013 22:40:20 ******/
       4:  SET ANSI_NULLS ON
       5:  GO
       6:  SET QUOTED_IDENTIFIER ON
       7:  GO
       8:  SET ANSI_PADDING ON
       9:  GO
      10:  CREATE TABLE [dbo].[service_counter_snapshots](
      11:      [Id] [int] IDENTITY(1,1) NOT NULL,
      12:      [ServiceCounterId] [int] NOT NULL,
      13:      [SnapshotMachineName] [varchar](100) NULL,
      14:      [CreationTimeUtc] [datetime] NOT NULL,
      15:      [ServiceCounterValue] [float] NULL,
      16:  PRIMARY KEY CLUSTERED 
      17:  (
      18:      [Id] ASC
      19:  )WITH (PAD_INDEX  = OFF, STATISTICS_NORECOMPUTE  = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS  = ON, ALLOW_PAGE_LOCKS  = ON) ON [PRIMARY]
      20:  ) ON [PRIMARY]
      21:  GO
      22:  SET ANSI_PADDING OFF
      23:  GO
      24:  /****** Object:  Table [dbo].[services]    Script Date: 01/25/2013 22:40:20 ******/
      25:  SET ANSI_NULLS ON
      26:  GO
      27:  SET QUOTED_IDENTIFIER ON
      28:  GO
      29:  SET ANSI_PADDING ON
      30:  GO
      31:  CREATE TABLE [dbo].[services](
      32:      [Name] [varchar](100) NOT NULL,
      33:      [DisplayName] [varchar](1000) NULL,
      34:  PRIMARY KEY CLUSTERED 
      35:  (
      36:      [Name] ASC
      37:  )WITH (PAD_INDEX  = OFF, STATISTICS_NORECOMPUTE  = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS  = ON, ALLOW_PAGE_LOCKS  = ON) ON [PRIMARY]
      38:  ) ON [PRIMARY]
      39:  GO
      40:  SET ANSI_PADDING OFF
      41:  GO
      42:  /****** Object:  Table [dbo].[service_counters]    Script Date: 01/25/2013 22:40:20 ******/
      43:  SET ANSI_NULLS ON
      44:  GO
      45:  SET QUOTED_IDENTIFIER ON
      46:  GO
      47:  SET ANSI_PADDING ON
      48:  GO
      49:  CREATE TABLE [dbo].[service_counters](
      50:      [Id] [int] IDENTITY(1,1) NOT NULL,
      51:      [ServiceName] [varchar](100) NOT NULL,
      52:      [MachineName] [varchar](100) NULL,
      53:      [CategoryName] [varchar](100) NOT NULL,
      54:      [CounterName] [varchar](100) NOT NULL,
      55:      [InstanceName] [varchar](100) NULL,
      56:      [DisplayName] [varchar](1000) NULL,
      57:      [DisplayType] [varchar](7) NOT NULL,
      58:  PRIMARY KEY CLUSTERED 
      59:  (
      60:      [Id] ASC
      61:  )WITH (PAD_INDEX  = OFF, STATISTICS_NORECOMPUTE  = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS  = ON, ALLOW_PAGE_LOCKS  = ON) ON [PRIMARY]
      62:  ) ON [PRIMARY]
      63:  GO
      64:  SET ANSI_PADDING OFF
      65:  GO
      66:  /****** Object:  Default [DF__service_c__Displ__08EA5793]    Script Date: 01/25/2013 22:40:20 ******/
      67:  ALTER TABLE [dbo].[service_counters] ADD  DEFAULT ('table') FOR [DisplayType]
      68:  GO
      69:  /****** Object:  ForeignKey [FK__service_c__Servi__09DE7BCC]    Script Date: 01/25/2013 22:40:20 ******/
      70:  ALTER TABLE [dbo].[service_counters]  WITH CHECK ADD FOREIGN KEY([ServiceName])
      71:  REFERENCES [dbo].[services] ([Name])
      72:  GO

    services表 存储我们需要监控的服务的进程。 每个服务都需要监控一系列的性能计数器 (存储在 service_counters 表)。数据收集服务在启动的时候根据service_counters 表创建 System.Diagnostics.PerformanceCounter class 的实例列表。 服务每隔一段时间收集一次性能计数器数据并把它存储到service_counter_snapshots 表。

    所有的数据操作通过一个Restful服务接口DataCollectorController 进行操作。

       1:  using System;
       2:  using System.Collections.Generic;
       3:  using System.Linq;
       4:  using System.Net;
       5:  using System.Net.Http;
       6:  using System.Web.Http;
       7:  using PerformanceCounterCollect.Models;
       8:  using PerformanceCounterCollect.Web.Models;
       9:   
      10:  namespace PerformanceCounterCollect.Web.Controllers
      11:  {
      12:      public class DataCollectorController : ApiController
      13:      {
      14:          private ServiceCounterRepository scRepository;
      15:          private ServiceCounterSnapshotRepository scSnapshotReposity;
      16:   
      17:          public DataCollectorController()
      18:          {
      19:              scRepository = new ServiceCounterRepository();
      20:              scSnapshotReposity = new ServiceCounterSnapshotRepository();
      21:          }
      22:   
      23:          // GET api/values/5
      24:          [HttpGet]
      25:          public IEnumerable<ServiceCounter> SelectServiceCounter(string machineName)
      26:          {
      27:              return scRepository.SelectServiceCounter(machineName);
      28:          }
      29:   
      30:          // POST api/values
      31:          [HttpPost]
      32:          public IEnumerable<ServiceCounterSnapshot> SaveServiceSnapshots([FromBody]IEnumerable<ServiceCounterSnapshot> value)
      33:          {
      34:              return scSnapshotReposity.SaveServiceSnapshots(value);
      35:          }
      36:      }
      37:        
      38:  }
    数据操作使用到了轻型的ORM类Dapper,使用了Repository模式。
       1:  using System;
       2:  using System.Data;
       3:  using System.Data.SqlClient;
       4:  using System.Linq;
       5:  using System.Web.Configuration;
       6:  using Dapper;
       7:   
       8:  namespace PerformanceCounterCollect.Web.Models
       9:  {
      10:      public abstract class BaseRepository
      11:      {
      12:          protected static void SetIdentity<T>(IDbConnection connection, Action<T> setId)
      13:          {
      14:              dynamic identity = connection.Query("SELECT @@IDENTITY AS Id").Single();
      15:              T newId = (T)identity.Id;
      16:              setId(newId);
      17:          }
      18:   
      19:          protected static IDbConnection OpenConnection()
      20:          {
      21:              IDbConnection connection = new SqlConnection(WebConfigurationManager.ConnectionStrings["SqlDiagnosticsDb"].ConnectionString);
      22:              connection.Open();
      23:              return connection;
      24:          }
      25:      }
      26:  }
     
       1:  using System;
       2:  using System.Collections.Generic;
       3:  using System.Data;
       4:  using System.Linq;
       5:  using System.Web;
       6:  using Dapper;
       7:  using PerformanceCounterCollect.Models;
       8:   
       9:   
      10:  namespace PerformanceCounterCollect.Web.Models
      11:  {
      12:      public class ServiceCounterRepository : BaseRepository
      13:      {
      14:          public IEnumerable<ServiceCounter> SelectServiceCounter(string machineName)
      15:          {
      16:              using (IDbConnection connection = OpenConnection())
      17:              {
      18:                  string query = "select Id,ServiceName,CategoryName,CounterName,InstanceName from service_counters where MachineName=@MachineName";
      19:                  return connection.Query<ServiceCounter>(query, new { MachineName = machineName });
      20:              }
      21:          }
      22:   
      23:   
      24:      }
      25:  }
     
     
       1:  using System;
       2:  using System.Collections.Generic;
       3:  using System.Data;
       4:  using System.Linq;
       5:  using System.Web;
       6:  using Dapper;
       7:  using PerformanceCounterCollect.Models;
       8:   
       9:  namespace PerformanceCounterCollect.Web.Models
      10:  {
      11:      public class ServiceCounterSnapshotRepository: BaseRepository
      12:      {
      13:          public IEnumerable<ServiceCounterSnapshot> SaveServiceSnapshots(IEnumerable<ServiceCounterSnapshot> snapshots)
      14:          {
      15:              using (IDbConnection connection = OpenConnection())
      16:              {
      17:                  foreach (var snapshot in snapshots)
      18:                  {
      19:                      // insert new snapshot to the database
      20:                      int retVal = connection.Execute(
      21:      @"insert into service_counter_snapshots(ServiceCounterId,SnapshotMachineName,CreationTimeUtc,ServiceCounterValue) values (
      22:          @ServiceCounterId,@SnapshotMachineName,@CreationTimeUtc,@ServiceCounterValue)", snapshot);
      23:                      SetIdentity<int>(connection, id => snapshot.Id = id);
      24:                  }
      25:              }
      26:              return snapshots;
      27:          }
      28:      }
      29:  }

    2、监控服务,也就是数据收集代理程序Monitoring Service:

       1:  using System;
       2:  using System.Collections.Generic;
       3:  using System.Linq;
       4:  using System.Text;
       5:  using System.Threading;
       6:  using System.Threading.Tasks;
       7:  using Topshelf;
       8:  using Topshelf.Logging;
       9:   
      10:  namespace PerformanceCounterCollect.Services
      11:  {
      12:      class PerfmonWorker: ServiceControl
      13:      {
      14:          private readonly LogWriter logger = HostLogger.Get<PerfmonWorker>();
      15:          public static bool ShouldStop { get; private set; }
      16:          private ManualResetEvent stopHandle;
      17:   
      18:          public bool Start(HostControl hostControl)
      19:          {
      20:              logger.Info("Starting PerfmonWorker...");
      21:   
      22:              stopHandle = new ManualResetEvent(false);
      23:   
      24:              ThreadPool.QueueUserWorkItem(new ServiceMonitor().Monitor, stopHandle);
      25:   
      26:              return true;
      27:          }
      28:   
      29:          public bool Stop(HostControl hostControl)
      30:          {
      31:              ShouldStop = true;
      32:              logger.Info("Stopping PerfmonWorker...");
      33:              // wait for all threads to finish
      34:              stopHandle.WaitOne(ServiceMonitor.SleepIntervalInMilliSecs + 10);
      35:   
      36:              return true;
      37:          }
      38:      }
      39:    
      40:  }

    服务使用了Topshelf和NLog,具体参看《使用Topshelf 5步创建Windows 服务》。在服务启动的时候开启监控线程,执行方法ServiceMonitor.Monitor:

       1:  using System;
       2:  using System.Collections.Generic;
       3:  using System.Diagnostics;
       4:  using System.Linq;
       5:  using System.Text;
       6:  using System.Threading;
       7:  using System.Threading.Tasks;
       8:  using PerformanceCounterCollect.Models;
       9:  using Topshelf.Logging;
      10:   
      11:  namespace PerformanceCounterCollect.Services
      12:  {
      13:      sealed class ServiceMonitor
      14:      {
      15:          public const int SleepIntervalInMilliSecs = 50000;
      16:   
      17:          private readonly LogWriter logger = HostLogger.Get<ServiceMonitor>();
      18:          private IList<Tuple<int, PerformanceCounter>> serviceCounters;
      19:   
      20:          public void Monitor(object state)
      21:          {
      22:              ManualResetEvent stopHandle = (ManualResetEvent)state;
      23:              String machineName = Environment.MachineName;
      24:              try
      25:              {
      26:                  Initialize(machineName);
      27:                  var snapshots = new ServiceCounterSnapshot[serviceCounters.Count];
      28:   
      29:                  while (!PerfmonWorker.ShouldStop)
      30:                  {
      31:                      Thread.Sleep(SleepIntervalInMilliSecs);
      32:   
      33:                      // this would be our timestamp value by which we will group the snapshots
      34:                      DateTime timeStamp = DateTime.UtcNow;
      35:                      // collect snapshots
      36:                      for (int i = 0; i < serviceCounters.Count; i++)
      37:                      {
      38:                          var snapshot = new ServiceCounterSnapshot();
      39:                          snapshot.CreationTimeUtc = timeStamp;
      40:                          snapshot.SnapshotMachineName = machineName;
      41:                          snapshot.ServiceCounterId = serviceCounters[i].Item1;
      42:                          try
      43:                          {
      44:                              snapshot.ServiceCounterValue = serviceCounters[i].Item2.NextValue();
      45:                              logger.DebugFormat("Performance counter {0} read value: {1}", GetPerfCounterPath(serviceCounters[i].Item2),
      46:                                                  snapshot.ServiceCounterValue);
      47:                          }
      48:                          catch (InvalidOperationException)
      49:                          {
      50:                              snapshot.ServiceCounterValue = null;
      51:                              logger.DebugFormat("Performance counter {0} didn't send any value.", GetPerfCounterPath(serviceCounters[i].Item2));
      52:                          }
      53:                          snapshots[i] = snapshot;
      54:                      }
      55:                      SaveServiceSnapshots(snapshots);
      56:                  }
      57:              }
      58:              finally
      59:              {
      60:                  stopHandle.Set();
      61:              }
      62:          }
      63:   
      64:          private void Initialize(String machineName)
      65:          {
      66:              try
      67:              {
      68:                  var counters = new List<Tuple<int, PerformanceCounter>>();
      69:   
      70:                  foreach (var counter in PerfmonClient.SelectServiceCounter(machineName))
      71:                  {
      72:                      logger.InfoFormat(@"Creating performance counter: {0}\{1}\{2}\{3}", counter.MachineName ?? ".", counter.CategoryName,
      73:                                          counter.CounterName, counter.InstanceName);
      74:                      var perfCounter = new PerformanceCounter(counter.CategoryName, counter.CounterName, counter.InstanceName, counter.MachineName ?? ".");
      75:                      counters.Add(new Tuple<int, PerformanceCounter>(counter.Id, perfCounter));
      76:                      // first value doesn't matter so we should call the counter at least once
      77:                      try { perfCounter.NextValue(); }
      78:                      catch { }
      79:                  }
      80:                 
      81:   
      82:                  serviceCounters = counters;
      83:              }
      84:              catch (Exception ex)
      85:              {
      86:                  logger.Error(ex);
      87:              }
      88:          }
      89:   
      90:          private void SaveServiceSnapshots(IEnumerable<ServiceCounterSnapshot> snapshots)
      91:          {
      92:              PerfmonClient.SaveServiceSnapshots(snapshots);
      93:          }
      94:   
      95:          private String GetPerfCounterPath(PerformanceCounter cnt)
      96:          {
      97:              return String.Format(@"{0}\{1}\{2}\{3}", cnt.MachineName, cnt.CategoryName, cnt.CounterName, cnt.InstanceName);
      98:          }
      99:      }
     100:  }

    Monitor 方法中初始化完要采集的性能计数器实例后开启一个主循环,定期的收集数据,如果相关的性能计数器实例没有运行,计数器将会抛出InvalidOperationException 我们就把它设置为null。数据集的数据通过WebAPI发回服务器端存储,这样就可以实现性能计数器的集中存储了。

    3、使用方法

    使用很简单,首先定义我们要收集的数据

    insert into services values ('notepad', 'notepad process test');

    insert into service_counters values ( 'notepad', ‘GEFFZHANG-PC’, 'process', '% Processor Time', 'notepad', null, 'graph');

    insert into service_counters values ( 'notepad', ‘GEFFZHANG-PC’, 'process', 'working set', 'notepad', null, 'graph');

    然后启动我们的性能计数器收集服务

    image

    代码参见项目 https://github.com/geffzhang/PerformanceCounterCollect/

    WMI Performance Counter Logging Console App

    欢迎大家扫描下面二维码成为我的客户,为你服务和上云

  • 相关阅读:
    简单理解桶排序
    实现js的类似alert效果的函数
    简单理解插入排序
    一个js简单的日历显示效果的函数
    详解一个自己原创的正则匹配IP的表达式
    一个简单的js实现倒计时函数
    简单理解冒泡排序
    简单理解js的this
    vue项目分享html页面(服务器只能内网访问)
    vue项目移动端查看、分享pdf(服务器只能内网访问)
  • 原文地址:https://www.cnblogs.com/shanyou/p/2877582.html
Copyright © 2011-2022 走看看