性能计数器数据收集服务
性能计数器数据收集服务
Introducing Musketeer – the performance counter data collector 演示了一个Windows服务收集性能计数器的数据,将性能计数器数据写入Mysql数据库。参照这篇文章,将在Windows服务中直接连接数据库的代码抽离到一个WebAPI服务中,同时把数据库更改为Sql Server。下面简要介绍下我的改造,项目虽小,其中用到了众多的开源项目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');
然后启动我们的性能计数器收集服务
代码参见项目 https://github.com/geffzhang/PerformanceCounterCollect/tree/6279cfab351b0c1b92f65db36247fcb7371816c5
作者: 自由、创新、研究、探索……
出处: http://shanyou.cnblogs.com/
版权:本文版权归作者和博客园共有
转载:欢迎转载,为了保存作者的创作热情,请按要求【转载】,谢谢
要求:未经作者同意,必须保留此段声明;必须在文章中给出原文连接;否则必究法律责任
翻译此页 阿拉伯语 爱沙尼亚语 白苗语 保加利亚语 波兰语 波斯语 朝鲜语 丹麦语 德语 俄语 法语 繁体中文 芬兰语 海地克里奥尔语 荷兰语 加泰隆语 捷克语 拉脱维亚语 立陶宛语 罗马尼亚 挪威语 葡萄牙语 日语 瑞典语 斯洛伐克语 斯洛文尼亚语 泰语 土耳其语 乌克兰语 西班牙语 希伯来语 希腊语 匈牙利语 意大利语 印地语 印度尼西亚语 英语 越南语
分类: 企业解决方案 , ASP.NET Web API
作者: Leo_wl
出处: http://www.cnblogs.com/Leo_wl/
本文版权归作者和博客园共有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文连接,否则保留追究法律责任的权利。
版权信息