性能计数器数据收集服务

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了性能计数器数据收集服务相关的知识,希望对你有一定的参考价值。

Windows服务收集性能计数器的数据,将性能计数器数据写入数据库,项目虽小,其中用到了众多的开源项目Topshelf、NLog、Dapper,ASP.NET Web API,Newtonsoft.Json等等

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

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

 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.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:

 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:​

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 didnt 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 doesnt 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);

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

性能计数器数据收集服务_数据




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

​WMI Performance Counter Logging Console App​



以上是关于性能计数器数据收集服务的主要内容,如果未能解决你的问题,请参考以下文章

使用刷新对象收集 wmi 性能计数器

如何在Windows服务器做性能测试

Windows 性能监视器API

Windows 性能监视器API

Windows 性能监视器API

Windows 性能监视器API